Skip to content

Commit

Permalink
Smooth envelope transitions (fixes sfztools#373)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpcima committed Mar 22, 2021
1 parent 8c951d3 commit 2519498
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 7 deletions.
22 changes: 17 additions & 5 deletions src/sfizz/ADSREnvelope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ void ADSREnvelope::getBlock(absl::Span<Float> 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;
Expand Down Expand Up @@ -106,25 +107,35 @@ void ADSREnvelope::getBlock(absl::Span<Float> 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:
if (!shouldRelease && freeRunning) {
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:
Expand All @@ -144,6 +155,7 @@ void ADSREnvelope::getBlock(absl::Span<Float> output) noexcept
this->currentValue = currentValue;
this->shouldRelease = shouldRelease;
this->releaseDelay = releaseDelay;
this->transitionDelta = transitionDelta;

ASSERT(!hasNanInf(output));
}
Expand Down
6 changes: 4 additions & 2 deletions src/sfizz/ADSREnvelope.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ADSREnvelope {
* @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
*
Expand All @@ -83,6 +83,7 @@ class ADSREnvelope {
Decay,
Sustain,
Release,
Fadeout,
Done
};
State currentState { State::Done };
Expand All @@ -99,7 +100,8 @@ class ADSREnvelope {
int releaseDelay { 0 };
bool shouldRelease { false };
bool freeRunning { false };
LEAK_DETECTOR(ADSREnvelope);
Float transitionDelta {};
};
LEAK_DETECTOR(ADSREnvelope);

}
5 changes: 5 additions & 0 deletions src/sfizz/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down

0 comments on commit 2519498

Please sign in to comment.