From 7a1a2c9381c84718464fb5337363121ef5c3085f Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Mon, 10 Aug 2020 15:22:35 +0200 Subject: [PATCH] Finer tests and behavior for note_polyphony- note_polyphony is shared amoung polyphony groups- Lower velocity samples do not mute higher velocity samples, but they still play! --- src/sfizz/Synth.cpp | 11 ++--- tests/PolyphonyT.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c746f273..a184803b 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -925,6 +925,7 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc if (region->notePolyphony) { if (!voice->releasedOrFree() + && voice->getRegion()->group == region->group && voice->getTriggerNumber() == noteNumber && voice->getTriggerType() == Voice::TriggerType::NoteOn) { notePolyphonyCounter += 1; @@ -948,11 +949,11 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc } // Polyphony reached on note_polyphony - if (region->notePolyphony && notePolyphonyCounter >= *region->notePolyphony) { - if (selfMaskCandidate != nullptr) - selfMaskCandidate->release(delay); - else // We're the lowest velocity guy here - continue; + // If there's a self-masking candidate, release it + if (region->notePolyphony + && notePolyphonyCounter >= *region->notePolyphony + && selfMaskCandidate != nullptr) { + selfMaskCandidate->release(delay); } auto parent = region->parent; diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index 0fa3aa01..f1e261d1 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -218,7 +218,7 @@ TEST_CASE("[Polyphony] Not self-masking") synth.noteOn(0, 66, 64); REQUIRE(synth.getNumActiveVoices(true) == 3); // One of these is releasing REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm); - REQUIRE(synth.getVoiceView(0)->releasedOrFree()); // The first encountered voice is the masking candidate + REQUIRE(synth.getVoiceView(0)->releasedOrFree()); REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm); REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 64_norm); @@ -242,3 +242,101 @@ TEST_CASE("[Polyphony] Self-masking with the exact same velocity") REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 63_norm); REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); } + +TEST_CASE("[Polyphony] Self-masking only works from low to high") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*sine key=64 note_polyphony=1 + )"); + synth.noteOn(0, 64, 63); + synth.noteOn(0, 64, 62); + REQUIRE(synth.getNumActiveVoices(true) == 2); // Both notes are playing + REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm); + REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm); + REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); +} + +TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default)") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*saw key=64 note_polyphony=1 + sample=*sine key=64 note_polyphony=1 + )"); + synth.noteOn(0, 64, 62); + synth.noteOn(0, 64, 63); + REQUIRE(synth.getNumActiveVoices(true) == 4); // Both notes are playing + REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 62_norm); + REQUIRE(synth.getVoiceView(0)->releasedOrFree()); // got killed + REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm); + REQUIRE(synth.getVoiceView(1)->releasedOrFree()); // got killed + REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 63_norm); + REQUIRE(synth.getVoiceView(2)->releasedOrFree()); // got killed + REQUIRE(synth.getVoiceView(3)->getTriggerValue() == 63_norm); + REQUIRE(!synth.getVoiceView(3)->releasedOrFree()); +} + +TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default, with keyswitches)") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sw_lokey=36 sw_hikey=37 sw_default=36 + sw_last=36 key=48 note_polyphony=1 sample=*saw + sw_last=37 key=48 transpose=12 note_polyphony=1 sample=*tri + )"); + synth.noteOn(0, 48, 63); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.cc(0, 64, 127); + synth.noteOn(0, 37, 127); + synth.noteOff(0, 37, 0); + synth.noteOn(0, 48, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); + REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm); + REQUIRE(synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 64_norm); + REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); +} + + +TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + group=1 sample=*saw key=64 note_polyphony=1 + group=2 sample=*sine key=64 note_polyphony=1 + )"); + synth.noteOn(0, 64, 62); + synth.noteOn(0, 64, 63); + REQUIRE(synth.getNumActiveVoices(true) == 4); // Both notes are playing + REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 62_norm); + REQUIRE(synth.getVoiceView(0)->releasedOrFree()); // got killed + REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm); + REQUIRE(synth.getVoiceView(1)->releasedOrFree()); // got killed + REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 63_norm); + REQUIRE(!synth.getVoiceView(2)->releasedOrFree()); + REQUIRE(synth.getVoiceView(3)->getTriggerValue() == 63_norm); + REQUIRE(!synth.getVoiceView(3)->releasedOrFree()); +} + +TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups (with keyswitches)") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sw_lokey=36 sw_hikey=37 sw_default=36 + group=1 sw_last=36 key=48 note_polyphony=1 sample=*saw + group=2 sw_last=37 key=48 transpose=12 note_polyphony=1 sample=*tri + )"); + synth.noteOn(0, 48, 63); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.cc(0, 64, 127); + synth.noteOn(0, 37, 127); + synth.noteOff(0, 37, 0); + synth.noteOn(0, 48, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); + REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm); + REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 64_norm); + REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); +}