Merge pull request #730 from jpcima/depth-v1-oncc

Implement SFZv1 depthcc modulations for EG and LFO
This commit is contained in:
JP Cimalando 2021-03-21 17:22:49 +01:00 committed by GitHub
commit ce0a4df29f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 117 additions and 9 deletions

View file

@ -849,26 +849,34 @@ bool sfz::Region::parseLFOOpcode(const Opcode& opcode, LFODescription& lfo)
//
ModKey sourceKey;
ModKey sourceDepthKey;
ModKey targetKey;
OpcodeSpec<float> depthSpec;
OpcodeSpec<float> depthModSpec;
if (absl::StartsWith(opcode.name, "amplfo_")) {
sourceKey = ModKey::createNXYZ(ModId::AmpLFO, id);
sourceDepthKey = ModKey::createNXYZ(ModId::AmpLFODepth, id);
targetKey = ModKey::createNXYZ(ModId::Volume, id);
lfo.freqKey = ModKey::createNXYZ(ModId::AmpLFOFrequency, id);
depthSpec = Default::ampLFODepth;
depthModSpec = Default::volumeMod;
}
else if (absl::StartsWith(opcode.name, "pitchlfo_")) {
sourceKey = ModKey::createNXYZ(ModId::PitchLFO, id);
sourceDepthKey = ModKey::createNXYZ(ModId::PitchLFODepth, id);
targetKey = ModKey::createNXYZ(ModId::Pitch, id);
lfo.freqKey = ModKey::createNXYZ(ModId::PitchLFOFrequency, id);
depthSpec = Default::pitchLFODepth;
depthModSpec = Default::pitchMod;
}
else if (absl::StartsWith(opcode.name, "fillfo_")) {
sourceKey = ModKey::createNXYZ(ModId::FilLFO, id);
sourceDepthKey = ModKey::createNXYZ(ModId::FilLFODepth, id);
targetKey = ModKey::createNXYZ(ModId::FilCutoff, id);
lfo.freqKey = ModKey::createNXYZ(ModId::FilLFOFrequency, id);
depthSpec = Default::filLFODepth;
depthModSpec = Default::filterCutoffMod;
}
else {
ASSERTFALSE;
@ -885,7 +893,8 @@ bool sfz::Region::parseLFOOpcode(const Opcode& opcode, LFODescription& lfo)
getOrCreateConnection(sourceKey, targetKey).sourceDepth = opcode.read(depthSpec);
break;
case_any_lfo_any_ccN("depth"): // also depthcc&
// TODO(jpc) LFO v1
getOrCreateConnection(sourceKey, targetKey).sourceDepthMod = sourceDepthKey;
processGenericCc(opcode, depthModSpec, sourceDepthKey);
break;
case_any_lfo("depthchanaft"):
// TODO(jpc) LFO v1
@ -1057,6 +1066,19 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg)
ModKey::createNXYZ(ModId::FilCutoff, id)).velToDepth = opcode.read(Default::egVel2Depth);
break;
case_any_ccN("pitcheg_depth"):
getOrCreateConnection(
ModKey::createNXYZ(ModId::PitchEG, id),
ModKey::createNXYZ(ModId::Pitch, id)).sourceDepthMod = ModKey::createNXYZ(ModId::PitchEGDepth, id);
processGenericCc(opcode, Default::pitchMod, ModKey::createNXYZ(ModId::PitchEGDepth, id));
break;
case_any_ccN("fileg_depth"):
getOrCreateConnection(
ModKey::createNXYZ(ModId::FilEG, id),
ModKey::createNXYZ(ModId::FilCutoff, id)).sourceDepthMod = ModKey::createNXYZ(ModId::FilEGDepth, id);
processGenericCc(opcode, Default::filterCutoffMod, ModKey::createNXYZ(ModId::FilEGDepth, id));
break;
default:
return false;
}

View file

@ -494,6 +494,7 @@ struct Region {
ModKey source;
ModKey target;
float sourceDepth = 0.0f;
ModKey sourceDepthMod;
float velToDepth = 0.0f;
};
std::vector<Connection> connections;

View file

@ -1683,7 +1683,7 @@ void Synth::Impl::setupModMatrix()
continue;
}
if (!mm.connect(source, target, conn.sourceDepth, conn.velToDepth)) {
if (!mm.connect(source, target, conn.sourceDepth, conn.sourceDepthMod, conn.velToDepth)) {
DBG("[sfizz] Failed to connect modulation source and target");
ASSERTFALSE;
}

View file

@ -76,10 +76,20 @@ int ModIds::flags(ModId id) noexcept
return kModIsPerVoice|kModIsAdditive;
case ModId::OscillatorModDepth:
return kModIsPerVoice|kModIsPercentMultiplicative;
case ModId::PitchEGDepth:
return kModIsPerVoice|kModIsAdditive;
case ModId::FilEGDepth:
return kModIsPerVoice|kModIsAdditive;
case ModId::AmpLFODepth:
return kModIsPerVoice|kModIsAdditive;
case ModId::AmpLFOFrequency:
return kModIsPerVoice|kModIsAdditive;
case ModId::PitchLFODepth:
return kModIsPerVoice|kModIsAdditive;
case ModId::PitchLFOFrequency:
return kModIsPerVoice|kModIsAdditive;
case ModId::FilLFODepth:
return kModIsPerVoice|kModIsAdditive;
case ModId::FilLFOFrequency:
return kModIsPerVoice|kModIsAdditive;
case ModId::LFOFrequency:

View file

@ -53,8 +53,13 @@ enum class ModId : int {
EqBandwidth,
OscillatorDetune,
OscillatorModDepth,
PitchEGDepth,
FilEGDepth,
AmpLFODepth,
AmpLFOFrequency,
PitchLFODepth,
PitchLFOFrequency,
FilLFODepth,
FilLFOFrequency,
LFOFrequency,
LFOBeats,

View file

@ -144,10 +144,20 @@ 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::PitchEGDepth:
return absl::StrCat("PitchEGDepth {", region_.number(), "}");
case ModId::FilEGDepth:
return absl::StrCat("FilterEGDepth {", region_.number(), "}");
case ModId::AmpLFODepth:
return absl::StrCat("AmplitudeLFODepth {", region_.number(), "}");
case ModId::AmpLFOFrequency:
return absl::StrCat("AmplitudeLFOFrequency {", region_.number(), "}");
case ModId::PitchLFODepth:
return absl::StrCat("PitchLFODepth {", region_.number(), "}");
case ModId::PitchLFOFrequency:
return absl::StrCat("PitchLFOFrequency {", region_.number(), "}");
case ModId::FilLFODepth:
return absl::StrCat("FilterLFODepth {", region_.number(), "}");
case ModId::FilLFOFrequency:
return absl::StrCat("FilterLFOFrequency {", region_.number(), "}");
case ModId::LFOFrequency:

View file

@ -38,6 +38,7 @@ struct ModMatrix::Impl {
struct ConnectionData {
float sourceDepth_ {};
ModKey sourceDepthMod_ {};
float velToDepth_ {};
};
@ -193,7 +194,7 @@ ModMatrix::TargetId ModMatrix::findTarget(const ModKey& key) const
return TargetId(it->second);
}
bool ModMatrix::connect(SourceId sourceId, TargetId targetId, float sourceDepth, float velToDepth)
bool ModMatrix::connect(SourceId sourceId, TargetId targetId, float sourceDepth, const ModKey& sourceDepthMod, float velToDepth)
{
Impl& impl = *impl_;
unsigned sourceIndex = sourceId.number();
@ -205,6 +206,7 @@ bool ModMatrix::connect(SourceId sourceId, TargetId targetId, float sourceDepth,
Impl::Target& target = impl.targets_[targetIndex];
Impl::ConnectionData& conn = target.connectedSources[sourceIndex];
conn.sourceDepth_ = sourceDepth;
conn.sourceDepthMod_ = sourceDepthMod;
conn.velToDepth_ = velToDepth;
return true;
@ -412,26 +414,46 @@ float* ModMatrix::getModulation(TargetId targetId)
sourceDepth += triggerValue * velToDepth;
}
const float* sourceDepthMod = getModulationByKey(sourcesPos->second.sourceDepthMod_);
if (isFirstSource) {
if (sourceDepth != 1) {
if (sourceDepth == 1 && !sourceDepthMod)
copy(absl::Span<const float>(sourceBuffer), buffer);
else if (!sourceDepthMod) {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] = sourceDepth * sourceBuffer[i];
}
else {
copy(absl::Span<const float>(sourceBuffer), buffer);
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] = (sourceDepth + sourceDepthMod[i]) * sourceBuffer[i];
}
isFirstSource = false;
}
else {
if (targetFlags & kModIsMultiplicative) {
multiplyMul1<float>(sourceDepth, sourceBuffer, buffer);
if (!sourceDepthMod)
multiplyMul1<float>(sourceDepth, sourceBuffer, buffer);
else {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= (sourceDepth + sourceDepthMod[i]) * sourceBuffer[i];
}
}
else if (targetFlags & kModIsPercentMultiplicative) {
multiplyMul1<float>(0.01f * sourceDepth, sourceBuffer, buffer);
if (!sourceDepthMod)
multiplyMul1<float>(0.01f * sourceDepth, sourceBuffer, buffer);
else {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= 0.01f * (sourceDepth + sourceDepthMod[i]) * sourceBuffer[i];
}
}
else {
ASSERT(targetFlags & kModIsAdditive);
multiplyAdd1<float>(sourceDepth, sourceBuffer, buffer);
if (!sourceDepthMod)
multiplyAdd1<float>(sourceDepth, sourceBuffer, buffer);
else {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] += (sourceDepth + sourceDepthMod[i]) * sourceBuffer[i];
}
}
}
}

View file

@ -92,10 +92,11 @@ public:
* @param sourceId source of the connection
* @param targetId target of the connection
* @param sourceDepth amount which multiplies the source output
* @param sourceDepthMod optional modulation which adds to the source depth
* @param velToDepth amount which full velocity adds to the source depth
* @return true if the connection was successfully made, otherwise false
*/
bool connect(SourceId sourceId, TargetId targetId, float sourceDepth, float velToDepth = 0.0f);
bool connect(SourceId sourceId, TargetId targetId, float sourceDepth, const ModKey& sourceDepthMod, float velToDepth);
/**
* @brief Reinitialize modulation sources overall.

View file

@ -371,3 +371,40 @@ TEST_CASE("[Modulations] LFO v1 connections")
R"("FilterLFO {2}" -> "FilterCutoff {2, N=1}")",
}, 3));
}
TEST_CASE("[Modulations] LFO v1 CC connections")
{
sfz::Synth synth;
synth.loadSfzString("/modulation.sfz", R"(
<region> sample=*sine amplfo_depth_oncc1=10
<region> sample=*sine pitchlfo_depth_oncc2=1200
<region> sample=*sine fillfo_depth_oncc3=-3600
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFODepth {0}")",
R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchLFODepth {1}")",
R"("Controller 3 {curve=0, smooth=0, step=-0}" -> "FilterLFODepth {2}")",
R"("AmplitudeLFO {0}" -> "Volume {0}")",
R"("PitchLFO {1}" -> "Pitch {1}")",
R"("FilterLFO {2}" -> "FilterCutoff {2, N=1}")",
}, 3));
}
TEST_CASE("[Modulations] EG v1 CC connections")
{
sfz::Synth synth;
synth.loadSfzString("/modulation.sfz", R"(
<region> sample=*sine pitcheg_depth_oncc2=1200
<region> sample=*sine fileg_depth_oncc3=-3600
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchEGDepth {0}")",
R"("Controller 3 {curve=0, smooth=0, step=-0}" -> "FilterEGDepth {1}")",
R"("PitchEG {0}" -> "Pitch {0}")",
R"("FilterEG {1}" -> "FilterCutoff {1, N=1}")",
}, 2));
}