From 77589f4109ed2c55a52bb44900e1fb40f4955862 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 24 Mar 2020 18:28:26 +0100 Subject: [PATCH] Rework the envelope generator --- src/sfizz/ADSREnvelope.cpp | 149 ++++++------------------------------- src/sfizz/ADSREnvelope.h | 7 +- 2 files changed, 27 insertions(+), 129 deletions(-) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index c7f2952f..9c8f5559 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -18,10 +18,21 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, return static_cast(timeInSeconds * sampleRate); }; + auto secondsToLinRate = [sampleRate](Type timeInSeconds) { + timeInSeconds = std::max(timeInSeconds, config::virtuallyZero); + return 1 / (sampleRate * timeInSeconds); + }; + + auto secondsToExpRate = [sampleRate](Type timeInSeconds) { + if (timeInSeconds < config::virtuallyZero) + return 0.0; + return std::exp(-10.0 / (timeInSeconds * sampleRate)); + }; + this->delay = delay + secondsToSamples(desc.getDelay(state, velocity)); - this->attack = secondsToSamples(desc.getAttack(state, velocity)); - this->decay = secondsToSamples(desc.getDecay(state, velocity)); - this->release = secondsToSamples(desc.getRelease(state, velocity)); + this->attackStep = secondsToLinRate(desc.getAttack(state, velocity)); + 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)); @@ -30,7 +41,6 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, releaseDelay = 0; shouldRelease = false; freeRunning = ((region.trigger == SfzTrigger::release) || (region.trigger == SfzTrigger::release_key)); - step = 0.0; currentValue = this->start; currentState = State::Delay; } @@ -38,13 +48,8 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, template Type ADSREnvelope::getNextValue() noexcept { - if (shouldRelease && releaseDelay-- == 0) { + if (shouldRelease && releaseDelay-- == 0) currentState = State::Release; - if (currentValue > config::virtuallyZero) - step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue + config::virtuallyZero)) / (release > 0 ? release : 1)); - else - step = 1; - } switch (currentState) { case State::Delay: @@ -52,13 +57,11 @@ Type ADSREnvelope::getNextValue() noexcept return start; currentState = State::Attack; - step = (peak - currentValue) / (attack > 0 ? attack : 1); // fallthrough case State::Attack: - if (attack-- > 0) { - currentValue += step; + currentValue += peak * attackStep; + if (currentValue < peak) return currentValue; - } currentState = State::Hold; currentValue = peak; @@ -67,14 +70,12 @@ Type ADSREnvelope::getNextValue() noexcept if (hold-- > 0) return currentValue; - step = std::exp(std::log(sustain + config::virtuallyZero) / (decay > 0 ? decay : 1)); currentState = State::Decay; // fallthrough case State::Decay: - if (decay-- > 0) { - currentValue *= step; + currentValue *= decayRate; + if (currentValue > sustain) return currentValue; - } currentState = State::Sustain; currentValue = sustain; @@ -84,10 +85,9 @@ Type ADSREnvelope::getNextValue() noexcept shouldRelease = true; return currentValue; case State::Release: - if (release-- > 0) { - currentValue *= step; + currentValue *= releaseRate; + if (currentValue > config::virtuallyZero) return currentValue; - } currentState = State::Done; currentValue = 0.0; @@ -100,109 +100,8 @@ Type ADSREnvelope::getNextValue() noexcept template void ADSREnvelope::getBlock(absl::Span output) noexcept { - auto originalSpan = output; - auto remainingSamples = static_cast(output.size()); - int length; - switch (currentState) { - case State::Delay: - length = min(remainingSamples, delay); - fill(output, currentValue); - output.remove_prefix(length); - remainingSamples -= length; - delay -= length; - if (remainingSamples == 0) - break; - - currentState = State::Attack; - step = (peak - start) / (attack > 0 ? attack : 1); - // fallthrough - case State::Attack: - length = min(remainingSamples, attack); - currentValue = linearRamp(output, currentValue, step); - output.remove_prefix(length); - remainingSamples -= length; - attack -= length; - if (remainingSamples == 0) - break; - - currentValue = peak; - currentState = State::Hold; - // fallthrough - case State::Hold: - length = min(remainingSamples, hold); - fill(output, currentValue); - output.remove_prefix(length); - remainingSamples -= length; - hold -= length; - if (remainingSamples == 0) - break; - - step = std::exp(std::log(sustain + config::virtuallyZero) / (decay > 0 ? decay : 1)); - currentState = State::Decay; - // fallthrough - case State::Decay: - length = min(remainingSamples, decay); - currentValue = multiplicativeRamp(output, currentValue, step); - output.remove_prefix(length); - remainingSamples -= length; - decay -= length; - if (remainingSamples == 0) - break; - - currentValue = sustain; - currentState = State::Sustain; - // fallthrough - case State::Sustain: - if (freeRunning) - shouldRelease = true; - break; - case State::Release: - length = min(remainingSamples, release); - currentValue = multiplicativeRamp(output, currentValue, step); - output.remove_prefix(length); - remainingSamples -= length; - release -= length; - if (remainingSamples == 0) - break; - - currentValue = 0.0; - currentState = State::Done; - // fallthrough - case State::Done: - // fallthrough - default: - break; - } - fill(output, currentValue); - - if (shouldRelease) { - remainingSamples = static_cast(originalSpan.size()); - if (releaseDelay > remainingSamples) - { - releaseDelay -= remainingSamples; - return; - } - - originalSpan.remove_prefix(releaseDelay); - if (originalSpan.size() > 0) - currentValue = originalSpan.front(); - if (currentValue > config::virtuallyZero) - step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1)); - else - step = 1; - remainingSamples -= releaseDelay; - length = min(remainingSamples, release); - currentState = State::Release; - currentValue = multiplicativeRamp(originalSpan, currentValue, step); - originalSpan.remove_prefix(length); - release -= length; - - if (release == 0) { - currentValue = 0.0; - currentState = State::Done; - fill(originalSpan, 0.0); - } - } + for (size_t i = 0, n = output.size(); i < n; ++i) + output[i] = getNextValue(); } template @@ -233,7 +132,7 @@ void ADSREnvelope::startRelease(int releaseDelay, bool fastRelease) noexce currentState = State::Release; if (fastRelease) - this->release = 0; + this->releaseRate = 0; } } diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index ce2b959d..608567c6 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -86,11 +86,10 @@ private: }; State currentState { State::Done }; Type currentValue { 0.0 }; - Type step { 0.0 }; int delay { 0 }; - int attack { 0 }; - int decay { 0 }; - int release { 0 }; + Type attackStep { 0 }; + Type decayRate { 0 }; + Type releaseRate { 0 }; int hold { 0 }; Type start { 0 }; Type peak { 0 };