diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index af7e1a01..de92d4c6 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -614,32 +614,38 @@ void Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept if (impl.region_ == nullptr) return; + const Region& region = *impl.region_; + if (impl.state_ != State::playing) return; - if (impl.region_->checkSustain && (ccNumber == impl.region_->sostenutoCC)) { - if (ccValue < impl.region_->sostenutoThreshold) { + if (ccNumber != region.sustainCC && ccNumber != region.sostenutoCC) + return; + + if (region.checkSustain && (ccNumber == region.sostenutoCC)) { + if (ccValue < region.sostenutoThreshold) { impl.sostenutoState_ = Impl::SostenutoState::Up; } else if (impl.sostenutoState_ == Impl::SostenutoState::Up) { impl.sostenutoState_ = Impl::SostenutoState::Sustaining; } } - if (impl.region_->checkSostenuto && (ccNumber == impl.region_->sustainCC)) { - if (ccValue < impl.region_->sustainThreshold) { + if (region.checkSostenuto && (ccNumber == region.sustainCC)) { + if (ccValue < region.sustainThreshold) { impl.sustainState_ = Impl::SustainState::Up; } else { impl.sustainState_ = Impl::SustainState::Sustaining; } } - const bool sustainPedalReleaseCondition = !impl.region_->checkSustain + const bool sustainPedalReleaseCondition = !region.checkSustain || (impl.sustainState_ != Impl::SustainState::Sustaining); - const bool sostenutoPedalReleaseCondition = !impl.region_->checkSostenuto + const bool sostenutoPedalReleaseCondition = !region.checkSostenuto || (impl.sostenutoState_ != Impl::SostenutoState::Sustaining); - if (impl.noteIsOff_ && sostenutoPedalReleaseCondition && sustainPedalReleaseCondition) + if (impl.noteIsOff_ && region.loopMode != LoopMode::one_shot + && sostenutoPedalReleaseCondition && sustainPedalReleaseCondition) release(delay); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 5ae2cb51..8db5b06c 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -926,6 +926,52 @@ TEST_CASE("[Synth] Release (sustain + sostenuto)") } } +TEST_CASE("[Synth] One shot regions with sustain + sostenuto") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/one_shot_sustain.sfz", R"( + key=60 sample=kick.wav loop_mode=one_shot + )"); + SECTION("Sustain") + { + synth.noteOn(0, 60, 85); + synth.cc(1, 64, 127); + synth.noteOff(2, 60, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.cc(1, 64, 0); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + } + SECTION("Sostenuto") + { + synth.noteOn(0, 60, 85); + synth.cc(1, 66, 127); + synth.noteOff(2, 60, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.cc(1, 66, 0); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + } + SECTION("Sostenuto up first") + { + synth.noteOn(0, 60, 85); + synth.cc(1, 66, 127); + synth.cc(1, 64, 127); + synth.noteOff(2, 60, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.cc(3, 66, 0); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.cc(4, 64, 0); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + } +} + TEST_CASE("[Synth] Sustain threshold default") { sfz::Synth synth;