diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index e8fa12b9..311e7b2c 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -44,8 +44,8 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const this->decayRate = secondsToExpRate(desc.getDecay(state, velocity)); this->releaseRate = secondsToExpRate(desc.getRelease(state, velocity)); this->hold = secondsToSamples(desc.getHold(state, velocity)); - this->sustain = normalizePercents(desc.getSustain(state, velocity)); - this->start = normalizePercents(desc.getStart(state, velocity)); + this->sustain = desc.getSustain(state, velocity); + this->start = desc.getStart(state, velocity); releaseDelay = 0; sustainThreshold = this->sustain + config::virtuallyZero; diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index c304dde0..d76ed822 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -74,13 +74,13 @@ UInt8Spec sequence { 1, {1, 100}, 0 }; FloatSpec volume { 0.0f, {-144.0f, 48.0f}, kPermissiveBounds }; FloatSpec volumeMod { 0.0f, {-144.0f, 48.0f}, kPermissiveBounds }; FloatSpec amplitude { 100.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds }; -FloatSpec amplitudeMod { 0.0f, {0.0f, 10000.0f}, kPermissiveBounds }; +FloatSpec amplitudeMod { 0.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec pan { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; -FloatSpec panMod { 0.0f, {-200.0f, 200.0f}, kPermissiveBounds }; +FloatSpec panMod { 0.0f, {-200.0f, 200.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec position { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; -FloatSpec positionMod { 0.0f, {-200.0f, 200.0f}, kPermissiveBounds }; +FloatSpec positionMod { 0.0f, {-200.0f, 200.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec width { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; -FloatSpec widthMod { 0.0f, {-200.0f, 200.0f}, kPermissiveBounds }; +FloatSpec widthMod { 0.0f, {-200.0f, 200.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec ampKeytrack { 0.0f, {-96.0f, 12.0f}, kPermissiveBounds }; FloatSpec ampVeltrack { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec ampVelcurve { 0.0f, {0.0f, 1.0f}, kPermissiveBounds }; @@ -136,8 +136,9 @@ FloatSpec lfoScale { 1.0f, {0.0f, 1.0f}, kPermissiveBounds }; FloatSpec egTime { 0.0f, {0.0f, 100.0f}, kPermissiveBounds }; FloatSpec egRelease { 0.001f, {0.0f, 100.0f}, kPermissiveBounds }; FloatSpec egTimeMod { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds }; -FloatSpec egPercent { 0.0f, {0.0f, 100.0f}, kPermissiveBounds }; -FloatSpec egPercentMod { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds }; +FloatSpec egSustain { 100.0f, {0.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec egPercent { 0.0f, {0.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec egPercentMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec egDepth { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec egVel2Depth { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; BoolSpec flexEGAmpeg { false, {0, 1}, kEnforceBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 9fd5842e..9346dad8 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -240,6 +240,7 @@ namespace Default extern const OpcodeSpec egTime; extern const OpcodeSpec egRelease; extern const OpcodeSpec egTimeMod; + extern const OpcodeSpec egSustain; extern const OpcodeSpec egPercent; extern const OpcodeSpec egPercentMod; extern const OpcodeSpec egDepth; diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index ef87057c..a646dfb1 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -71,8 +71,8 @@ struct EGDescription { float delay { Default::egTime }; float hold { Default::egTime }; float release { Default::egTime }; - float start { Default::egPercent.bounds.getStart() }; - float sustain { Default::egPercent.bounds.getEnd() }; + float start { Default::egPercent }; + float sustain { Default::egSustain }; float depth { Default::egDepth }; float vel2attack { Default::egTimeMod }; float vel2decay { Default::egTimeMod }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index ac395b03..8ae0805f 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1513,10 +1513,8 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec spec, break; case kOpcodeStepCcN: { - const float maxStep = - max(std::abs(spec.bounds.getStart()), std::abs(spec.bounds.getEnd())); - const OpcodeSpec stepCC { 0.0f, Range(0.0f, maxStep), 0 }; - p.step = opcode.read(stepCC); + const OpcodeSpec stepCC { 0.0f, {}, kPermissiveBounds }; + p.step = spec.normalizeInput(opcode.read(stepCC)); } break; case kOpcodeSmoothCcN: diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 68182805..6304f4d3 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -731,17 +731,17 @@ void Synth::Impl::finalizeSfzLoad() if (!usedCCs.test(7)) { region.getOrCreateConnection( ModKey::createCC(7, 4, defaultSmoothness, 0), - ModKey::createNXYZ(ModId::Amplitude, region.id)).sourceDepth = 100.0f; + ModKey::createNXYZ(ModId::Amplitude, region.id)).sourceDepth = 1.0f; } if (!usedCCs.test(10)) { region.getOrCreateConnection( ModKey::createCC(10, 1, defaultSmoothness, 0), - ModKey::createNXYZ(ModId::Pan, region.id)).sourceDepth = 100.0f; + ModKey::createNXYZ(ModId::Pan, region.id)).sourceDepth = 1.0f; } if (!usedCCs.test(11)) { region.getOrCreateConnection( ModKey::createCC(11, 4, defaultSmoothness, 0), - ModKey::createNXYZ(ModId::Amplitude, region.id)).sourceDepth = 100.0f; + ModKey::createNXYZ(ModId::Amplitude, region.id)).sourceDepth = 1.0f; } } @@ -1699,7 +1699,7 @@ void Synth::Impl::setupModMatrix() // normalize the stepcc to 0-1 if (sourceKey.id() == ModId::Controller) { ModKey::Parameters p = sourceKey.parameters(); - p.step = (conn.sourceDepth == 0.0f) ? 0.0f : + p.step = (conn.sourceDepth <= 0.0f) ? 0.0f : (p.step / conn.sourceDepth); sourceKey = ModKey::createCC(p.cc, p.curve, p.smooth, p.step); } diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 238fb265..9be0b9ff 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -516,7 +516,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto value = region.ccModDepth(indices[1], ModId::Pan); if (value) { - client.receive<'f'>(delay, path, *value); + client.receive<'f'>(delay, path, *value * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -526,7 +526,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto params = region.ccModParameters(indices[1], ModId::Pan); if (params) { - client.receive<'f'>(delay, path, params->step); + client.receive<'f'>(delay, path, params->step * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -561,7 +561,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto value = region.ccModDepth(indices[1], ModId::Width); if (value) { - client.receive<'f'>(delay, path, *value); + client.receive<'f'>(delay, path, *value * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -571,7 +571,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto params = region.ccModParameters(indices[1], ModId::Width); if (params) { - client.receive<'f'>(delay, path, params->step); + client.receive<'f'>(delay, path, params->step * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -606,7 +606,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto value = region.ccModDepth(indices[1], ModId::Position); if (value) { - client.receive<'f'>(delay, path, *value); + client.receive<'f'>(delay, path, *value * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -616,7 +616,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto params = region.ccModParameters(indices[1], ModId::Position); if (params) { - client.receive<'f'>(delay, path, params->step); + client.receive<'f'>(delay, path, params->step * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -651,7 +651,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto value = region.ccModDepth(indices[1], ModId::Amplitude); if (value) { - client.receive<'f'>(delay, path, *value); + client.receive<'f'>(delay, path, *value * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -661,7 +661,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) auto params = region.ccModParameters(indices[1], ModId::Amplitude); if (params) { - client.receive<'f'>(delay, path, params->step); + client.receive<'f'>(delay, path, params->step * 100.0f); } else { client.receive<'N'>(delay, path, {}); } @@ -943,12 +943,12 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/region&/ampeg_start", "") { GET_REGION_OR_BREAK(indices[0]) - client.receive<'f'>(delay, path, region.amplitudeEG.start); + client.receive<'f'>(delay, path, region.amplitudeEG.start * 100.0f); } break; MATCH("/region&/ampeg_sustain", "") { GET_REGION_OR_BREAK(indices[0]) - client.receive<'f'>(delay, path, region.amplitudeEG.sustain); + client.receive<'f'>(delay, path, region.amplitudeEG.sustain * 100.0f); } break; MATCH("/region&/ampeg_depth", "") { @@ -995,7 +995,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) if (indices[1] != 2) break; - client.receive<'f'>(delay, path, region.amplitudeEG.vel2sustain); + client.receive<'f'>(delay, path, region.amplitudeEG.vel2sustain * 100.0f); } break; MATCH("/region&/ampeg_vel&depth", "") { @@ -1152,13 +1152,13 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/region&/ampeg_start_cc&", "") { GET_REGION_OR_BREAK(indices[0]) float value = region.amplitudeEG.ccStart.getWithDefault(indices[1]); - client.receive<'f'>(delay, path, value); + client.receive<'f'>(delay, path, value * 100.0f); } break; MATCH("/region&/ampeg_sustain_cc&", "") { GET_REGION_OR_BREAK(indices[0]) float value = region.amplitudeEG.ccSustain.getWithDefault(indices[1]); - client.receive<'f'>(delay, path, value); + client.receive<'f'>(delay, path, value * 100.0f); } break; #define GET_FILTER_OR_BREAK(idx) \ diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 10d5070c..1f449bd0 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -826,7 +826,7 @@ void Voice::Impl::amplitudeEnvelope(absl::Span modulationSpan) noexcept applyGain1(baseGain_, modulationSpan); if (float* mod = mm.getModulation(amplitudeTarget_)) { for (size_t i = 0; i < numSamples; ++i) - modulationSpan[i] *= normalizePercents(mod[i]); + modulationSpan[i] *= mod[i]; } // Volume envelope @@ -891,7 +891,7 @@ void Voice::Impl::panStageMono(AudioSpan buffer) noexcept fill(*modulationSpan, region_->pan); if (float* mod = mm.getModulation(panTarget_)) { for (size_t i = 0; i < numSamples; ++i) - (*modulationSpan)[i] += normalizePercents(mod[i]); + (*modulationSpan)[i] += mod[i]; } pan(*modulationSpan, leftBuffer, rightBuffer); } @@ -913,7 +913,7 @@ void Voice::Impl::panStageStereo(AudioSpan buffer) noexcept fill(*modulationSpan, region_->pan); if (float* mod = mm.getModulation(panTarget_)) { for (size_t i = 0; i < numSamples; ++i) - (*modulationSpan)[i] += normalizePercents(mod[i]); + (*modulationSpan)[i] += mod[i]; } pan(*modulationSpan, leftBuffer, rightBuffer); @@ -921,14 +921,14 @@ void Voice::Impl::panStageStereo(AudioSpan buffer) noexcept fill(*modulationSpan, region_->width); if (float* mod = mm.getModulation(widthTarget_)) { for (size_t i = 0; i < numSamples; ++i) - (*modulationSpan)[i] += normalizePercents(mod[i]); + (*modulationSpan)[i] += mod[i]; } width(*modulationSpan, leftBuffer, rightBuffer); fill(*modulationSpan, region_->position); if (float* mod = mm.getModulation(positionTarget_)) { for (size_t i = 0; i < numSamples; ++i) - (*modulationSpan)[i] += normalizePercents(mod[i]); + (*modulationSpan)[i] += mod[i]; } pan(*modulationSpan, leftBuffer, rightBuffer); @@ -1511,7 +1511,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan buffer) noexcept applyGain1(oscillatorModDepth, *modulatorSpan); const float* modDepthMod = resources_.modMatrix.getModulation(oscillatorModDepthTarget_); if (modDepthMod) - multiplyMul1(0.01f, absl::MakeConstSpan(modDepthMod, numFrames), *modulatorSpan); + applyGain(absl::MakeConstSpan(modDepthMod, numFrames), *modulatorSpan); // compute carrierĂ—modulator switch (region_->oscillatorMode) { diff --git a/src/sfizz/modulations/ModId.cpp b/src/sfizz/modulations/ModId.cpp index cc08a16a..dcc70a44 100644 --- a/src/sfizz/modulations/ModId.cpp +++ b/src/sfizz/modulations/ModId.cpp @@ -49,9 +49,9 @@ int ModIds::flags(ModId id) noexcept // targets case ModId::MasterAmplitude: - return kModIsPerVoice|kModIsPercentMultiplicative; + return kModIsPerVoice|kModIsMultiplicative; case ModId::Amplitude: - return kModIsPerVoice|kModIsPercentMultiplicative; + return kModIsPerVoice|kModIsMultiplicative; case ModId::Pan: return kModIsPerVoice|kModIsAdditive; case ModId::Width: @@ -77,7 +77,7 @@ int ModIds::flags(ModId id) noexcept case ModId::OscillatorDetune: return kModIsPerVoice|kModIsAdditive; case ModId::OscillatorModDepth: - return kModIsPerVoice|kModIsPercentMultiplicative; + return kModIsPerVoice|kModIsMultiplicative; case ModId::PitchEGDepth: return kModIsPerVoice|kModIsAdditive; case ModId::FilEGDepth: diff --git a/src/sfizz/modulations/ModId.h b/src/sfizz/modulations/ModId.h index 251ba9e3..1e18356f 100644 --- a/src/sfizz/modulations/ModId.h +++ b/src/sfizz/modulations/ModId.h @@ -85,8 +85,6 @@ enum ModFlags : int { kModIsAdditive = 1 << 3, //! This target is multiplicative (T) kModIsMultiplicative = 1 << 4, - //! This target is %-multiplicative (T) - kModIsPercentMultiplicative = 1 << 5, }; namespace ModIds { diff --git a/src/sfizz/modulations/ModMatrix.cpp b/src/sfizz/modulations/ModMatrix.cpp index d5a90678..3d780ef4 100644 --- a/src/sfizz/modulations/ModMatrix.cpp +++ b/src/sfizz/modulations/ModMatrix.cpp @@ -438,14 +438,6 @@ float* ModMatrix::getModulation(TargetId targetId) buffer[i] *= (sourceDepth + sourceDepthMod[i]) * sourceBuffer[i]; } } - else if (targetFlags & kModIsPercentMultiplicative) { - if (!sourceDepthMod) - multiplyMul1(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); if (!sourceDepthMod) @@ -465,8 +457,6 @@ float* ModMatrix::getModulation(TargetId targetId) if (isFirstSource) { if (targetFlags & kModIsMultiplicative) fill(buffer, 1.0f); - else if (targetFlags & kModIsPercentMultiplicative) - fill(buffer, 100.0f); else { ASSERT(targetFlags & kModIsAdditive); fill(buffer, 0.0f); diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index fa48d801..8c09292a 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -378,7 +378,7 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines") const ModKey target = ModKey::createNXYZ(ModId::Amplitude, synth.getRegionView(2)->getId()); const RegionCCView view(*synth.getRegionView(2), target); REQUIRE(!view.empty()); - REQUIRE(view.valueAt(10) == 34.0f); + REQUIRE(view.valueAt(10) == Approx(0.34f).margin(1e-3)); } TEST_CASE("[Files] Specific bug: relative path with backslashes") diff --git a/tests/ModulationsT.cpp b/tests/ModulationsT.cpp index 48179b66..cfec1788 100644 --- a/tests/ModulationsT.cpp +++ b/tests/ModulationsT.cpp @@ -42,15 +42,13 @@ TEST_CASE("[Modulations] Flags") { checkBasicFlags(flags); REQUIRE((bool(flags & sfz::kModIsAdditive) + - bool(flags & sfz::kModIsMultiplicative) + - bool(flags & sfz::kModIsPercentMultiplicative)) == 0); + bool(flags & sfz::kModIsMultiplicative)) == 0); }; static auto* checkTargetFlags = +[](int flags) { checkBasicFlags(flags); REQUIRE((bool(flags & sfz::kModIsAdditive) + - bool(flags & sfz::kModIsMultiplicative) + - bool(flags & sfz::kModIsPercentMultiplicative)) == 1); + bool(flags & sfz::kModIsMultiplicative)) == 1); }; sfz::ModIds::forEachSourceId([](sfz::ModId id) @@ -385,7 +383,7 @@ TEST_CASE("[Modulations] LFO v1 CC connections") 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"("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}")", @@ -403,7 +401,7 @@ TEST_CASE("[Modulations] EG v1 CC connections") 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"("Controller 3 {curve=0, smooth=0, step=0}" -> "FilterEGDepth {1}")", R"("PitchEG {0}" -> "Pitch {0}")", R"("FilterEG {1}" -> "FilterCutoff {1, N=1}")", }, 2));