Skip to content

Instantly share code, notes, and snippets.

@fefanto
Last active February 3, 2021 08:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fefanto/3057474c020c2cdfab5c462c50b1cc48 to your computer and use it in GitHub Desktop.
Save fefanto/3057474c020c2cdfab5c462c50b1cc48 to your computer and use it in GitHub Desktop.
Utility class based on juce::LinearSmoothedValue for exponential smoothing of parameters
/*
==============================================================================
Utility class for exponentially smoothed values like volume etc. that should
not change abruptly to avoid audio glitches.
This Class is based on juce::LinearSmoothedValue,
part of the JUCE Library (https://github.com/julianstorer/JUCE).
Smoothing algorithm is based on a 1 pole IIR lowpass:
y(n) = A0 * x[n] + B1 * y[n-1]
Coefficient calculation is based on Csound's one pole lowpass:
http://www.csounds.com/manual/html/tone.html ,
with one additional optimization: the A0 * x[n] part is computed at control rate (setNextValue)
saving one multiplication from the audio rate method (getNextValue)
NOTE:
Currently no smoothing optimization is peformed, i.e. filter calculation is always performed.
Maybe no optimization is needed since getNextValue is pretty light (one add + one multiply +
one assign) and comparable with its linear counterpart.
USAGE:
To use this class
(1) place this header file alongside
<JUCE_folder>/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h
(2) add the include reference:
#include "effects/juce_LowPassSmoothedValue.h"
in <JUCE_folder>/modules/juce_audio_basics/juce_audio_basics.h
==============================================================================
*/
#ifndef JUCE_LOWPASSSMOOTHEDVALUE_H_INCLUDED
#define JUCE_LOWPASSSMOOTHEDVALUE_H_INCLUDED
//==============================================================================
template <typename FloatType>
class LowpassSmoothedValue
{
public:
/** Constructor.
If no reset(sampleRate,time) is called, initial behaviour is similar to LinearSmoothedValue construction,
i.e. with A0=1 and B1=0, currentValue goes to targetValue right when you call setTarget.
*/
LowpassSmoothedValue() noexcept
: currentValue (0), target (0), A0 (1), B1 (0), xv(0)
{
}
/** Constructor.
Same behaviour as constructor above.
*/
LowpassSmoothedValue (FloatType initialValue) noexcept
: currentValue (0), target (initialValue), A0 (1), B1 (0), xv(0)
{
}
//==============================================================================
/** Reset to a new sample rate and time constant length. */
void reset (double sampleRate, double filterTimeInSeconds) noexcept
{
// sanity check: 0.00005 ms = 20kHz cutoff ≈ 99% of target in double of this time ≈ 4 samples at 44100
// if you use this setting you won't get a divbyzero below, but you may get the clicks you were trying to avoid.
jassert (sampleRate > 0 && filterTimeInSeconds >= 0.00005);
double lowpassCutoffFreq = 1/filterTimeInSeconds;
double cosf = 2.0 - cos (2* double_Pi * (lowpassCutoffFreq / 2.0) / sampleRate);
double cb1 = cosf - sqrt(cosf * cosf - 1.0);
B1 = (FloatType)cb1;
A0 = (FloatType)(1.0 - cb1);
setValue (target);
}
/** Set a new target value. */
void setValue (FloatType newValue) noexcept
{
target = newValue;
xv = A0 * newValue; // set the "A0" part of the IIR filter output at "Control rate".
}
/** Compute the next value. */
FloatType getNextValue() noexcept
{
return currentValue = xv + B1 * currentValue; // Audio rate
}
/** Do not Compute the next value. */
FloatType getCurrentValue() noexcept
{
return currentValue;
}
/** Returns true if the current value is currently being interpolated. */
bool isSmoothing() const noexcept
{
return true; // no smoothing optimization - TBD set a normalized threshold [value-target] under which we stop smoothing
}
/** Returns the target value towards which the smoothed value is currently moving. */
FloatType getTargetValue() const noexcept
{
return target;
}
private:
FloatType A0;
FloatType B1;
FloatType xv;
FloatType currentValue, target;
};
#endif // JUCE_LOWPASSSMOOTHEDVALUE_H_INCLUDED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment