Browse Source

Add value smoother classes, originally from Jean Pierre Cimalando

Signed-off-by: falkTX <falktx@falktx.com>
pull/421/head
falkTX 2 years ago
parent
commit
5b7670584d
1 changed files with 204 additions and 0 deletions
  1. +204
    -0
      distrho/extra/ValueSmoother.hpp

+ 204
- 0
distrho/extra/ValueSmoother.hpp View File

@@ -0,0 +1,204 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
* Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED
#define DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED

#include "../DistrhoUtils.hpp"

START_NAMESPACE_DISTRHO

// --------------------------------------------------------------------------------------------------------------------

/**
* @brief An exponential smoother for control values
*
* This continually smooths a value towards a defined target,
* using a low-pass filter of the 1st order, which creates an exponential curve.
*
* The length of the curve is defined by a T60 constant,
* which is the time it takes for a 1-to-0 smoothing to fall to -60dB.
*
* Note that this smoother has asymptotical behavior,
* and it must not be assumed that the final target is ever reached.
*/
class ExponentialValueSmoother {
float coef;
float target;
float mem;
float tau;
float sampleRate;

public:
ExponentialValueSmoother()
: coef(0.f),
target(0.f),
mem(0.f),
tau(0.f),
sampleRate(0.f) {}

void setSampleRate(const float newSampleRate) noexcept
{
if (d_isNotEqual(sampleRate, newSampleRate))
{
sampleRate = newSampleRate;
updateCoef();
}
}

void setTimeConstant(const float newT60) noexcept
{
const float newTau = newT60 * (float)(1.0 / 6.91);

if (d_isNotEqual(tau, newTau))
{
tau = newTau;
updateCoef();
}
}

float getCurrentValue() const noexcept
{
return mem;
}

float getTargetValue() const noexcept
{
return target;
}

void setTargetValue(const float newTarget) noexcept
{
target = newTarget;
}

void clearToTargetValue() noexcept
{
mem = target;
}

inline float peek() const noexcept
{
return mem * coef + target * (1.f - coef);
}

inline float next() noexcept
{
return (mem = mem * coef + target * (1.f - coef));
}

private:
void updateCoef() noexcept
{
coef = std::exp(-1.f / (tau * sampleRate));
}
};

// --------------------------------------------------------------------------------------------------------------------

/**
* @brief A linear smoother for control values
*
* This continually smooths a value towards a defined target, using linear segments.
*
* The duration of the smoothing segment is defined by the given time constant.
* Every time the target changes, a new segment restarts for the whole duration of the time constant.
*
* Note that this smoother, unlike an exponential smoother, eventually should converge to its target value.
*/
class LinearValueSmoother {
float step;
float target;
float mem;
float tau;
float sampleRate;

public:
LinearValueSmoother()
: step(0.f),
target(0.f),
mem(0.f),
tau(0.f),
sampleRate(0.f) {}

void setSampleRate(const float newSampleRate) noexcept
{
if (d_isNotEqual(sampleRate, newSampleRate))
{
sampleRate = newSampleRate;
updateStep();
}
}

void setTimeConstant(const float newTau) noexcept
{
if (d_isNotEqual(tau, newTau))
{
tau = newTau;
updateStep();
}
}

float getCurrentValue() const noexcept
{
return mem;
}

float getTargetValue() const noexcept
{
return target;
}

void setTargetValue(const float newTarget) noexcept
{
if (d_isNotEqual(target, newTarget))
{
target = newTarget;
updateStep();
}
}

void clearToTargetValue() noexcept
{
mem = target;
}

inline float peek() const noexcept
{
const float dy = target - mem;
return mem + std::copysign(std::fmin(std::abs(dy), std::abs(step)), dy);
}

inline float next() noexcept
{
const float y0 = mem;
const float dy = target - y0;
return (mem = y0 + std::copysign(std::fmin(std::abs(dy), std::abs(step)), dy));
}

private:
void updateStep() noexcept
{
step = (target - mem) / (tau * sampleRate);
}
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED

Loading…
Cancel
Save