sfizz/sources/ADSREnvelope.cpp
2019-08-30 00:49:58 +02:00

228 lines
7.1 KiB
C++

// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ADSREnvelope.h"
#include "Globals.h"
#include "Helpers.h"
#include "SIMDHelpers.h"
#include <algorithm>
namespace sfz {
template <class Type>
void ADSREnvelope<Type>::reset(int attack, int release, Type sustain, int delay, int decay, int hold, Type start, Type depth) noexcept
{
ASSERT(start <= 1.0f);
ASSERT(sustain <= 1.0f);
sustain = std::clamp<Type>(sustain, 0.0, 1.0);
start = std::clamp<Type>(start, 0.0, 1.0);
currentState = State::Done;
this->delay = delay;
this->attack = attack;
this->decay = decay;
this->release = release;
this->hold = hold;
this->start = depth * start;
this->sustain = depth * sustain;
this->peak = depth;
releaseDelay = 0;
shouldRelease = false;
step = 0.0;
currentValue = this->start;
currentState = State::Delay;
}
template <class Type>
Type ADSREnvelope<Type>::getNextValue() noexcept
{
if (shouldRelease && releaseDelay-- == 0) {
currentState = State::Release;
step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1));
}
switch (currentState) {
case State::Delay:
if (delay-- > 0)
return start;
currentState = State::Attack;
step = (1.0 - currentValue) / (attack > 0 ? attack : 1);
[[fallthrough]];
case State::Attack:
if (attack-- > 0) {
currentValue += step;
return currentValue;
}
currentState = State::Hold;
currentValue = 1.0;
[[fallthrough]];
case State::Hold:
if (hold-- > 0)
return currentValue;
step = std::exp(std::log(sustain) / (decay > 0 ? decay : 1));
currentState = State::Decay;
[[fallthrough]];
case State::Decay:
if (decay-- > 0) {
currentValue *= step;
return currentValue;
}
currentState = State::Sustain;
currentValue = sustain;
[[fallthrough]];
case State::Sustain:
return currentValue;
case State::Release:
if (release-- > 0) {
currentValue *= step;
return currentValue;
}
currentState = State::Done;
currentValue = 0.0;
[[fallthrough]];
default:
return 0.0;
}
}
template <class Type>
void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
{
auto originalSpan = output;
auto remainingSamples = static_cast<int>(output.size());
int length;
switch (currentState) {
case State::Delay:
length = min(remainingSamples, delay);
::fill<Type>(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<Type>(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<Type>(output, currentValue);
output.remove_prefix(length);
remainingSamples -= length;
hold -= length;
if (remainingSamples == 0)
break;
step = std::exp(std::log(sustain) / (decay > 0 ? decay : 1));
currentState = State::Decay;
[[fallthrough]];
case State::Decay:
length = min(remainingSamples, decay);
currentValue = ::multiplicativeRamp<Type>(output, currentValue, step);
output.remove_prefix(length);
remainingSamples -= length;
decay -= length;
if (remainingSamples == 0)
break;
currentValue = sustain;
currentState = State::Sustain;
[[fallthrough]];
case State::Sustain:
break;
case State::Release:
length = min(remainingSamples, release);
currentValue = ::multiplicativeRamp<Type>(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<Type>(output, currentValue);
if (shouldRelease) {
remainingSamples = static_cast<int>(originalSpan.size());
if (releaseDelay > remainingSamples) {
releaseDelay -= remainingSamples;
return;
}
originalSpan.remove_prefix(releaseDelay);
if (originalSpan.size() > 0)
currentValue = originalSpan.front();
step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1));
remainingSamples -= releaseDelay;
length = min(remainingSamples, release);
currentState = State::Release;
currentValue = ::multiplicativeRamp<Type>(originalSpan, currentValue, step);
originalSpan.remove_prefix(length);
release -= length;
if (release == 0) {
currentValue = 0.0;
currentState = State::Done;
::fill<Type>(originalSpan, 0.0);
}
}
}
template <class Type>
bool ADSREnvelope<Type>::isSmoothing() noexcept
{
return currentState != State::Done;
}
template <class Type>
void ADSREnvelope<Type>::startRelease(int releaseDelay) noexcept
{
shouldRelease = true;
this->releaseDelay = releaseDelay;
}
}