From 13be8c9ac1d2697a60bb6cd6d22cc93fadf6945c Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 22 May 2021 00:40:13 +0200 Subject: [PATCH 1/5] Add the ability to cancel the release on the basic ADSR Envelope --- src/sfizz/ADSREnvelope.cpp | 11 +- src/sfizz/ADSREnvelope.h | 6 + src/sfizz/Voice.cpp | 6 + src/sfizz/modulations/ModGenerator.h | 9 ++ src/sfizz/modulations/ModMatrix.cpp | 13 ++ src/sfizz/modulations/ModMatrix.h | 5 + .../modulations/sources/ADSREnvelope.cpp | 122 +++++++++--------- src/sfizz/modulations/sources/ADSREnvelope.h | 1 + .../modulations/sources/ChannelAftertouch.cpp | 7 - .../modulations/sources/ChannelAftertouch.h | 1 - .../modulations/sources/PolyAftertouch.cpp | 7 - .../modulations/sources/PolyAftertouch.h | 1 - 12 files changed, 114 insertions(+), 75 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index daf0872a..346525e3 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -122,7 +122,8 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept break; } while (count < size) { - currentValue = std::max(sustain, currentValue + transitionDelta); + if (currentValue > sustain) + currentValue = std::max(sustain, currentValue + transitionDelta); output[count++] = currentValue; } break; @@ -173,6 +174,14 @@ void ADSREnvelope::startRelease(int releaseDelay) noexcept this->releaseDelay = releaseDelay; } +void ADSREnvelope::cancelRelease(int delay) noexcept +{ + (void)delay; + currentState = State::Sustain; + shouldRelease = false; + this->releaseDelay = -1; +} + void ADSREnvelope::setReleaseTime(Float timeInSeconds) noexcept { releaseRate = secondsToExpRate(timeInSeconds); diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index f1d3d668..ace1da7b 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -49,6 +49,12 @@ public: * @param releaseDelay the delay before releasing in samples */ void startRelease(int releaseDelay) noexcept; + /** + * @brief Cancel a release and get back into sustain. + * + * @param delay + */ + void cancelRelease(int delay) noexcept; /** * @brief Is the envelope smoothing? * diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fea3f196..ed3623b2 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -676,6 +676,12 @@ void Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept if (impl.noteIsOff_ && region.loopMode != LoopMode::one_shot && sostenutoPedalReleaseCondition && sustainPedalReleaseCondition) release(delay); + + if (region.checkSustain && (impl.sustainState_ == Impl::SustainState::Sustaining) + && impl.released() && (region.trigger != Trigger::release && region.trigger != Trigger::release_key) ) { + ModMatrix& modMatrix = impl.resources_.getModMatrix(); + modMatrix.cancelRelease(impl.id_, impl.region_->getId(), delay); + } } void Voice::registerPitchWheel(int delay, float pitch) noexcept diff --git a/src/sfizz/modulations/ModGenerator.h b/src/sfizz/modulations/ModGenerator.h index 7bb6c329..55fe46ba 100644 --- a/src/sfizz/modulations/ModGenerator.h +++ b/src/sfizz/modulations/ModGenerator.h @@ -49,6 +49,15 @@ public: */ virtual void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { (void)sourceKey; (void)voiceId; (void)delay; } + /** + * @brief Cancel the release and get back into sustain + * + * @param sourceKey identifier of the source to release + * @param voiceId the particular voice to initialize, if per-voice + * @param delay the frame time when it happens + */ + virtual void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { (void)sourceKey; (void)voiceId; (void)delay; } + /** * @brief Generate a cycle of the modulator * diff --git a/src/sfizz/modulations/ModMatrix.cpp b/src/sfizz/modulations/ModMatrix.cpp index 35ab05f7..98a92c86 100644 --- a/src/sfizz/modulations/ModMatrix.cpp +++ b/src/sfizz/modulations/ModMatrix.cpp @@ -281,6 +281,19 @@ void ModMatrix::releaseVoice(NumericId voiceId, NumericId regionI } } +void ModMatrix::cancelRelease(NumericId voiceId, NumericId regionId, unsigned delay) +{ + Impl& impl = *impl_; + + ASSERT(regionId); + + const auto idNumber = static_cast(regionId.number()); + for (auto idx: impl.sourceIndicesForRegion_[idNumber]) { + const Impl::Source& source = impl.sources_[idx]; + source.gen->cancelRelease(source.key, voiceId, delay); + } +} + void ModMatrix::beginCycle(unsigned numFrames) { Impl& impl = *impl_; diff --git a/src/sfizz/modulations/ModMatrix.h b/src/sfizz/modulations/ModMatrix.h index 2cef2c0e..2aa59fe4 100644 --- a/src/sfizz/modulations/ModMatrix.h +++ b/src/sfizz/modulations/ModMatrix.h @@ -116,6 +116,11 @@ public: */ void releaseVoice(NumericId voiceId, NumericId regionId, unsigned delay); + /** + * @brief Cancel release for a given voice. + */ + void cancelRelease(NumericId voiceId, NumericId regionId, unsigned delay); + /** * @brief Start modulation processing for the entire cycle. * This clears all the buffers. diff --git a/src/sfizz/modulations/sources/ADSREnvelope.cpp b/src/sfizz/modulations/sources/ADSREnvelope.cpp index 1189791b..0ec5af8e 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.cpp +++ b/src/sfizz/modulations/sources/ADSREnvelope.cpp @@ -18,6 +18,52 @@ ADSREnvelopeSource::ADSREnvelopeSource(VoiceManager& manager, MidiState& state) { } +ADSREnvelope* getEG(Voice* voice, const ModKey& key) +{ + ADSREnvelope* eg = nullptr; + if (!voice) + return eg; + + switch (key.id()) { + case ModId::AmpEG: + eg = voice->getAmplitudeEG(); + break; + case ModId::PitchEG: + eg = voice->getPitchEG(); + break; + case ModId::FilEG: + eg = voice->getFilterEG(); + break; + default: + return eg; + } + + return eg; +} + +const EGDescription* getEGDescription(const Region* region, const ModKey& key) +{ + const EGDescription* desc = nullptr; + if (!region) + return desc; + + switch (key.id()) { + case ModId::AmpEG: + desc = ®ion->amplitudeEG; + break; + case ModId::PitchEG: + desc = &*region->pitchEG; + break; + case ModId::FilEG: + desc = &*region->filterEG; + break; + default: + return desc; + } + + return desc; +} + void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) { Voice* voice = voiceManager_.getVoiceById(voiceId); @@ -27,29 +73,9 @@ void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId voiceId, } const Region* region = voice->getRegion(); - ADSREnvelope* eg = nullptr; - const EGDescription* desc = nullptr; - - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - desc = ®ion->amplitudeEG; - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - desc = &*region->pitchEG; - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - desc = &*region->filterEG; - break; - default: - ASSERTFALSE; - return; - } + ADSREnvelope* eg = getEG(voice, sourceKey); + const EGDescription* desc = getEGDescription(region, sourceKey); + ASSERT(eg); const TriggerEvent& triggerEvent = voice->getTriggerEvent(); const float sampleRate = voice->getSampleRate(); @@ -64,27 +90,24 @@ void ADSREnvelopeSource::release(const ModKey& sourceKey, NumericId voice return; } - ADSREnvelope* eg = nullptr; + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - break; - default: + eg->startRelease(delay); +} + +void ADSREnvelopeSource::cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) +{ + Voice* voice = voiceManager_.getVoiceById(voiceId); + if (!voice) { ASSERTFALSE; return; } - eg->startRelease(delay); + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); + + eg->cancelRelease(delay); } void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) @@ -95,25 +118,8 @@ void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId voic return; } - ADSREnvelope* eg = nullptr; - - switch (sourceKey.id()) { - case ModId::AmpEG: - eg = voice->getAmplitudeEG(); - ASSERT(eg); - break; - case ModId::PitchEG: - eg = voice->getPitchEG(); - ASSERT(eg); - break; - case ModId::FilEG: - eg = voice->getFilterEG(); - ASSERT(eg); - break; - default: - ASSERTFALSE; - return; - } + ADSREnvelope* eg = getEG(voice, sourceKey); + ASSERT(eg); eg->getBlock(buffer); } diff --git a/src/sfizz/modulations/sources/ADSREnvelope.h b/src/sfizz/modulations/sources/ADSREnvelope.h index d52a9ca0..436127a7 100644 --- a/src/sfizz/modulations/sources/ADSREnvelope.h +++ b/src/sfizz/modulations/sources/ADSREnvelope.h @@ -17,6 +17,7 @@ public: explicit ADSREnvelopeSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; + void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: diff --git a/src/sfizz/modulations/sources/ChannelAftertouch.cpp b/src/sfizz/modulations/sources/ChannelAftertouch.cpp index 96658aee..4f54c67b 100644 --- a/src/sfizz/modulations/sources/ChannelAftertouch.cpp +++ b/src/sfizz/modulations/sources/ChannelAftertouch.cpp @@ -23,13 +23,6 @@ void ChannelAftertouchSource::init(const ModKey& sourceKey, NumericId voi UNUSED(delay); } -void ChannelAftertouchSource::release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) -{ - UNUSED(sourceKey); - UNUSED(voiceId); - UNUSED(delay); -} - void ChannelAftertouchSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { UNUSED(sourceKey); diff --git a/src/sfizz/modulations/sources/ChannelAftertouch.h b/src/sfizz/modulations/sources/ChannelAftertouch.h index d162b7ec..f6ddd5a7 100644 --- a/src/sfizz/modulations/sources/ChannelAftertouch.h +++ b/src/sfizz/modulations/sources/ChannelAftertouch.h @@ -16,7 +16,6 @@ class ChannelAftertouchSource : public ModGenerator { public: explicit ChannelAftertouchSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; - void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: diff --git a/src/sfizz/modulations/sources/PolyAftertouch.cpp b/src/sfizz/modulations/sources/PolyAftertouch.cpp index ce91f7bb..5c4ef22c 100644 --- a/src/sfizz/modulations/sources/PolyAftertouch.cpp +++ b/src/sfizz/modulations/sources/PolyAftertouch.cpp @@ -23,13 +23,6 @@ void PolyAftertouchSource::init(const ModKey& sourceKey, NumericId voiceI UNUSED(delay); } -void PolyAftertouchSource::release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) -{ - UNUSED(sourceKey); - UNUSED(voiceId); - UNUSED(delay); -} - void PolyAftertouchSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { UNUSED(sourceKey); diff --git a/src/sfizz/modulations/sources/PolyAftertouch.h b/src/sfizz/modulations/sources/PolyAftertouch.h index c70ea360..5d37ee2a 100644 --- a/src/sfizz/modulations/sources/PolyAftertouch.h +++ b/src/sfizz/modulations/sources/PolyAftertouch.h @@ -16,7 +16,6 @@ class PolyAftertouchSource : public ModGenerator { public: explicit PolyAftertouchSource(VoiceManager &manager, MidiState& state); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; - void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: From 8721c34e1423a90590b026d188a5c13c4556bee8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 14:50:07 +0200 Subject: [PATCH 2/5] Add the FlexEG cancellation --- src/sfizz/FlexEnvelope.cpp | 36 +++++++++++++++---- src/sfizz/FlexEnvelope.h | 5 +++ .../modulations/sources/FlexEnvelope.cpp | 20 +++++++++++ src/sfizz/modulations/sources/FlexEnvelope.h | 1 + 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 4158978f..98e09552 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -49,6 +49,7 @@ struct FlexEnvelope::Impl { // void process(absl::Span out); + bool advanceToStage(unsigned stageNumber); bool advanceToNextStage(); }; @@ -114,6 +115,25 @@ void FlexEnvelope::release(unsigned releaseDelay) impl.currentFramesUntilRelease_ = releaseDelay; } +void FlexEnvelope::cancelRelease(unsigned delay) +{ + Impl& impl = *impl_; + const FlexEGDescription& desc = *impl.desc_; + + if (impl.currentFramesUntilRelease_) { + // Cancel the future release + impl.currentFramesUntilRelease_ = absl::nullopt; + return; + } + + if (!impl.isReleased_) + return; + + impl.isReleased_ = false; + impl.advanceToStage(desc.sustain); + impl.stageTargetLevel_ = impl.currentLevel_; +} + unsigned FlexEnvelope::getRemainingDelay() const noexcept { const Impl& impl = *impl_; @@ -226,25 +246,29 @@ void FlexEnvelope::Impl::process(absl::Span out) } } -bool FlexEnvelope::Impl::advanceToNextStage() +bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) { const FlexEGDescription& desc = *desc_; - unsigned nextStageNo = currentStageNumber_ + 1; - currentStageNumber_ = nextStageNo; + currentStageNumber_ = stageNumber; - if (nextStageNo >= desc.points.size()) + if (stageNumber >= desc.points.size()) return false; - const FlexEGPoint& point = desc.points[nextStageNo]; + const FlexEGPoint& point = desc.points[stageNumber]; stageSourceLevel_ = currentLevel_; stageTargetLevel_ = point.level; stageTime_ = point.time; - stageSustained_ = int(nextStageNo) == desc.sustain; + stageSustained_ = int(stageNumber) == desc.sustain; stageCurve_ = &point.curve(); currentTime_ = 0; return true; }; +bool FlexEnvelope::Impl::advanceToNextStage() +{ + return advanceToStage(currentStageNumber_ + 1); +} + } // namespace sfz diff --git a/src/sfizz/FlexEnvelope.h b/src/sfizz/FlexEnvelope.h index d8cc2bf0..6a737cf1 100644 --- a/src/sfizz/FlexEnvelope.h +++ b/src/sfizz/FlexEnvelope.h @@ -47,6 +47,11 @@ public: */ void release(unsigned releaseDelay); + /** + Cancel the release + */ + void cancelRelease(unsigned delay); + /** Get the remaining delay samples */ diff --git a/src/sfizz/modulations/sources/FlexEnvelope.cpp b/src/sfizz/modulations/sources/FlexEnvelope.cpp index 5184bc5e..b3e85cfc 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.cpp +++ b/src/sfizz/modulations/sources/FlexEnvelope.cpp @@ -66,6 +66,26 @@ void FlexEnvelopeSource::release(const ModKey& sourceKey, NumericId voice eg->release(delay); } +void FlexEnvelopeSource::cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) +{ + unsigned egIndex = sourceKey.parameters().N; + + Voice* voice = voiceManager_.getVoiceById(voiceId); + if (!voice) { + ASSERTFALSE; + return; + } + + const Region* region = voice->getRegion(); + if (egIndex >= region->flexEGs.size()) { + ASSERTFALSE; + return; + } + + FlexEnvelope* eg = voice->getFlexEG(egIndex); + eg->cancelRelease(delay); +} + void FlexEnvelopeSource::generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) { unsigned egIndex = sourceKey.parameters().N; diff --git a/src/sfizz/modulations/sources/FlexEnvelope.h b/src/sfizz/modulations/sources/FlexEnvelope.h index 1868ab10..81b7a2f7 100644 --- a/src/sfizz/modulations/sources/FlexEnvelope.h +++ b/src/sfizz/modulations/sources/FlexEnvelope.h @@ -16,6 +16,7 @@ public: explicit FlexEnvelopeSource(VoiceManager& manager); void init(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void release(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; + void cancelRelease(const ModKey& sourceKey, NumericId voiceId, unsigned delay) override; void generate(const ModKey& sourceKey, NumericId voiceId, absl::Span buffer) override; private: From 1153c1521171ca2a1f267a990ed6e00a7814cbb8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 15:23:12 +0200 Subject: [PATCH 3/5] Parse `hint_sustain_cancels_release` --- src/sfizz/Defaults.cpp | 1 + src/sfizz/Defaults.h | 1 + src/sfizz/Synth.cpp | 6 ++++++ src/sfizz/SynthConfig.h | 2 ++ src/sfizz/Voice.cpp | 1 + 5 files changed, 11 insertions(+) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 5908a340..0cb3803c 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -187,6 +187,7 @@ FloatSpec lofiBitred { 0.0f, {0.0f, 100.0f}, 0 }; FloatSpec lofiDecim { 0.0f, {0.0f, 100.0f}, 0 }; FloatSpec rectify { 0.0f, {0.0f, 100.0f}, 0 }; UInt32Spec stringsNumber { maxStrings, {0, maxStrings}, 0 }; +BoolSpec sustainCancelsRelease { false, {0, 1}, kEnforceBounds }; ESpec trigger { Trigger::attack, {Trigger::attack, Trigger::release_key}, 0}; ESpec crossfadeCurve { CrossfadeCurve::power, {CrossfadeCurve::gain, CrossfadeCurve::power}, 0}; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 71e3b029..41a72607 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -305,6 +305,7 @@ namespace Default extern const OpcodeSpec selfMask; extern const OpcodeSpec filter; extern const OpcodeSpec eq; + extern const OpcodeSpec sustainCancelsRelease; // Default/max count for objects constexpr int numEQs { 3 }; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c76de616..54cca904 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -437,6 +437,12 @@ void Synth::Impl::handleControlOpcodes(const std::vector& members) DBG("Unsupported value for hint_stealing: " << member.value); } break; + case hash("hint_sustain_cancels_release"): + { + SynthConfig& config = resources_.getSynthConfig(); + config.sustainCancelsRelease = member.read(Default::sustainCancelsRelease); + } + break; default: // Unsupported control opcode DBG("Unsupported control opcode: " << member.name); diff --git a/src/sfizz/SynthConfig.h b/src/sfizz/SynthConfig.h index dbd9414b..f5f38d89 100644 --- a/src/sfizz/SynthConfig.h +++ b/src/sfizz/SynthConfig.h @@ -28,5 +28,7 @@ struct SynthConfig { return freeWheeling ? freeWheelingOscillatorQuality : liveOscillatorQuality; } + + bool sustainCancelsRelease { Default::sustainCancelsRelease }; }; } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index ed3623b2..78e96d64 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -678,6 +678,7 @@ void Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept release(delay); if (region.checkSustain && (impl.sustainState_ == Impl::SustainState::Sustaining) + && impl.resources_.getSynthConfig().sustainCancelsRelease && impl.released() && (region.trigger != Trigger::release && region.trigger != Trigger::release_key) ) { ModMatrix& modMatrix = impl.resources_.getModMatrix(); modMatrix.cancelRelease(impl.id_, impl.region_->getId(), delay); From 8e13b232a30dd4357d53554b2e8be7b2197f32f7 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 15:31:59 +0200 Subject: [PATCH 4/5] Add tests --- tests/SynthT.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 3ca5db3f..919163a6 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1828,3 +1828,93 @@ TEST_CASE("[Synth] Short empty files are turned into *silence") }; REQUIRE(messageList == expected); } + +TEST_CASE("[Synth] Sustain cancels release (Flex EG)") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + hint_sustain_cancels_release=1 + sample=*sine + eg01_ampeg=1 eg01_sustain=2 + eg01_time1=0 eg01_level1=0.00 + eg01_time2=0.06 eg01_level2=0.92 + eg01_time3=1.00 eg01_level3=0.00 eg01_shape3=-3 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); +} + +TEST_CASE("[Synth] Sustain cancels release (Flex EG) is off by default") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*sine + eg01_ampeg=1 eg01_sustain=2 + eg01_time1=0 eg01_level1=0.00 + eg01_time2=0.06 eg01_level2=0.92 + eg01_time3=1.00 eg01_level3=0.00 eg01_shape3=-3 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); +} + +TEST_CASE("[Synth] Sustain cancels release") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + hint_sustain_cancels_release=1 + sample=*sine ampeg_release=10 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); +} + +TEST_CASE("[Synth] Sustain cancels release is off by default") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"( + sample=*sine ampeg_release=10 + )"); + synth.noteOn(0, 60, 63 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.noteOff(0, 60, 0 ); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); + synth.renderBlock(buffer); + synth.renderBlock(buffer); + synth.cc(0, 64, 127); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { } ); +} From 6b0c79b162d99f95f68750d28f4d5aa7f21ce2e4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 23 May 2021 16:37:23 +0200 Subject: [PATCH 5/5] Remove some branches from the envelope --- src/sfizz/ADSREnvelope.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 346525e3..873069ce 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -123,7 +123,7 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept } while (count < size) { if (currentValue > sustain) - currentValue = std::max(sustain, currentValue + transitionDelta); + currentValue += transitionDelta; output[count++] = currentValue; } break;