From ed09e30fcf18e98173f95b6e3b8aa2c349713e6b Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 23 Mar 2021 22:33:41 +0100 Subject: [PATCH] Missing the delayed release logic for sostenuto --- src/sfizz/Synth.cpp | 1 - src/sfizz/Voice.cpp | 54 +++++++++++++++++++++------------ tests/SynthT.cpp | 73 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 98 insertions(+), 30 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 879816b1..4e2acebb 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -1155,7 +1155,6 @@ void Synth::Impl::startDelayedReleaseVoices(Region* region, int delay, SisterVoi } for (auto& note: region->delayedReleases) { - // FIXME: we really need to have some form of common method to find and start voices... const TriggerEvent noteOffEvent { TriggerEventType::NoteOff, note.first, note.second }; startVoice(region, delay, noteOffEvent, ring); } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index ce3edd70..47cfec83 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -202,6 +202,8 @@ struct Voice::Impl State state_ { State::idle }; bool noteIsOff_ { false }; + enum class SustainState { Up, Sustaining }; + SustainState sustainState_ { SustainState::Up }; enum class SostenutoState { Up, Sustaining, PreviouslyDown }; SostenutoState sostenutoState_ { SostenutoState::Up }; @@ -461,6 +463,13 @@ bool Voice::startVoice(Region* region, int delay, const TriggerEvent& event) noe impl.resources_.modMatrix.initVoice(impl.id_, region->getId(), impl.initialDelay_); impl.saveModulationTargets(region); + if (region->checkSustain) { + const bool sustainPressed = + impl.resources_.midiState.getCCValue(region->sustainCC) >= region->sustainThreshold; + impl.sustainState_ = + sustainPressed ? Impl::SustainState::Sustaining : Impl::SustainState::Up; + } + if (region->checkSostenuto) { const bool sostenutoPressed = impl.resources_.midiState.getCCValue(region->sostenutoCC) >= region->sostenutoThreshold; @@ -552,12 +561,13 @@ void Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept if (impl.region_->loopMode == LoopMode::one_shot) return; - if (!impl.region_->checkSustain - || impl.resources_.midiState.getCCValue(impl.region_->sustainCC) < impl.region_->sustainThreshold) - release(delay); + const bool sustainPedalReleaseCondition = !impl.region_->checkSustain + || impl.sustainState_ != Impl::SustainState::Sustaining; - if (!impl.region_->checkSostenuto - || impl.sostenutoState_ != Impl::SostenutoState::Sustaining) + const bool sostenutoPedalReleaseCondition = !impl.region_->checkSostenuto + || impl.sostenutoState_ != Impl::SostenutoState::Sustaining; + + if (sustainPedalReleaseCondition && sostenutoPedalReleaseCondition) release(delay); } } @@ -572,24 +582,30 @@ void Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept if (impl.state_ != State::playing) return; - if (impl.region_->checkSustain - && impl.noteIsOff_ - && ccNumber == impl.region_->sustainCC - && ccValue < impl.region_->sustainThreshold) - release(delay); - - if (impl.region_->checkSostenuto - && ccNumber == impl.region_->sostenutoCC) { + if (impl.region_->checkSustain && (ccNumber == impl.region_->sostenutoCC)) { if (ccValue < impl.region_->sostenutoThreshold) { impl.sostenutoState_ = Impl::SostenutoState::Up; - } else { - if (impl.sostenutoState_ == Impl::SostenutoState::Up) - impl.sostenutoState_ = Impl::SostenutoState::Sustaining; + } else if (impl.sostenutoState_ == Impl::SostenutoState::Up) { + impl.sostenutoState_ = Impl::SostenutoState::Sustaining; } - - if (impl.sostenutoState_ != Impl::SostenutoState::Sustaining && impl.noteIsOff_) - release(delay); } + + if (impl.region_->checkSostenuto && (ccNumber == impl.region_->sustainCC)) { + if (ccValue < impl.region_->sustainThreshold) { + impl.sustainState_ = Impl::SustainState::Up; + } else { + impl.sustainState_ = Impl::SustainState::Sustaining; + } + } + + const bool sustainPedalReleaseCondition = !impl.region_->checkSustain + || (impl.noteIsOff_ && (impl.sustainState_ != Impl::SustainState::Sustaining)); + + const bool sostenutoPedalReleaseCondition = !impl.region_->checkSostenuto + || (impl.noteIsOff_ && (impl.sostenutoState_ != Impl::SostenutoState::Sustaining)); + + if (sostenutoPedalReleaseCondition && sustainPedalReleaseCondition) + release(delay); } void Voice::registerPitchWheel(int delay, float pitch) noexcept diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 8175b740..a1692aaa 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -835,15 +835,15 @@ TEST_CASE("[Synth] Release (Different sostenuto CC)") { sfz::Synth synth; synth.loadSfzString(fs::current_path() / "tests/TestFiles/release.sfz", R"( - sostenuto_cc=54 + sostenuto_cc=54 key=62 sample=*silence key=62 sample=*sine trigger=release )"); synth.noteOn(0, 62, 85); - synth.cc(0, 54, 127); - synth.noteOff(0, 62, 85); + synth.cc(1, 54, 127); + synth.noteOff(2, 62, 85); REQUIRE( synth.getNumActiveVoices() == 1 ); - synth.cc(0, 54, 0); + synth.cc(3, 54, 0); REQUIRE( synth.getNumActiveVoices() == 2 ); } @@ -881,6 +881,59 @@ TEST_CASE("[Synth] Sustain threshold") REQUIRE( synth.getNumActiveVoices() == 5 ); } +TEST_CASE("[Synth] Sustain") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sostenuto.sfz", R"( + sample=*sine + )"); + + SECTION("1 note") + { + synth.noteOn(0, 62, 85); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.cc(1, 64, 1); + synth.noteOff(2, 62, 85); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.cc(3, 64, 0); + REQUIRE( numPlayingVoices(synth) == 0 ); + } + + SECTION("2 notes") + { + synth.noteOn(0, 62, 85); + synth.noteOn(0, 64, 85); + synth.cc(1, 64, 1); + synth.noteOff(2, 62, 85); + synth.noteOff(2, 64, 85); + REQUIRE( numPlayingVoices(synth) == 2 ); + synth.cc(3, 64, 0); + REQUIRE( numPlayingVoices(synth) == 0 ); + } + + SECTION("1 note after the pedal is depressed") + { + synth.cc(0, 64, 1); + synth.noteOn(1, 62, 85); + synth.noteOff(2, 62, 85); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.cc(3, 64, 0); + REQUIRE( numPlayingVoices(synth) == 0 ); + } + + SECTION("2 notes, 1 after the pedal is depressed") + { + synth.noteOn(0, 62, 85); + synth.cc(1, 64, 1); + synth.noteOn(2, 64, 85); + synth.noteOff(3, 62, 85); + synth.noteOff(3, 64, 85); + REQUIRE( numPlayingVoices(synth) == 2 ); + synth.cc(4, 64, 0); + REQUIRE( numPlayingVoices(synth) == 0 ); + } +} + TEST_CASE("[Synth] Sostenuto") { sfz::Synth synth; @@ -893,9 +946,9 @@ TEST_CASE("[Synth] Sostenuto") synth.noteOn(0, 62, 85); synth.cc(1, 66, 1); synth.noteOff(2, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( numPlayingVoices(synth) == 1 ); synth.cc(3, 66, 0); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( numPlayingVoices(synth) == 0 ); } SECTION("2 notes") @@ -905,9 +958,9 @@ TEST_CASE("[Synth] Sostenuto") synth.cc(1, 66, 1); synth.noteOff(2, 62, 85); synth.noteOff(2, 64, 85); - REQUIRE( synth.getNumActiveVoices() == 2 ); + REQUIRE( numPlayingVoices(synth) == 2 ); synth.cc(3, 66, 0); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( numPlayingVoices(synth) == 0 ); } SECTION("1 note but after the pedal is depressed") @@ -915,7 +968,7 @@ TEST_CASE("[Synth] Sostenuto") synth.cc(0, 66, 1); synth.noteOn(1, 62, 85); synth.noteOff(2, 62, 85); - REQUIRE( synth.getNumActiveVoices() == 0 ); + REQUIRE( numPlayingVoices(synth) == 0 ); } SECTION("2 notes, 1 after the pedal is depressed") @@ -925,7 +978,7 @@ TEST_CASE("[Synth] Sostenuto") synth.noteOn(2, 64, 85); synth.noteOff(3, 62, 85); synth.noteOff(3, 64, 85); - REQUIRE( synth.getNumActiveVoices() == 1 ); + REQUIRE( numPlayingVoices(synth) == 1 ); REQUIRE( getActiveVoices(synth)[0]->getTriggerEvent().number == 62 ); } }