Skip to content

Commit

Permalink
Temposync voice processors (#104)
Browse files Browse the repository at this point in the history
* Temposync voice processors

Add temposync communication to the voice processors. Turns out
the SimpleLFO has a bit of drift due to float vs double which
we need to clean up, but the basic idea is right, so merge this
then open a followup issue.
  • Loading branch information
baconpaul authored Jun 30, 2024
1 parent c8c5826 commit 08406e9
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 42 deletions.
63 changes: 46 additions & 17 deletions include/sst/voice-effects/VoiceEffectCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ template <typename VFXConfig> struct VoiceEffectTemplateBase : public VFXConfig:

float envelope_rate_linear_nowrap(float f)
{
return VFXConfig::blockSize * VFXConfig::getSampleRateInv(this) * std::pow(2, -f);
auto res = VFXConfig::blockSize * VFXConfig::getSampleRateInv(this) * std::pow(2, -f);
if (getIsTemposync())
{
updateTempo();
res = res * temposyncratio;
}
return res;
}

static_assert(std::is_same<decltype(VFXConfig::getSampleRate(
Expand Down Expand Up @@ -173,30 +179,53 @@ template <typename VFXConfig> struct VoiceEffectTemplateBase : public VFXConfig:
}
}

template <typename T, typename = void> struct has_oversampling : std::false_type
{
#define HASMEM(M, GetSig, DVAL, PARENS) \
template <typename T, typename = void> struct has_##M : std::false_type \
{ \
}; \
template <typename T> struct has_##M<T, std::void_t<decltype(&T::M)>> : std::true_type \
{ \
}; \
GetSig \
{ \
if constexpr (has_##M<VFXConfig>::value) \
{ \
return VFXConfig::M PARENS; \
} \
else \
{ \
return DVAL; \
} \
};

template <typename T>
struct has_oversampling<T, std::void_t<decltype(&T::oversamplingRatio)>> : std::true_type
{
};
HASMEM(oversamplingRatio, constexpr int16_t getOversamplingRatio(), 1, );
HASMEM(getTempoPointer, double *getBaseTempoPointer(), &defaultTempo, (asBase()));
HASMEM(isTemposync, bool getIsTemposync(), false, (asBase()));

#undef HASMEM

using BiquadFilterType =
sst::filters::Biquad::BiquadFilter<VoiceEffectTemplateBase<VFXConfig>, VFXConfig::blockSize,
VoiceEffectTemplateBase<VFXConfig>>;

constexpr int16_t getOversamplingRatio()
void updateTempo()
{
if constexpr (has_oversampling<VFXConfig>::value)
{
return VFXConfig::oversamplingRatio;
}
else
auto tempoPointer = getBaseTempoPointer();
assert(tempoPointer);
if (tempo != *tempoPointer)
{
return 1;
tempo = *tempoPointer;
temposyncratio = tempo / 120.0;
temposyncratioinv = 1.0 / temposyncratio;
}
}

using BiquadFilterType =
sst::filters::Biquad::BiquadFilter<VoiceEffectTemplateBase<VFXConfig>, VFXConfig::blockSize,
VoiceEffectTemplateBase<VFXConfig>>;
private:
double defaultTempo{120.0};

protected:
double tempo{defaultTempo}, temposyncratio{defaultTempo / 120.0},
temposyncratioinv{120.0 / defaultTempo};
};
} // namespace sst::voice_effects::core

Expand Down
9 changes: 1 addition & 8 deletions include/sst/voice-effects/delay/Chorus.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,7 @@ template <typename VFXConfig> struct Chorus : core::VoiceEffectTemplateBase<VFXC
case fpFeedback:
return pmd().asPercent().withDefault(0.f).withName("Feedback");
case fpRate:
return pmd()
.asFloat()
.withRange(-3, 4)
.withPolarity(pmd::ParamMetaData::Polarity::UNIPOLAR_POSITIVE)
// .temposyncable()
// .withTemposyncMultiplier(-1)
.withATwoToTheBFormatting(1, 1, "Hz")
.withName("Rate");
return pmd().asLfoRate(-3, 4).withName("Rate");
case fpDepth:
return pmd()
.asFloat()
Expand Down
9 changes: 1 addition & 8 deletions include/sst/voice-effects/modulation/Phaser.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,7 @@ template <typename VFXConfig> struct Phaser : core::VoiceEffectTemplateBase<VFXC
case fpResonance:
return pmd().asPercent().withDefault(0.707).withName("Resonance");
case fpRate:
return pmd()
.asFloat()
.withRange(-3, 4)
.withPolarity(pmd::ParamMetaData::Polarity::UNIPOLAR_POSITIVE)
// .temposyncable()
// .withTemposyncMultiplier(-1)
.withATwoToTheBFormatting(1, 1, "Hz")
.withName("Rate");
return pmd().asLfoRate(-3, 4).withName("Rate");
case fpDepth:
return pmd()
.asFloat()
Expand Down
11 changes: 2 additions & 9 deletions include/sst/voice-effects/modulation/Tremolo.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,7 @@ template <typename VFXConfig> struct Tremolo : core::VoiceEffectTemplateBase<VFX
case fpVolume:
return pmd().asLinearDecibel(-60.f, 12.f).withName("Volume");
case fpRate:
return pmd()
.asFloat()
.withRange(-3, 4)
.withPolarity(pmd::ParamMetaData::Polarity::UNIPOLAR_POSITIVE)
// .temposyncable()
// .withTemposyncMultiplier(-1)
.withATwoToTheBFormatting(1, 1, "Hz")
.withName("Rate");
return pmd().asLfoRate(-3, 4).withName("Rate");
case fpDepth:
return pmd()
.asFloat()
Expand Down Expand Up @@ -444,7 +437,7 @@ template <typename VFXConfig> struct Tremolo : core::VoiceEffectTemplateBase<VFX

// How does it know which MonoTo... function to choose? By first calling this.
bool getMonoToStereoSetting() const { return this->getIntParam(ipStereo) > 0; }

bool enableKeytrack(bool b)
{
auto res = (b != keytrackOn);
Expand Down

0 comments on commit 08406e9

Please sign in to comment.