diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index fce8dd91..c746f273 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; } }; @@ -924,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()); +} 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 ); +}