diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 25bcb538..e1840d94 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -102,11 +102,23 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept { State currentState = this->currentState; Type currentValue = this->currentValue; + bool shouldRelease = this->shouldRelease; + int releaseDelay = this->releaseDelay; while (!output.empty()) { size_t count = 0; size_t size = output.size(); + if (shouldRelease && releaseDelay == 0) { + // release takes effect this frame + currentState = State::Release; + releaseDelay = -1; + } + else if (shouldRelease && releaseDelay > 0) { + // prevent computing the segment further than release point + size = std::min(size, releaseDelay); + } + switch (currentState) { case State::Delay: while (count < size && delay-- > 0) { @@ -139,23 +151,13 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept } break; case State::Sustain: - if (freeRunning) + if (!shouldRelease && freeRunning) { shouldRelease = true; - if (!shouldRelease) { - count = size; - currentValue = sustain; - sfz::fill(output.first(count), currentValue); - } - else { - if (releaseDelay > 0) { - count = clamp(releaseDelay, 0, size); - currentValue = sustain; - sfz::fill(output.first(count), currentValue); - releaseDelay -= count; - } - if (releaseDelay <= 0) - currentState = State::Release; + break; } + count = size; + currentValue = sustain; + sfz::fill(output.first(count), currentValue); break; case State::Release: while (count < size && (currentValue *= releaseRate) > config::virtuallyZero) @@ -172,11 +174,16 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept break; } + if (shouldRelease) + releaseDelay = std::max(-1, releaseDelay - static_cast(count)); + output.remove_prefix(count); } this->currentState = currentState; this->currentValue = currentValue; + this->shouldRelease = shouldRelease; + this->releaseDelay = releaseDelay; } template @@ -203,9 +210,6 @@ void ADSREnvelope::startRelease(int releaseDelay, bool fastRelease) noexce shouldRelease = true; this->releaseDelay = releaseDelay; - if (releaseDelay == 0) - currentState = State::Release; - if (fastRelease) this->releaseRate = 0; }