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 */