Refactor the opcode handling for LFO and EG
This commit is contained in:
parent
1e3b85827e
commit
97c9411f8d
4 changed files with 444 additions and 542 deletions
|
|
@ -53,6 +53,28 @@ static absl::string_view extractBackInteger(absl::string_view opcodeName)
|
||||||
return opcodeName.substr(i);
|
return opcodeName.substr(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Opcode::getLetterOnlyName() const
|
||||||
|
{
|
||||||
|
absl::string_view name { this->name };
|
||||||
|
|
||||||
|
std::string letterOnlyName;
|
||||||
|
letterOnlyName.reserve(name.size());
|
||||||
|
|
||||||
|
bool charWasDigit = false;
|
||||||
|
for (unsigned char c : name) {
|
||||||
|
bool charIsDigit = absl::ascii_isdigit(c);
|
||||||
|
|
||||||
|
if (!charIsDigit)
|
||||||
|
letterOnlyName.push_back(c);
|
||||||
|
else if (!charWasDigit)
|
||||||
|
letterOnlyName.push_back('&');
|
||||||
|
|
||||||
|
charWasDigit = charIsDigit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return letterOnlyName;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Opcode::getDerivedName(OpcodeCategory newCategory, unsigned number) const
|
std::string Opcode::getDerivedName(OpcodeCategory newCategory, unsigned number) const
|
||||||
{
|
{
|
||||||
std::string derivedName(name);
|
std::string derivedName(name);
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,12 @@ struct Opcode {
|
||||||
*/
|
*/
|
||||||
Opcode cleanUp(OpcodeScope scope) const;
|
Opcode cleanUp(OpcodeScope scope) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate a letter-only name, replacing any digit sequence with
|
||||||
|
* in the opcode name with a single ampersand character.
|
||||||
|
*/
|
||||||
|
std::string getLetterOnlyName() const;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief Get the derived opcode name to convert it to another category.
|
* @brief Get the derived opcode name to convert it to another category.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "modulations/ModId.h"
|
#include "modulations/ModId.h"
|
||||||
#include "absl/strings/str_replace.h"
|
#include "absl/strings/str_replace.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "absl/strings/match.h"
|
||||||
#include "absl/algorithm/container.h"
|
#include "absl/algorithm/container.h"
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
@ -45,32 +46,18 @@ sfz::Region::Region(int regionNumber, const MidiState& midiState, absl::string_v
|
||||||
amplitudeEG.release = Default::egRelease;
|
amplitudeEG.release = Default::egRelease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for ccN processing
|
||||||
|
#define case_any_ccN(x) \
|
||||||
|
case hash(x "_oncc&"): \
|
||||||
|
case hash(x "_curvecc&"): \
|
||||||
|
case hash(x "_stepcc&"): \
|
||||||
|
case hash(x "_smoothcc&")
|
||||||
|
|
||||||
bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
{
|
{
|
||||||
const Opcode opcode = rawOpcode.cleanUp(kOpcodeScopeRegion);
|
const Opcode opcode = rawOpcode.cleanUp(kOpcodeScopeRegion);
|
||||||
|
|
||||||
switch (opcode.lettersOnlyHash) {
|
switch (opcode.lettersOnlyHash) {
|
||||||
// Helper for ccN processing
|
|
||||||
#define case_any_ccN(x) \
|
|
||||||
case hash(x "_oncc&"): \
|
|
||||||
case hash(x "_curvecc&"): \
|
|
||||||
case hash(x "_stepcc&"): \
|
|
||||||
case hash(x "_smoothcc&")
|
|
||||||
|
|
||||||
#define LFO_EG_filter_EQ_target(sourceKey, targetKey, spec) \
|
|
||||||
{ \
|
|
||||||
const auto number = opcode.parameters.front(); \
|
|
||||||
if (number == 0) \
|
|
||||||
return false; \
|
|
||||||
\
|
|
||||||
const auto index = opcode.parameters.size() == 2 ? opcode.parameters.back() - 1 : 0; \
|
|
||||||
if (!extendIfNecessary(filters, index + 1, Default::numFilters)) \
|
|
||||||
return false; \
|
|
||||||
\
|
|
||||||
const ModKey source = ModKey::createNXYZ(sourceKey, id, number - 1); \
|
|
||||||
const ModKey target = ModKey::createNXYZ(targetKey, id, index); \
|
|
||||||
getOrCreateConnection(source, target).sourceDepth = opcode.read(spec); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sound source: sample playback
|
// Sound source: sample playback
|
||||||
case hash("sample"):
|
case hash("sample"):
|
||||||
|
|
@ -747,523 +734,6 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
bendSmooth = opcode.read(Default::smoothCC);
|
bendSmooth = opcode.read(Default::smoothCC);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Modulation: LFO
|
|
||||||
case hash("lfo&_freq"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].freq = opcode.read(Default::lfoFreq);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case_any_ccN("lfo&_freq"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
processGenericCc(opcode, Default::lfoFreqMod, ModKey::createNXYZ(ModId::LFOFrequency, id, lfoNumber - 1));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_beats"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].beats = opcode.read(Default::lfoBeats);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case_any_ccN("lfo&_beats"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
processGenericCc(opcode, Default::lfoBeatsMod, ModKey::createNXYZ(ModId::LFOBeats, id, lfoNumber - 1));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_phase"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].phase0 = opcode.read(Default::lfoPhase);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_delay"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].delay = opcode.read(Default::lfoDelay);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_fade"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].fade = opcode.read(Default::lfoFade);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_count"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].count = opcode.read(Default::lfoCount);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_steps"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
if (!lfos[lfoNumber - 1].seq)
|
|
||||||
lfos[lfoNumber - 1].seq = LFODescription::StepSequence();
|
|
||||||
lfos[lfoNumber - 1].seq->steps.resize(opcode.read(Default::lfoSteps));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_step&"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
const auto stepNumber = opcode.parameters[1];
|
|
||||||
if (lfoNumber == 0 || stepNumber == 0 || stepNumber > config::maxLFOSteps)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
if (!lfos[lfoNumber - 1].seq)
|
|
||||||
lfos[lfoNumber - 1].seq = LFODescription::StepSequence();
|
|
||||||
if (!extendIfNecessary(lfos[lfoNumber - 1].seq->steps, stepNumber, Default::numLFOSteps))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].seq->steps[stepNumber - 1] = opcode.read(Default::lfoStepX);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_wave&"): // also lfo&_wave
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
const auto subNumber = opcode.parameters[1];
|
|
||||||
if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].sub[subNumber - 1].wave = opcode.read(Default::lfoWave);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_offset&"): // also lfo&_offset
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
const auto subNumber = opcode.parameters[1];
|
|
||||||
if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].sub[subNumber - 1].offset = opcode.read(Default::lfoOffset);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_ratio&"): // also lfo&_ratio
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
const auto subNumber = opcode.parameters[1];
|
|
||||||
if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].sub[subNumber - 1].ratio = opcode.read(Default::lfoRatio);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_scale&"): // also lfo&_scale
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
const auto subNumber = opcode.parameters[1];
|
|
||||||
if (lfoNumber == 0 || subNumber == 0 || subNumber > config::maxLFOSubs)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos, lfoNumber, Default::numLFOs))
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(lfos[lfoNumber - 1].sub, subNumber, Default::numLFOSubs))
|
|
||||||
return false;
|
|
||||||
lfos[lfoNumber - 1].sub[subNumber - 1].scale = opcode.read(Default::lfoScale);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Modulation: LFO (targets)
|
|
||||||
case hash("lfo&_amplitude"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::amplitudeMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_pan"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Pan, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::panMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_width"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Width, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::widthMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_position"): // sfizz extension
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Position, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::positionMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_pitch"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Pitch, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::pitchMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_volume"):
|
|
||||||
{
|
|
||||||
const auto lfoNumber = opcode.parameters.front();
|
|
||||||
if (lfoNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Volume, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::volumeMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("lfo&_cutoff&"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilCutoff, Default::filterCutoffMod);
|
|
||||||
break;
|
|
||||||
case hash("lfo&_resonance&"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilResonance, Default::filterResonanceMod);
|
|
||||||
break;
|
|
||||||
case hash("lfo&_fil&gain"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilGain, Default::filterGainMod);
|
|
||||||
break;
|
|
||||||
case hash("lfo&_eq&gain"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqGain, Default::eqGainMod);
|
|
||||||
break;
|
|
||||||
case hash("lfo&_eq&freq"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqFrequency, Default::eqFrequencyMod);
|
|
||||||
break;
|
|
||||||
case hash("lfo&_eq&bw"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqBandwidth, Default::eqBandwidthMod);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Modulation: Flex EG (targets)
|
|
||||||
case hash("eg&_amplitude"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::amplitudeMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_pan"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Pan, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::panMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_width"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Width, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::widthMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_position"): // sfizz extension
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Position, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::positionMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_pitch"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Pitch, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::pitchMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_volume"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
|
||||||
const ModKey target = ModKey::createNXYZ(ModId::Volume, id);
|
|
||||||
getOrCreateConnection(source, target).sourceDepth =
|
|
||||||
opcode.read(Default::volumeMod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_cutoff&"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilCutoff, Default::filterCutoffMod);
|
|
||||||
break;
|
|
||||||
case hash("eg&_resonance&"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilResonance, Default::filterResonanceMod);
|
|
||||||
break;
|
|
||||||
case hash("eg&_fil&gain"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilGain, Default::filterGainMod);
|
|
||||||
break;
|
|
||||||
case hash("eg&_eq&gain"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqGain, Default::eqGainMod);
|
|
||||||
break;
|
|
||||||
case hash("eg&_eq&freq"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqFrequency, Default::eqFrequencyMod);
|
|
||||||
break;
|
|
||||||
case hash("eg&_eq&bw"):
|
|
||||||
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqBandwidth, Default::eqBandwidthMod);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case hash("eg&_ampeg"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
|
||||||
return false;
|
|
||||||
auto ampeg = opcode.read(Default::flexEGAmpeg);
|
|
||||||
FlexEGDescription& desc = flexEGs[egNumber - 1];
|
|
||||||
if (desc.ampeg != ampeg) {
|
|
||||||
desc.ampeg = ampeg;
|
|
||||||
flexAmpEG = absl::nullopt;
|
|
||||||
for (size_t i = 0, n = flexEGs.size(); i < n && !flexAmpEG; ++i) {
|
|
||||||
if (flexEGs[i].ampeg)
|
|
||||||
flexAmpEG = static_cast<uint8_t>(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Amplitude Envelope
|
|
||||||
case hash("ampeg_attack"):
|
|
||||||
case hash("ampeg_decay"):
|
|
||||||
case hash("ampeg_delay"):
|
|
||||||
case hash("ampeg_hold"):
|
|
||||||
case hash("ampeg_release"):
|
|
||||||
case hash("ampeg_start"):
|
|
||||||
case hash("ampeg_sustain"):
|
|
||||||
case hash("ampeg_veltoattack"): // also ampeg_vel2attack
|
|
||||||
case hash("ampeg_veltodecay"): // also ampeg_vel2decay
|
|
||||||
case hash("ampeg_veltodelay"): // also ampeg_vel2delay
|
|
||||||
case hash("ampeg_veltohold"): // also ampeg_vel2hold
|
|
||||||
case hash("ampeg_veltorelease"): // also ampeg_vel2release
|
|
||||||
case hash("ampeg_veltosustain"): // also ampeg_vel2sustain
|
|
||||||
case hash("ampeg_attack_oncc&"): // also ampeg_attackcc&
|
|
||||||
case hash("ampeg_decay_oncc&"): // also ampeg_decaycc&
|
|
||||||
case hash("ampeg_delay_oncc&"): // also ampeg_delaycc&
|
|
||||||
case hash("ampeg_hold_oncc&"): // also ampeg_holdcc&
|
|
||||||
case hash("ampeg_release_oncc&"): // also ampeg_releasecc&
|
|
||||||
case hash("ampeg_start_oncc&"): // also ampeg_startcc&
|
|
||||||
case hash("ampeg_sustain_oncc&"): // also ampeg_sustaincc&
|
|
||||||
parseEGOpcode(opcode, amplitudeEG);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case hash("pitcheg_attack"):
|
|
||||||
case hash("pitcheg_decay"):
|
|
||||||
case hash("pitcheg_delay"):
|
|
||||||
case hash("pitcheg_hold"):
|
|
||||||
case hash("pitcheg_release"):
|
|
||||||
case hash("pitcheg_start"):
|
|
||||||
case hash("pitcheg_sustain"):
|
|
||||||
case hash("pitcheg_veltoattack"): // also pitcheg_vel2attack
|
|
||||||
case hash("pitcheg_veltodecay"): // also pitcheg_vel2decay
|
|
||||||
case hash("pitcheg_veltodelay"): // also pitcheg_vel2delay
|
|
||||||
case hash("pitcheg_veltohold"): // also pitcheg_vel2hold
|
|
||||||
case hash("pitcheg_veltorelease"): // also pitcheg_vel2release
|
|
||||||
case hash("pitcheg_veltosustain"): // also pitcheg_vel2sustain
|
|
||||||
case hash("pitcheg_attack_oncc&"): // also pitcheg_attackcc&
|
|
||||||
case hash("pitcheg_decay_oncc&"): // also pitcheg_decaycc&
|
|
||||||
case hash("pitcheg_delay_oncc&"): // also pitcheg_delaycc&
|
|
||||||
case hash("pitcheg_hold_oncc&"): // also pitcheg_holdcc&
|
|
||||||
case hash("pitcheg_release_oncc&"): // also pitcheg_releasecc&
|
|
||||||
case hash("pitcheg_start_oncc&"): // also pitcheg_startcc&
|
|
||||||
case hash("pitcheg_sustain_oncc&"): // also pitcheg_sustaincc&
|
|
||||||
if (parseEGOpcode(opcode, pitchEG))
|
|
||||||
getOrCreateConnection(
|
|
||||||
ModKey::createNXYZ(ModId::PitchEG, id),
|
|
||||||
ModKey::createNXYZ(ModId::Pitch, id));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case hash("fileg_attack"):
|
|
||||||
case hash("fileg_decay"):
|
|
||||||
case hash("fileg_delay"):
|
|
||||||
case hash("fileg_hold"):
|
|
||||||
case hash("fileg_release"):
|
|
||||||
case hash("fileg_start"):
|
|
||||||
case hash("fileg_sustain"):
|
|
||||||
case hash("fileg_veltoattack"): // also fileg_vel2attack
|
|
||||||
case hash("fileg_veltodecay"): // also fileg_vel2decay
|
|
||||||
case hash("fileg_veltodelay"): // also fileg_vel2delay
|
|
||||||
case hash("fileg_veltohold"): // also fileg_vel2hold
|
|
||||||
case hash("fileg_veltorelease"): // also fileg_vel2release
|
|
||||||
case hash("fileg_veltosustain"): // also fileg_vel2sustain
|
|
||||||
case hash("fileg_attack_oncc&"): // also fileg_attackcc&
|
|
||||||
case hash("fileg_decay_oncc&"): // also fileg_decaycc&
|
|
||||||
case hash("fileg_delay_oncc&"): // also fileg_delaycc&
|
|
||||||
case hash("fileg_hold_oncc&"): // also fileg_holdcc&
|
|
||||||
case hash("fileg_release_oncc&"): // also fileg_releasecc&
|
|
||||||
case hash("fileg_start_oncc&"): // also fileg_startcc&
|
|
||||||
case hash("fileg_sustain_oncc&"): // also fileg_sustaincc&
|
|
||||||
if (parseEGOpcode(opcode, filterEG))
|
|
||||||
getOrCreateConnection(
|
|
||||||
ModKey::createNXYZ(ModId::FilEG, id),
|
|
||||||
ModKey::createNXYZ(ModId::FilCutoff, id));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case hash("pitcheg_depth"):
|
|
||||||
getOrCreateConnection(
|
|
||||||
ModKey::createNXYZ(ModId::PitchEG, id),
|
|
||||||
ModKey::createNXYZ(ModId::Pitch, id)).sourceDepth = opcode.read(Default::egDepth);
|
|
||||||
break;
|
|
||||||
case hash("fileg_depth"):
|
|
||||||
getOrCreateConnection(
|
|
||||||
ModKey::createNXYZ(ModId::FilEG, id),
|
|
||||||
ModKey::createNXYZ(ModId::FilCutoff, id)).sourceDepth = opcode.read(Default::egDepth);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case hash("pitcheg_veltodepth"): // also pitcheg_vel2depth
|
|
||||||
getOrCreateConnection(
|
|
||||||
ModKey::createNXYZ(ModId::PitchEG, id),
|
|
||||||
ModKey::createNXYZ(ModId::Pitch, id)).velToDepth = opcode.read(Default::egVel2Depth);
|
|
||||||
break;
|
|
||||||
case hash("fileg_veltodepth"): // also fileg_vel2depth
|
|
||||||
getOrCreateConnection(
|
|
||||||
ModKey::createNXYZ(ModId::FilEG, id),
|
|
||||||
ModKey::createNXYZ(ModId::FilCutoff, id)).velToDepth = opcode.read(Default::egVel2Depth);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Flex envelopes
|
|
||||||
case hash("eg&_dynamic"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
|
||||||
return false;
|
|
||||||
auto& eg = flexEGs[egNumber - 1];
|
|
||||||
eg.dynamic = opcode.read(Default::flexEGDynamic);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_sustain"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
|
||||||
return false;
|
|
||||||
auto& eg = flexEGs[egNumber - 1];
|
|
||||||
eg.sustain = opcode.read(Default::flexEGSustain);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_time&"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
|
||||||
return false;
|
|
||||||
auto& eg = flexEGs[egNumber - 1];
|
|
||||||
const auto pointNumber = opcode.parameters[1];
|
|
||||||
if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints))
|
|
||||||
return false;
|
|
||||||
eg.points[pointNumber].time = opcode.read(Default::flexEGPointTime);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_level&"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
|
||||||
return false;
|
|
||||||
auto& eg = flexEGs[egNumber - 1];
|
|
||||||
const auto pointNumber = opcode.parameters[1];
|
|
||||||
if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints))
|
|
||||||
return false;
|
|
||||||
eg.points[pointNumber].level = opcode.read(Default::flexEGPointLevel);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case hash("eg&_shape&"):
|
|
||||||
{
|
|
||||||
const auto egNumber = opcode.parameters.front();
|
|
||||||
if (egNumber == 0)
|
|
||||||
return false;
|
|
||||||
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
|
||||||
return false;
|
|
||||||
auto& eg = flexEGs[egNumber - 1];
|
|
||||||
const auto pointNumber = opcode.parameters[1];
|
|
||||||
if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints))
|
|
||||||
return false;
|
|
||||||
eg.points[pointNumber].setShape(opcode.read(Default::flexEGPointShape));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case hash("effect&"):
|
case hash("effect&"):
|
||||||
{
|
{
|
||||||
const auto effectNumber = opcode.parameters.back();
|
const auto effectNumber = opcode.parameters.back();
|
||||||
|
|
@ -1284,11 +754,49 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
case hash("ampeg_depth"):
|
case hash("ampeg_depth"):
|
||||||
case hash("ampeg_veltodepth"): // also ampeg_vel2depth
|
case hash("ampeg_veltodepth"): // also ampeg_vel2depth
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#undef case_any_ccN
|
default: {
|
||||||
#undef LFO_EG_filter_EQ_target
|
// Amplitude Envelope
|
||||||
|
if (absl::StartsWith(opcode.name, "ampeg_")) {
|
||||||
|
if (parseEGOpcode(opcode, amplitudeEG))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Pitch Envelope
|
||||||
|
if (absl::StartsWith(opcode.name, "pitcheg_")) {
|
||||||
|
if (parseEGOpcode(opcode, pitchEG)) {
|
||||||
|
getOrCreateConnection(
|
||||||
|
ModKey::createNXYZ(ModId::PitchEG, id),
|
||||||
|
ModKey::createNXYZ(ModId::Pitch, id));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Filter Envelope
|
||||||
|
if (absl::StartsWith(opcode.name, "fileg_")) {
|
||||||
|
if (parseEGOpcode(opcode, filterEG)) {
|
||||||
|
getOrCreateConnection(
|
||||||
|
ModKey::createNXYZ(ModId::FilEG, id),
|
||||||
|
ModKey::createNXYZ(ModId::FilCutoff, id));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
const std::string letterOnlyName = opcode.getLetterOnlyName();
|
||||||
|
|
||||||
|
// Modulation: LFO
|
||||||
|
if (absl::StartsWith(letterOnlyName, "lfo&_")) {
|
||||||
|
if (parseLFOOpcodeV2(opcode))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Modulation: Flex EG
|
||||||
|
if (absl::StartsWith(letterOnlyName, "eg&_")) {
|
||||||
|
if (parseEGOpcodeV2(opcode))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1390,6 +898,29 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg)
|
||||||
eg.ccSustain[opcode.parameters.back()] = opcode.read(Default::egPercentMod);
|
eg.ccSustain[opcode.parameters.back()] = opcode.read(Default::egPercentMod);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case hash("pitcheg_depth"):
|
||||||
|
getOrCreateConnection(
|
||||||
|
ModKey::createNXYZ(ModId::PitchEG, id),
|
||||||
|
ModKey::createNXYZ(ModId::Pitch, id)).sourceDepth = opcode.read(Default::egDepth);
|
||||||
|
break;
|
||||||
|
case hash("fileg_depth"):
|
||||||
|
getOrCreateConnection(
|
||||||
|
ModKey::createNXYZ(ModId::FilEG, id),
|
||||||
|
ModKey::createNXYZ(ModId::FilCutoff, id)).sourceDepth = opcode.read(Default::egDepth);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hash("pitcheg_veltodepth"): // also pitcheg_vel2depth
|
||||||
|
getOrCreateConnection(
|
||||||
|
ModKey::createNXYZ(ModId::PitchEG, id),
|
||||||
|
ModKey::createNXYZ(ModId::Pitch, id)).velToDepth = opcode.read(Default::egVel2Depth);
|
||||||
|
break;
|
||||||
|
case hash("fileg_veltodepth"): // also fileg_vel2depth
|
||||||
|
getOrCreateConnection(
|
||||||
|
ModKey::createNXYZ(ModId::FilEG, id),
|
||||||
|
ModKey::createNXYZ(ModId::FilCutoff, id)).velToDepth = opcode.read(Default::egVel2Depth);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -1412,6 +943,333 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, absl::optional<EGDescripti
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sfz::Region::parseLFOOpcodeV2(const Opcode& opcode)
|
||||||
|
{
|
||||||
|
const unsigned lfoNumber1Based = opcode.parameters.front();
|
||||||
|
|
||||||
|
if (lfoNumber1Based <= 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(lfos, lfoNumber1Based, Default::numLFOs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const unsigned lfoNumber = lfoNumber1Based - 1;
|
||||||
|
LFODescription& lfo = lfos[lfoNumber];
|
||||||
|
|
||||||
|
//
|
||||||
|
auto getOrCreateLFOStep = [&opcode, &lfo]() -> float* {
|
||||||
|
const unsigned stepNumber1Based = opcode.parameters[1];
|
||||||
|
if (stepNumber1Based <= 0 || stepNumber1Based > config::maxLFOSteps)
|
||||||
|
return nullptr;
|
||||||
|
if (!lfo.seq)
|
||||||
|
lfo.seq = LFODescription::StepSequence();
|
||||||
|
if (!extendIfNecessary(lfo.seq->steps, stepNumber1Based, Default::numLFOSteps))
|
||||||
|
return nullptr;
|
||||||
|
return &lfo.seq->steps[stepNumber1Based - 1];
|
||||||
|
};
|
||||||
|
auto getOrCreateLFOSub = [&opcode, &lfo]() -> LFODescription::Sub* {
|
||||||
|
const unsigned subNumber1Based = opcode.parameters[1];
|
||||||
|
if (subNumber1Based <= 0 || subNumber1Based > config::maxLFOSubs)
|
||||||
|
return nullptr;
|
||||||
|
if (!extendIfNecessary(lfo.sub, subNumber1Based, Default::numLFOSubs))
|
||||||
|
return nullptr;
|
||||||
|
return &lfo.sub[subNumber1Based - 1];
|
||||||
|
};
|
||||||
|
auto LFO_EG_filter_EQ_target = [this, &opcode, lfoNumber](ModId sourceId, ModId targetId, const OpcodeSpec<float>& spec) -> bool {
|
||||||
|
const unsigned index = opcode.parameters.size() == 2 ? opcode.parameters.back() - 1 : 0;
|
||||||
|
if (!extendIfNecessary(filters, index + 1, Default::numFilters))
|
||||||
|
return false;
|
||||||
|
const ModKey source = ModKey::createNXYZ(sourceId, id, lfoNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(targetId, id, index);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = opcode.read(spec);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
switch (opcode.lettersOnlyHash) {
|
||||||
|
|
||||||
|
// Modulation: LFO
|
||||||
|
case hash("lfo&_freq"):
|
||||||
|
lfo.freq = opcode.read(Default::lfoFreq);
|
||||||
|
break;
|
||||||
|
case_any_ccN("lfo&_freq"):
|
||||||
|
processGenericCc(opcode, Default::lfoFreqMod, ModKey::createNXYZ(ModId::LFOFrequency, id, lfoNumber));
|
||||||
|
break;
|
||||||
|
case hash("lfo&_beats"):
|
||||||
|
lfo.beats = opcode.read(Default::lfoBeats);
|
||||||
|
break;
|
||||||
|
case_any_ccN("lfo&_beats"):
|
||||||
|
processGenericCc(opcode, Default::lfoBeatsMod, ModKey::createNXYZ(ModId::LFOBeats, id, lfoNumber));
|
||||||
|
break;
|
||||||
|
case hash("lfo&_phase"):
|
||||||
|
lfo.phase0 = opcode.read(Default::lfoPhase);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_delay"):
|
||||||
|
lfo.delay = opcode.read(Default::lfoDelay);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_fade"):
|
||||||
|
lfo.fade = opcode.read(Default::lfoFade);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_count"):
|
||||||
|
lfo.count = opcode.read(Default::lfoCount);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_steps"):
|
||||||
|
if (!lfo.seq)
|
||||||
|
lfo.seq = LFODescription::StepSequence();
|
||||||
|
lfo.seq->steps.resize(opcode.read(Default::lfoSteps));
|
||||||
|
break;
|
||||||
|
case hash("lfo&_step&"):
|
||||||
|
if (float* step = getOrCreateLFOStep())
|
||||||
|
*step = opcode.read(Default::lfoStepX);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case hash("lfo&_wave&"): // also lfo&_wave
|
||||||
|
if (LFODescription::Sub* sub = getOrCreateLFOSub())
|
||||||
|
sub->wave = opcode.read(Default::lfoWave);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case hash("lfo&_offset&"): // also lfo&_offset
|
||||||
|
if (LFODescription::Sub* sub = getOrCreateLFOSub())
|
||||||
|
sub->offset = opcode.read(Default::lfoOffset);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case hash("lfo&_ratio&"): // also lfo&_ratio
|
||||||
|
if (LFODescription::Sub* sub = getOrCreateLFOSub())
|
||||||
|
sub->ratio = opcode.read(Default::lfoRatio);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case hash("lfo&_scale&"): // also lfo&_scale
|
||||||
|
if (LFODescription::Sub* sub = getOrCreateLFOSub())
|
||||||
|
sub->scale = opcode.read(Default::lfoScale);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Modulation: LFO (targets)
|
||||||
|
case hash("lfo&_amplitude"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::amplitudeMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("lfo&_pan"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Pan, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::panMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("lfo&_width"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Width, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::widthMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("lfo&_position"): // sfizz extension
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Position, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::positionMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("lfo&_pitch"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Pitch, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::pitchMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("lfo&_volume"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::LFO, id, lfoNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Volume, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::volumeMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hash("lfo&_cutoff&"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilCutoff, Default::filterCutoffMod);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_resonance&"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilResonance, Default::filterResonanceMod);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_fil&gain"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::LFO, ModId::FilGain, Default::filterGainMod);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_eq&gain"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqGain, Default::eqGainMod);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_eq&freq"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqFrequency, Default::eqFrequencyMod);
|
||||||
|
break;
|
||||||
|
case hash("lfo&_eq&bw"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::LFO, ModId::EqBandwidth, Default::eqBandwidthMod);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sfz::Region::parseEGOpcodeV2(const Opcode& opcode)
|
||||||
|
{
|
||||||
|
const unsigned egNumber1Based = opcode.parameters.front();
|
||||||
|
if (egNumber1Based <= 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(flexEGs, egNumber1Based, Default::numFlexEGs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const unsigned egNumber = egNumber1Based - 1;
|
||||||
|
FlexEGDescription& eg = flexEGs[egNumber];
|
||||||
|
|
||||||
|
//
|
||||||
|
auto getOrCreateEGPoint = [&opcode, &eg]() -> FlexEGPoint* {
|
||||||
|
const auto pointNumber = opcode.parameters[1];
|
||||||
|
if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints))
|
||||||
|
return nullptr;
|
||||||
|
return &eg.points[pointNumber];
|
||||||
|
};
|
||||||
|
auto LFO_EG_filter_EQ_target = [this, &opcode, egNumber](ModId sourceId, ModId targetId, const OpcodeSpec<float>& spec) -> bool {
|
||||||
|
const unsigned index = opcode.parameters.size() == 2 ? opcode.parameters.back() - 1 : 0;
|
||||||
|
if (!extendIfNecessary(filters, index + 1, Default::numFilters))
|
||||||
|
return false;
|
||||||
|
const ModKey source = ModKey::createNXYZ(sourceId, id, egNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(targetId, id, index);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = opcode.read(spec);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
switch (opcode.lettersOnlyHash) {
|
||||||
|
|
||||||
|
// Flex envelopes
|
||||||
|
case hash("eg&_dynamic"):
|
||||||
|
eg.dynamic = opcode.read(Default::flexEGDynamic);
|
||||||
|
break;
|
||||||
|
case hash("eg&_sustain"):
|
||||||
|
eg.sustain = opcode.read(Default::flexEGSustain);
|
||||||
|
break;
|
||||||
|
case hash("eg&_time&"):
|
||||||
|
if (FlexEGPoint* point = getOrCreateEGPoint())
|
||||||
|
point->time = opcode.read(Default::flexEGPointTime);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case hash("eg&_level&"):
|
||||||
|
if (FlexEGPoint* point = getOrCreateEGPoint())
|
||||||
|
point->level = opcode.read(Default::flexEGPointLevel);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case hash("eg&_shape&"):
|
||||||
|
if (FlexEGPoint* point = getOrCreateEGPoint())
|
||||||
|
point->setShape(opcode.read(Default::flexEGPointShape));
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Modulation: Flex EG (targets)
|
||||||
|
case hash("eg&_amplitude"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::amplitudeMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_pan"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Pan, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::panMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_width"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Width, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::widthMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_position"): // sfizz extension
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Position, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::positionMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_pitch"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Pitch, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::pitchMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_volume"):
|
||||||
|
{
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Volume, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth =
|
||||||
|
opcode.read(Default::volumeMod);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_cutoff&"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilCutoff, Default::filterCutoffMod);
|
||||||
|
break;
|
||||||
|
case hash("eg&_resonance&"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilResonance, Default::filterResonanceMod);
|
||||||
|
break;
|
||||||
|
case hash("eg&_fil&gain"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::FilGain, Default::filterGainMod);
|
||||||
|
break;
|
||||||
|
case hash("eg&_eq&gain"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqGain, Default::eqGainMod);
|
||||||
|
break;
|
||||||
|
case hash("eg&_eq&freq"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqFrequency, Default::eqFrequencyMod);
|
||||||
|
break;
|
||||||
|
case hash("eg&_eq&bw"):
|
||||||
|
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqBandwidth, Default::eqBandwidthMod);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hash("eg&_ampeg"):
|
||||||
|
{
|
||||||
|
auto ampeg = opcode.read(Default::flexEGAmpeg);
|
||||||
|
if (eg.ampeg != ampeg) {
|
||||||
|
eg.ampeg = ampeg;
|
||||||
|
flexAmpEG = absl::nullopt;
|
||||||
|
for (size_t i = 0, n = flexEGs.size(); i < n && !flexAmpEG; ++i) {
|
||||||
|
if (flexEGs[i].ampeg)
|
||||||
|
flexAmpEG = static_cast<uint8_t>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec, const ModKey& target)
|
bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec, const ModKey& target)
|
||||||
{
|
{
|
||||||
if (!opcode.isAnyCcN())
|
if (!opcode.isAnyCcN())
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,22 @@ struct Region {
|
||||||
* @return false
|
* @return false
|
||||||
*/
|
*/
|
||||||
bool parseEGOpcode(const Opcode& opcode, absl::optional<EGDescription>& eg);
|
bool parseEGOpcode(const Opcode& opcode, absl::optional<EGDescription>& eg);
|
||||||
|
/**
|
||||||
|
* @brief Parse a opcode which is specific to a particular SFZv2 LFO: lfoN.
|
||||||
|
*
|
||||||
|
* @param opcode
|
||||||
|
* @return true if the opcode was properly read and stored.
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
bool parseLFOOpcodeV2(const Opcode& opcode);
|
||||||
|
/**
|
||||||
|
* @brief Parse a opcode which is specific to a particular SFZv2 EG: egN.
|
||||||
|
*
|
||||||
|
* @param opcode
|
||||||
|
* @return true if the opcode was properly read and stored.
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
bool parseEGOpcodeV2(const Opcode& opcode);
|
||||||
/**
|
/**
|
||||||
* @brief Process a generic CC opcode, and fill the modulation parameters.
|
* @brief Process a generic CC opcode, and fill the modulation parameters.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue