From fa37669e083c2669d57cae8c505fc17c824848fa Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Wed, 26 Aug 2020 01:04:10 +0200 Subject: [PATCH] Add checks for release voices and note_polyphony --- src/sfizz/Synth.cpp | 6 ++++-- src/sfizz/Voice.cpp | 2 +- tests/PolyphonyT.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d141eda1..64505a62 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -976,7 +976,8 @@ void sfz::Synth::checkNotePolyphony(const Region* region, int delay, const Trigg for (Voice* voice : voiceViewArray) { const sfz::TriggerEvent& voiceTriggerEvent = voice->getTriggerEvent(); - if (!voice->releasedOrFree() + const bool skipVoice = (triggerEvent.type == TriggerEventType::NoteOn && voice->releasedOrFree()) || voice->isFree(); + if (!skipVoice && voice->getRegion()->group == region->group && voiceTriggerEvent.number == triggerEvent.number && voiceTriggerEvent.type == triggerEvent.type) { @@ -984,8 +985,9 @@ void sfz::Synth::checkNotePolyphony(const Region* region, int delay, const Trigg switch (region->selfMask) { case SfzSelfMask::mask: if (voiceTriggerEvent.value <= triggerEvent.value) { - if (!selfMaskCandidate || selfMaskCandidate->getTriggerEvent().value > voiceTriggerEvent.value) + if (!selfMaskCandidate || selfMaskCandidate->getTriggerEvent().value > voiceTriggerEvent.value) { selfMaskCandidate = voice; + } } break; case SfzSelfMask::dontMask: diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 9687ebc4..117af317 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -193,7 +193,7 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noex if (state != State::playing) return; - if (triggerEvent.number == noteNumber) { + if (triggerEvent.number == noteNumber && triggerEvent.type == TriggerEventType::NoteOn) { noteIsOff = true; if (region->loopMode == SfzLoopMode::one_shot) diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index 1610d7b3..1ff525cf 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -286,6 +286,7 @@ TEST_CASE("[Polyphony] Self-masking only works from low to high") synth.noteOn(0, 64, 63 ); synth.noteOn(0, 64, 62 ); REQUIRE( synth.getNumActiveVoices(true) == 2 ); // Both notes are playing + REQUIRE( numPlayingVoices(synth) == 2 ); // id REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 62_norm); @@ -386,3 +387,45 @@ TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups (wi REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 64_norm); REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); } + +TEST_CASE("[Polyphony] Note polyphony operates on release voices") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, blockSize }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + key=48 note_polyphony=1 sample=*saw trigger=release_key ampeg_attack=1 ampeg_decay=1 + )"); + synth.noteOn(0, 48, 63 ); + synth.noteOff(10, 48, 0 ); + REQUIRE( synth.getNumActiveVoices(true) == 1); + synth.noteOn(20, 48, 65 ); + synth.noteOff(30, 48, 10 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); + synth.renderBlock(buffer); + REQUIRE(numPlayingVoices(synth) == 1 ); + REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); + REQUIRE( synth.getVoiceView(0)->releasedOrFree()); + REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 65_norm); + REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); +} + +TEST_CASE("[Polyphony] Note polyphony operates on release voices (masking works from low to high but takes into account the replaced velocity)") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, blockSize }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + key=48 note_polyphony=1 sample=*saw trigger=release_key ampeg_attack=1 ampeg_decay=1 + )"); + synth.noteOn(0, 48, 63 ); + synth.noteOff(10, 48, 0 ); + REQUIRE( synth.getNumActiveVoices(true) == 1); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.noteOn(20, 48, 61 ); + synth.noteOff(30, 48, 10 ); + REQUIRE( synth.getNumActiveVoices(true) == 2 ); + REQUIRE( numPlayingVoices(synth) == 2 ); + REQUIRE( synth.getVoiceView(0)->getTriggerEvent().value == 63_norm); + REQUIRE(!synth.getVoiceView(0)->releasedOrFree()); + REQUIRE( synth.getVoiceView(1)->getTriggerEvent().value == 61_norm); + REQUIRE(!synth.getVoiceView(1)->releasedOrFree()); +}