Skip to content

Commit

Permalink
Implement the LFOs v1
Browse files Browse the repository at this point in the history
  • Loading branch information
jpcima committed Mar 10, 2021
1 parent ef1a548 commit 7f2daa2
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 8 deletions.
3 changes: 3 additions & 0 deletions src/sfizz/Defaults.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ extern const OpcodeSpec<float> pitchMod { 0.0f, Range<float>(-2400.0f, 2400.0f),
extern const OpcodeSpec<float> bendUp { 200.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> bendDown { -200.0f, Range<float>(-12000.0f, 12000.0f), 0 };
extern const OpcodeSpec<float> bendStep { 1.0f, Range<float>(1.0f, 1200.0f), 0 };
extern const OpcodeSpec<float> ampLFODepth { 0.0f, Range<float>(-10.0f, 10.0f), 0 };
extern const OpcodeSpec<float> pitchLFODepth { 0.0f, Range<float>(-1200.0f, 1200.0f), 0 };
extern const OpcodeSpec<float> filLFODepth { 0.0f, Range<float>(-1200.0f, 1200.0f), 0 };
extern const OpcodeSpec<float> lfoFreq { 0.0f, Range<float>(0.0f, 100.0f), 0 };
extern const OpcodeSpec<float> lfoFreqMod { 0.0f, Range<float>(-100.0f, 100.0f), 0 };
extern const OpcodeSpec<float> lfoBeats { 0.0f, Range<float>(0.0f, 1000.0f), 0 };
Expand Down
3 changes: 3 additions & 0 deletions src/sfizz/Defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ namespace Default
extern const OpcodeSpec<float> bendUp;
extern const OpcodeSpec<float> bendDown;
extern const OpcodeSpec<float> bendStep;
extern const OpcodeSpec<float> ampLFODepth;
extern const OpcodeSpec<float> pitchLFODepth;
extern const OpcodeSpec<float> filLFODepth;
extern const OpcodeSpec<float> lfoFreq;
extern const OpcodeSpec<float> lfoFreqMod;
extern const OpcodeSpec<float> lfoBeats;
Expand Down
131 changes: 131 additions & 0 deletions src/sfizz/Region.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,34 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
}
}

// Amplitude LFO
if (absl::StartsWith(opcode.name, "amplfo_")) {
if (parseLFOOpcode(opcode, amplitudeLFO)) {
getOrCreateConnection(
ModKey::createNXYZ(ModId::AmpLFO, id),
ModKey::createNXYZ(ModId::Volume, id));
return true;
}
}
// Pitch LFO
if (absl::StartsWith(opcode.name, "pitchlfo_")) {
if (parseLFOOpcode(opcode, pitchLFO)) {
getOrCreateConnection(
ModKey::createNXYZ(ModId::PitchLFO, id),
ModKey::createNXYZ(ModId::Pitch, id));
return true;
}
}
// Filter LFO
if (absl::StartsWith(opcode.name, "fillfo_")) {
if (parseLFOOpcode(opcode, filterLFO)) {
getOrCreateConnection(
ModKey::createNXYZ(ModId::FilLFO, id),
ModKey::createNXYZ(ModId::FilCutoff, id));
return true;
}
}

//
const std::string letterOnlyName = opcode.getLetterOnlyName();

Expand All @@ -802,6 +830,109 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
return true;
}

bool sfz::Region::parseLFOOpcode(const Opcode& opcode, LFODescription& lfo)
{
#define case_any_lfo(param) \
case hash("amplfo_" param): \
case hash("pitchlfo_" param): \
case hash("fillfo_" param) \

#define case_any_lfo_any_ccN(param) \
case_any_ccN("amplfo_" param): \
case_any_ccN("pitchlfo_" param): \
case_any_ccN("fillfo_" param) \

//
ModKey sourceKey;
ModKey targetKey;
OpcodeSpec<float> depthSpec;

if (absl::StartsWith(opcode.name, "amplfo_")) {
sourceKey = ModKey::createNXYZ(ModId::AmpLFO, id);
targetKey = ModKey::createNXYZ(ModId::Volume, id);
lfo.freqKey = ModKey::createNXYZ(ModId::AmpLFOFrequency, id);
depthSpec = Default::ampLFODepth;
}
else if (absl::StartsWith(opcode.name, "pitchlfo_")) {
sourceKey = ModKey::createNXYZ(ModId::PitchLFO, id);
targetKey = ModKey::createNXYZ(ModId::Pitch, id);
lfo.freqKey = ModKey::createNXYZ(ModId::PitchLFOFrequency, id);
depthSpec = Default::pitchLFODepth;
}
else if (absl::StartsWith(opcode.name, "fillfo_")) {
sourceKey = ModKey::createNXYZ(ModId::FilLFO, id);
targetKey = ModKey::createNXYZ(ModId::FilCutoff, id);
lfo.freqKey = ModKey::createNXYZ(ModId::FilLFOFrequency, id);
depthSpec = Default::filLFODepth;
}
else {
ASSERTFALSE;
return false;
}

//
switch (opcode.lettersOnlyHash) {

case_any_lfo("delay"):
lfo.delay = opcode.read(Default::lfoDelay);
break;
case_any_lfo("depth"):
getOrCreateConnection(sourceKey, targetKey).sourceDepth = opcode.read(depthSpec);
break;
case_any_lfo_any_ccN("depth"): // also depthcc&
// TODO(jpc) LFO v1
break;
case_any_lfo("depthchanaft"):
// TODO(jpc) LFO v1
break;
case_any_lfo("depthpolyaft"):
// TODO(jpc) LFO v1
break;
case_any_lfo("fade"):
lfo.fade = opcode.read(Default::lfoFade);
break;
case_any_lfo("freq"):
lfo.freq = opcode.read(Default::lfoFreq);
break;
case_any_lfo_any_ccN("freq"): // also freqcc&
processGenericCc(opcode, Default::lfoFreqMod, lfo.freqKey);
break;
case_any_lfo("freqchanaft"):
// TODO(jpc) LFO v1
break;
case_any_lfo("freqpolyaft"):
// TODO(jpc) LFO v1
break;

// sfizz extension
case_any_lfo("wave"):
lfo.sub[0].wave = opcode.read(Default::lfoWave);
break;

default:
return false;
}

#undef case_any_lfo

return true;
}

bool sfz::Region::parseLFOOpcode(const Opcode& opcode, absl::optional<LFODescription>& lfo)
{
bool create = lfo == absl::nullopt;
if (create) {
lfo = LFODescription();
lfo->sub[0].wave = LFOWave::Sine; // the LFO v1 default
}

bool parsed = parseLFOOpcode(opcode, *lfo);
if (!parsed && create)
lfo = absl::nullopt;

return parsed;
}

bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg)
{
#define case_any_eg(param) \
Expand Down
23 changes: 23 additions & 0 deletions src/sfizz/Region.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,26 @@ struct Region {
* @return false
*/
bool parseOpcode(const Opcode& opcode);
/**
* @brief Parse a opcode which is specific to a particular SFZv1 LFO:
* amplfo, pitchlfo, fillfo.
*
* @param opcode
* @param lfo
* @return true if the opcode was properly read and stored.
* @return false
*/
bool parseLFOOpcode(const Opcode& opcode, LFODescription& lfo);
/**
* @brief Parse a opcode which is specific to a particular SFZv1 LFO:
* amplfo, pitchlfo, fillfo.
*
* @param opcode
* @param lfo
* @return true if the opcode was properly read and stored.
* @return false
*/
bool parseLFOOpcode(const Opcode& opcode, absl::optional<LFODescription>& lfo);
/**
* @brief Parse a opcode which is specific to a particular SFZv1 EG:
* ampeg, pitcheg, fileg.
Expand Down Expand Up @@ -457,6 +477,9 @@ struct Region {

// LFOs
std::vector<LFODescription> lfos;
absl::optional<LFODescription> amplitudeLFO;
absl::optional<LFODescription> pitchLFO;
absl::optional<LFODescription> filterLFO;

bool hasStereoSample { false };

Expand Down
15 changes: 15 additions & 0 deletions src/sfizz/Synth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,9 @@ void Synth::Impl::finalizeSfzLoad()
size_t maxFlexEGs { 0 };
bool havePitchEG { false };
bool haveFilterEG { false };
bool haveAmplitudeLFO { false };
bool havePitchLFO { false };
bool haveFilterLFO { false };

FlexEGs::clearUnusedCurves();

Expand Down Expand Up @@ -683,6 +686,9 @@ void Synth::Impl::finalizeSfzLoad()
maxFlexEGs = max(maxFlexEGs, region->flexEGs.size());
havePitchEG = havePitchEG || region->pitchEG != absl::nullopt;
haveFilterEG = haveFilterEG || region->filterEG != absl::nullopt;
haveAmplitudeLFO = haveAmplitudeLFO || region->amplitudeLFO != absl::nullopt;
havePitchLFO = havePitchLFO || region->pitchLFO != absl::nullopt;
haveFilterLFO = haveFilterLFO || region->filterLFO != absl::nullopt;

++currentRegionIndex;
}
Expand Down Expand Up @@ -731,6 +737,9 @@ void Synth::Impl::finalizeSfzLoad()
settingsPerVoice_.maxFlexEGs = maxFlexEGs;
settingsPerVoice_.havePitchEG = havePitchEG;
settingsPerVoice_.haveFilterEG = haveFilterEG;
settingsPerVoice_.haveAmplitudeLFO = haveAmplitudeLFO;
settingsPerVoice_.havePitchLFO = havePitchLFO;
settingsPerVoice_.haveFilterLFO = haveFilterLFO;

applySettingsPerVoice();

Expand Down Expand Up @@ -1579,6 +1588,9 @@ void Synth::Impl::applySettingsPerVoice()
voice.setMaxFlexEGsPerVoice(settingsPerVoice_.maxFlexEGs);
voice.setPitchEGEnabledPerVoice(settingsPerVoice_.havePitchEG);
voice.setFilterEGEnabledPerVoice(settingsPerVoice_.haveFilterEG);
voice.setAmplitudeLFOEnabledPerVoice(settingsPerVoice_.haveAmplitudeLFO);
voice.setPitchLFOEnabledPerVoice(settingsPerVoice_.havePitchLFO);
voice.setFilterLFOEnabledPerVoice(settingsPerVoice_.haveFilterLFO);
}
}

Expand All @@ -1605,6 +1617,9 @@ void Synth::Impl::setupModMatrix()
case ModId::Controller:
gen = genController_.get();
break;
case ModId::AmpLFO:
case ModId::PitchLFO:
case ModId::FilLFO:
case ModId::LFO:
gen = genLFO_.get();
break;
Expand Down
3 changes: 3 additions & 0 deletions src/sfizz/SynthPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@ struct Synth::Impl final: public Parser::Listener {
size_t maxFlexEGs { 0 };
bool havePitchEG { false };
bool haveFilterEG { false };
bool haveAmplitudeLFO { false };
bool havePitchLFO { false };
bool haveFilterLFO { false };
} settingsPerVoice_;

Duration dispatchDuration_ { 0 };
Expand Down
6 changes: 6 additions & 0 deletions src/sfizz/modulations/ModId.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ int ModIds::flags(ModId id) noexcept
return kModIsPerVoice|kModIsAdditive;
case ModId::OscillatorModDepth:
return kModIsPerVoice|kModIsPercentMultiplicative;
case ModId::AmpLFOFrequency:
return kModIsPerVoice|kModIsAdditive;
case ModId::PitchLFOFrequency:
return kModIsPerVoice|kModIsAdditive;
case ModId::FilLFOFrequency:
return kModIsPerVoice|kModIsAdditive;
case ModId::LFOFrequency:
return kModIsPerVoice|kModIsAdditive;
case ModId::LFOBeats:
Expand Down
3 changes: 3 additions & 0 deletions src/sfizz/modulations/ModId.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ enum class ModId : int {
EqBandwidth,
OscillatorDetune,
OscillatorModDepth,
AmpLFOFrequency,
PitchLFOFrequency,
FilLFOFrequency,
LFOFrequency,
LFOBeats,

Expand Down
6 changes: 6 additions & 0 deletions src/sfizz/modulations/ModKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ std::string ModKey::toString() const
return absl::StrCat("OscillatorDetune {", region_.number(), ", N=", 1 + params_.N, "}");
case ModId::OscillatorModDepth:
return absl::StrCat("OscillatorModDepth {", region_.number(), ", N=", 1 + params_.N, "}");
case ModId::AmpLFOFrequency:
return absl::StrCat("AmplitudeLFOFrequency {", region_.number(), "}");
case ModId::PitchLFOFrequency:
return absl::StrCat("PitchLFOFrequency {", region_.number(), "}");
case ModId::FilLFOFrequency:
return absl::StrCat("FilterLFOFrequency {", region_.number(), "}");
case ModId::LFOFrequency:
return absl::StrCat("LFOFrequency {", region_.number(), ", N=", 1 + params_.N, "}");
case ModId::LFOBeats:
Expand Down
60 changes: 52 additions & 8 deletions src/sfizz/modulations/sources/LFO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,46 @@ LFOSource::LFOSource(VoiceManager& manager)

void LFOSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
{
unsigned lfoIndex = sourceKey.parameters().N;

Voice* voice = voiceManager_.getVoiceById(voiceId);
if (!voice) {
ASSERTFALSE;
return;
}

const Region* region = voice->getRegion();
if (lfoIndex >= region->lfos.size()) {
LFO* lfo = nullptr;
const LFODescription* desc = nullptr;

switch (sourceKey.id()) {
case ModId::AmpLFO:
lfo = voice->getAmplitudeLFO();
desc = &*region->amplitudeLFO;
break;
case ModId::PitchLFO:
lfo = voice->getPitchLFO();
desc = &*region->pitchLFO;
break;
case ModId::FilLFO:
lfo = voice->getFilterLFO();
desc = &*region->filterLFO;
break;
case ModId::LFO:
{
unsigned lfoIndex = sourceKey.parameters().N;
if (lfoIndex >= region->lfos.size()) {
ASSERTFALSE;
return;
}
lfo = voice->getLFO(lfoIndex);
desc = &region->lfos[lfoIndex];
}
break;
default:
ASSERTFALSE;
return;
}

LFO* lfo = voice->getLFO(lfoIndex);
lfo->configure(&region->lfos[lfoIndex]);
lfo->configure(desc);
lfo->start(delay);
}

Expand All @@ -52,13 +76,33 @@ void LFOSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl
}

const Region* region = voice->getRegion();
if (lfoIndex >= region->lfos.size()) {
LFO* lfo = nullptr;

switch (sourceKey.id()) {
case ModId::AmpLFO:
lfo = voice->getAmplitudeLFO();
break;
case ModId::PitchLFO:
lfo = voice->getPitchLFO();
break;
case ModId::FilLFO:
lfo = voice->getFilterLFO();
break;
case ModId::LFO:
{
if (lfoIndex >= region->lfos.size()) {
ASSERTFALSE;
fill(buffer, 0.0f);
return;
}
lfo = voice->getLFO(lfoIndex);
}
break;
default:
ASSERTFALSE;
fill(buffer, 0.0f);
return;
}

LFO* lfo = voice->getLFO(lfoIndex);
lfo->process(buffer);
}

Expand Down

0 comments on commit 7f2daa2

Please sign in to comment.