From 1b5a841d97c8ed083985ec36ee2c434967408af3 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sun, 9 Aug 2020 22:57:28 +0200 Subject: [PATCH 1/3] Handle default switch at all levels --- src/sfizz/Synth.cpp | 6 ++++++ tests/SynthT.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 606d3497..c1983016 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -226,6 +226,9 @@ void sfz::Synth::handleMasterOpcodes(const std::vector& members) if (auto value = readOpcode(member.value, Default::polyphonyRange)) currentSet->setPolyphonyLimit(*value); break; + case hash("sw_default"): + setValueFromOpcode(member, defaultSwitch, Default::keyRange); + break; } } } @@ -267,6 +270,9 @@ void sfz::Synth::handleGroupOpcodes(const std::vector& members, const st case hash("polyphony"): setValueFromOpcode(member, maxPolyphony, Default::polyphonyRange); break; + case hash("sw_default"): + setValueFromOpcode(member, defaultSwitch, Default::keyRange); + break; } }; diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index a71cb91d..ef63db86 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -939,3 +939,45 @@ TEST_CASE("[Synth] If rt_dead is active the release sample can sound after the a REQUIRE( synth.getRegionView(1)->delayedReleases.empty() ); } + +TEST_CASE("[Synth] sw_default works at a global level") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path(), R"( + sw_default=36 sw_lokey=36 sw_hikey=39 + sw_last=36 key=62 sample=*sine + sw_last=37 key=63 sample=*sine + )"); + synth.noteOn(0, 63, 85); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); + synth.noteOn(0, 62, 85); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); +} + +TEST_CASE("[Synth] sw_default works at a master level") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path(), R"( + sw_default=36 sw_lokey=36 sw_hikey=39 + sw_last=36 key=62 sample=*sine + sw_last=37 key=63 sample=*sine + )"); + synth.noteOn(0, 63, 85); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); + synth.noteOn(0, 62, 85); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); +} + +TEST_CASE("[Synth] sw_default works at a group level") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path(), R"( + sw_default=36 sw_lokey=36 sw_hikey=39 + sw_last=36 key=62 sample=*sine + sw_last=37 key=63 sample=*sine + )"); + synth.noteOn(0, 63, 85); + REQUIRE( synth.getNumActiveVoices(true) == 0 ); + synth.noteOn(0, 62, 85); + REQUIRE( synth.getNumActiveVoices(true) == 1 ); +} From 9b115303310e185e795c8692b71b4b676bb23bcf Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sun, 9 Aug 2020 23:17:31 +0200 Subject: [PATCH 2/3] Honor the base gain and volume before modifiers --- src/sfizz/Voice.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 97fa044d..b4f9598a 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -360,12 +360,14 @@ void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept egEnvelope.getBlock(modulationSpan); // Amplitude envelope + applyGain1(baseGain, modulationSpan); if (float* mod = mm.getModulationByKey(amplitudeKey)) { for (size_t i = 0; i < numSamples; ++i) modulationSpan[i] *= normalizePercents(mod[i]); } // Volume envelope + applyGain1(db2mag(baseVolumedB), modulationSpan); if (float* mod = mm.getModulationByKey(volumeKey)) { for (size_t i = 0; i < numSamples; ++i) modulationSpan[i] *= db2mag(mod[i]); From a8c524b7e8856e54a01fde9eda7a12ce16a2dbd4 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sun, 9 Aug 2020 23:29:28 +0200 Subject: [PATCH 3/3] Slightly looser masking condition --- src/sfizz/Synth.cpp | 2 +- tests/PolyphonyT.cpp | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c1983016..c244620b 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -930,7 +930,7 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc notePolyphonyCounter += 1; switch (region->selfMask) { case SfzSelfMask::mask: - if (voice->getTriggerValue() < velocity) { + if (voice->getTriggerValue() <= velocity) { if (!selfMaskCandidate || selfMaskCandidate->getTriggerValue() > voice->getTriggerValue()) selfMaskCandidate = voice.get(); } diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index aeac0ebe..15173915 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -224,3 +224,21 @@ TEST_CASE("[Polyphony] Not self-masking") REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 64_norm); REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); } + +TEST_CASE("[Polyphony] Self-masking with the exact same velocity") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path(), R"( + sample=*sine key=64 note_polyphony=2 + )"); + synth.noteOn(0, 64, 64); + synth.noteOn(0, 64, 63); + synth.noteOn(0, 64, 63); + REQUIRE(synth.getNumActiveVoices(true) == 3); // One of these is releasing + REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 64_norm); + REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 63_norm); + REQUIRE(synth.getVoiceView(1)->releasedOrFree()); // The first one is the masking candidate since they have the same velocity + REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 63_norm); + REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); +}