From 8c951d3a7f894aab0a023eb7f5b9daf8b6c5f573 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 22 Mar 2021 18:48:32 +0100 Subject: [PATCH 1/6] Remove unused ADSREnvelope::getNextValue() --- src/sfizz/ADSREnvelope.cpp | 51 -------------------------------------- src/sfizz/ADSREnvelope.h | 6 ----- 2 files changed, 57 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index b0c10d6e..132fe00b 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -59,57 +59,6 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const currentState = State::Delay; } -Float ADSREnvelope::getNextValue() noexcept -{ - if (shouldRelease && releaseDelay-- == 0) - currentState = State::Release; - - switch (currentState) { - case State::Delay: - if (delay-- > 0) - return start; - - currentState = State::Attack; - // fallthrough - case State::Attack: - currentValue += peak * attackStep; - if (currentValue < peak) - return currentValue; - - currentState = State::Hold; - currentValue = peak; - // fallthrough - case State::Hold: - if (hold-- > 0) - return currentValue; - - currentState = State::Decay; - // fallthrough - case State::Decay: - currentValue *= decayRate; - if (currentValue > sustainThreshold ) - return currentValue; - - currentState = State::Sustain; - currentValue = sustain; - // fallthrough - case State::Sustain: - if (freeRunning) - shouldRelease = true; - return currentValue; - case State::Release: - currentValue *= releaseRate; - if (currentValue > config::egReleaseThreshold) - return currentValue; - - currentState = State::Done; - currentValue = 0.0; - // fallthrough - default: - return 0.0; - } -} - void ADSREnvelope::getBlock(absl::Span output) noexcept { State currentState = this->currentState; diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index 648267d4..c155efa7 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -30,12 +30,6 @@ public: * @param velocity */ void reset(const EGDescription& desc, const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept; - /** - * @brief Get the next value for the envelope - * - * @return Float - */ - Float getNextValue() noexcept; /** * @brief Get a block of values for the envelope. This method tries hard to be efficient * and hopefully it is. From d19c41bd90f30602fc55711ab835fbd446a231da Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 22 Mar 2021 19:10:13 +0100 Subject: [PATCH 2/6] Smooth envelope transitions (fixes #373) --- src/sfizz/ADSREnvelope.cpp | 22 +++++++++++++++++----- src/sfizz/ADSREnvelope.h | 4 +++- src/sfizz/Config.h | 5 +++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 132fe00b..f004b05a 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -65,6 +65,7 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept Float currentValue = this->currentValue; bool shouldRelease = this->shouldRelease; int releaseDelay = this->releaseDelay; + Float transitionDelta = this->transitionDelta; while (!output.empty()) { size_t count = 0; @@ -106,8 +107,9 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept while (count < size && (currentValue *= decayRate) > sustain) output[count++] = currentValue; if (currentValue <= sustainThreshold) { - currentValue = sustain; currentState = State::Sustain; + currentValue = std::max(sustain, currentValue); + transitionDelta = (sustain - currentValue) / (sampleRate * config::egTransitionTime); } break; case State::Sustain: @@ -115,16 +117,25 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept shouldRelease = true; break; } - count = size; - currentValue = sustain; - sfz::fill(output.first(count), currentValue); + for (size_t i = 0; i < size; ++i) { + currentValue = std::max(sustain, currentValue + transitionDelta); + output[count++] = currentValue; + } break; case State::Release: while (count < size && (currentValue *= releaseRate) > config::egReleaseThreshold) output[count++] = currentValue; if (currentValue <= config::egReleaseThreshold) { - currentValue = 0; + currentState = State::Fadeout; + transitionDelta = -currentValue / (sampleRate * config::egTransitionTime); + } + break; + case State::Fadeout: + while (count < size && (currentValue += transitionDelta) > 0) + output[count++] = currentValue; + if (currentValue <= 0) { currentState = State::Done; + currentValue = 0; } break; default: @@ -144,6 +155,7 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept this->currentValue = currentValue; this->shouldRelease = shouldRelease; this->releaseDelay = releaseDelay; + this->transitionDelta = transitionDelta; ASSERT(!hasNanInf(output)); } diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index c155efa7..84a95eaa 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -62,7 +62,7 @@ public: * @return true * @return false */ - bool isReleased() const noexcept { return currentState == State::Release || shouldRelease; } + bool isReleased() const noexcept { return currentState >= State::Release || shouldRelease; } /** * @brief Get the remaining delay samples * @@ -83,6 +83,7 @@ private: Decay, Sustain, Release, + Fadeout, Done }; State currentState { State::Done }; @@ -99,6 +100,7 @@ private: int releaseDelay { 0 }; bool shouldRelease { false }; bool freeRunning { false }; + Float transitionDelta {}; LEAK_DETECTOR(ADSREnvelope); }; diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 89a44e09..a551b4f6 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -108,6 +108,11 @@ namespace config { finished. */ constexpr float egReleaseThreshold = 1e-4; + /** + Duration of a linear transition user to smooth cases of otherwise + immediate level transitions. (eg. decay->sustain or release->off) + */ + constexpr float egTransitionTime = 50e-3; /** Default metadata for MIDIName documents */ From a2cbec8c5e9581828ded25018a7a3adcf156eef2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 22 Mar 2021 19:30:34 +0100 Subject: [PATCH 3/6] Update tests: increase delays to account ADSR fadeout --- tests/FilesT.cpp | 2 +- tests/SynthT.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 07d12c03..6fc1e773 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -517,7 +517,7 @@ TEST_CASE("[Files] Off modes") REQUIRE( synth.getNumActiveVoices() == 3 ); REQUIRE( numPlayingVoices(synth) == 1 ); AudioBuffer buffer { 2, 256 }; - for (unsigned i = 0; i < 10; ++i) // Not enough for the "normal" voice to die + for (unsigned i = 0; i < 20; ++i) // Not enough for the "normal" voice to die synth.renderBlock(buffer); REQUIRE( synth.getNumActiveVoices() == 2 ); REQUIRE( fastVoice->isFree() ); diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index cd309176..3bf2cc64 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -31,7 +31,7 @@ TEST_CASE("[Synth] Play and check active voices") synth.noteOn(0, 36, 89); REQUIRE(synth.getNumActiveVoices() == 2); // Render for a while - for (int i = 0; i < 200; ++i) + for (int i = 0; i < 300; ++i) synth.renderBlock(buffer); REQUIRE(synth.getNumActiveVoices() == 0); } From 80d350d0c524127087cfa5097addcc4c475f73d3 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 22 Mar 2021 23:56:09 +0100 Subject: [PATCH 4/6] Remove ADSR unused variable --- src/sfizz/ADSREnvelope.cpp | 9 ++++----- src/sfizz/ADSREnvelope.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index f004b05a..0a79da8c 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -44,9 +44,8 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, const this->decayRate = secondsToExpRate(desc.getDecay(state, velocity)); this->releaseRate = secondsToExpRate(desc.getRelease(state, velocity)); this->hold = secondsToSamples(desc.getHold(state, velocity)); - this->peak = 1.0; this->sustain = normalizePercents(desc.getSustain(state, velocity)); - this->start = this->peak * normalizePercents(desc.getStart(state, velocity)); + this->start = normalizePercents(desc.getStart(state, velocity)); releaseDelay = 0; sustainThreshold = this->sustain + config::virtuallyZero; @@ -90,10 +89,10 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept currentState = State::Attack; break; case State::Attack: - while (count < size && (currentValue += peak * attackStep) < peak) + while (count < size && (currentValue += attackStep) < 1) output[count++] = currentValue; - if (currentValue >= peak) { - currentValue = peak; + if (currentValue >= 1) { + currentValue = 1; currentState = State::Hold; } break; diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index 84a95eaa..f5f435fb 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -94,7 +94,6 @@ private: Float releaseRate { 0 }; int hold { 0 }; Float start { 0 }; - Float peak { 0 }; Float sustain { 0 }; Float sustainThreshold { config::virtuallyZero }; int releaseDelay { 0 }; From cf7bbdd56578c051e6f58591fbd4f813b79a65f2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 23 Mar 2021 00:04:20 +0100 Subject: [PATCH 5/6] Fix the sustain loop --- 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 0a79da8c..063563ea 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -116,7 +116,7 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept shouldRelease = true; break; } - for (size_t i = 0; i < size; ++i) { + while (count < size) { currentValue = std::max(sustain, currentValue + transitionDelta); output[count++] = currentValue; } From 844aa1a6c29e23bdc49d60793b261e340b7e20b4 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 23 Mar 2021 00:08:10 +0100 Subject: [PATCH 6/6] Correctly set current value at exit of the release stage --- src/sfizz/ADSREnvelope.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 063563ea..e8fa12b9 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -79,6 +79,8 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept size = std::min(size, releaseDelay); } + Float previousValue; + switch (currentState) { case State::Delay: while (count < size && delay-- > 0) { @@ -122,10 +124,12 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept } break; case State::Release: + previousValue = currentValue; while (count < size && (currentValue *= releaseRate) > config::egReleaseThreshold) - output[count++] = currentValue; + output[count++] = previousValue = currentValue; if (currentValue <= config::egReleaseThreshold) { currentState = State::Fadeout; + currentValue = previousValue; transitionDelta = -currentValue / (sampleRate * config::egTransitionTime); } break;