42 static constexpr float pi = dspark::pi<float>;
43 static constexpr float twoPi = dspark::twoPi<float>;
44 static constexpr float sqrt2 = 1.41421356237309504880f;
60 void reset(
double sampleRate,
float rampTimeMilliseconds,
float initialValue = 0.0f) noexcept;
77 void processBlock(
float* buffer,
int numSamples,
bool multiply = false) noexcept;
105 void reset(
double sampleRate,
float timeConstantMilliseconds,
float initialValue = 1.0f) noexcept;
114 void skip() noexcept;
117 float current = 1.0f;
141 void reset(
double sampleRate,
float timeConstantMilliseconds,
float initialValue = 0.0f) noexcept;
145 [[nodiscard]]
float getCurrentValue() const noexcept {
return static_cast<float>(current); }
146 [[nodiscard]]
float getTargetValue() const noexcept {
return static_cast<float>(target); }
148 void skip() noexcept;
152 double current = 0.0;
160template <std::
size_t N>
164 static_assert(N >= 1,
"MultiPoleSmoother needs at least one pole");
168 void reset(
double sampleRate,
float timeConstantMilliseconds,
float initialValue = 0.0f) noexcept;
172 [[nodiscard]]
float getCurrentValue() const noexcept {
return poles.back().getCurrentValue(); }
173 [[nodiscard]]
float getTargetValue() const noexcept {
return poles.front().getTargetValue(); }
193 void reset(
double sampleRate,
float attackMilliseconds,
float releaseMilliseconds,
float initialValue = 0.0f) noexcept;
197 [[nodiscard]]
float getCurrentValue() const noexcept {
return static_cast<float>(current); }
198 [[nodiscard]]
float getTargetValue() const noexcept {
return static_cast<float>(target); }
200 void skip() noexcept;
205 double attackCoeff = 0.0;
206 double releaseCoeff = 0.0;
207 double current = 0.0;
220 void reset(
double sampleRate,
float maxRatePerSecond,
float initialValue = 0.0f) noexcept;
227 void skip() noexcept;
230 float maxDelta = 0.0f;
231 float current = 0.0f;
249 void reset(
double sampleRate,
float timeConstantMilliseconds,
float q = 0.707f,
float initialValue = 0.0f) noexcept;
253 [[nodiscard]]
float getCurrentValue() const noexcept {
return static_cast<float>(lowpass); }
254 [[nodiscard]]
float getTargetValue() const noexcept {
return static_cast<float>(target); }
256 void skip() noexcept;
258 [[nodiscard]]
float getBandPassOutput() const noexcept {
return static_cast<float>(bandpass); }
270 double lowpass = 0.0;
271 double bandpass = 0.0;
272 double highpass = 0.0;
288 static constexpr float epsilon = 1e-6f;
290 void reset(
double sampleRate,
float timeConstantMilliseconds,
float initialValue = 0.0f) noexcept;
291 void setTargetValue(
float newTarget) noexcept;
292 float getNextValue() noexcept;
294 [[nodiscard]]
float getCurrentValue() const noexcept {
return static_cast<float>(lastOut); }
295 [[nodiscard]]
float getTargetValue() const noexcept {
return static_cast<float>(target); }
296 [[nodiscard]]
bool isSmoothing() const noexcept;
297 void skip() noexcept;
300 double b0 = 0.0, b1 = 0.0, b2 = 0.0;
301 double a1 = 0.0, a2 = 0.0;
302 double s1 = 0.0, s2 = 0.0;
304 double lastOut = 0.0;
305 double prevOut = 0.0;
316 void reset(
double sampleRate,
float timeConstantMilliseconds,
float initialValue = 0.0f)
noexcept;
329 totalSteps =
static_cast<int>(sampleRate * rampTimeMilliseconds / 1000.0);
332 current = initialValue;
333 target = initialValue;
338 if (newTarget == target)
return;
340 stepsToGo = totalSteps;
342 step = (target - current) /
static_cast<float>(stepsToGo);
349 if (stepsToGo <= 0)
return current;
352 if (stepsToGo == 0) current = target;
366 return std::abs(current - target) >
epsilon;
379 int stepsToProcess = std::min(numSamples, stepsToGo);
383 for (; i < stepsToProcess; ++i)
386 buffer[i] = multiply ? buffer[i] * current : buffer[i] + current;
389 stepsToGo -= stepsToProcess;
390 if (stepsToGo == 0) current = target;
393 for (; i < numSamples; ++i)
395 buffer[i] = multiply ? buffer[i] * target : buffer[i] + target;
403 totalSteps =
static_cast<int>(sampleRate * timeConstantMilliseconds / 1000.0);
406 current = initialValue;
407 target = initialValue;
412 if (std::abs(newTarget) <
epsilon)
415 if (newTarget == target)
return;
418 if (totalSteps > 0 && std::abs(current) >
epsilon)
420 float safeCur = (current > 0.0f) ? std::max(current,
epsilon)
422 float ratio = target / safeCur;
425 stepsToGo = totalSteps;
426 coeff = std::exp(std::log(ratio) /
static_cast<float>(stepsToGo));
442 if (stepsToGo <= 0)
return current;
445 if (stepsToGo == 0) current = target;
459 return std::abs(current - target) >
epsilon;
471 const double timeConstantSeconds =
static_cast<double>(timeConstantMilliseconds) / 1000.0;
472 const double tau = sampleRate * timeConstantSeconds;
473 coeff = tau > 0.0 ? std::exp(-1.0 / tau) : 0.0;
474 current = initialValue;
475 target = initialValue;
486 current = target + coeff * (current - target);
487 return static_cast<float>(current);
492 return std::abs(current - target) >
epsilon;
502template <std::
size_t N>
505 for (
auto& pole : poles)
506 pole.reset(sampleRate, timeConstantMilliseconds, initialValue);
509template <std::
size_t N>
512 poles[0].setTargetValue(newTarget);
515template <std::
size_t N>
518 float val = poles[0].getNextValue();
519 for (std::size_t i = 1; i < N; ++i)
521 poles[i].setTargetValue(val);
522 val = poles[i].getNextValue();
527template <std::
size_t N>
535 return std::abs(poles.back().getCurrentValue()
536 - poles.front().getTargetValue()) >
epsilon;
539template <std::
size_t N>
545 const float t = poles.front().getTargetValue();
546 for (
auto& pole : poles)
548 pole.setTargetValue(t);
557 const double attackSeconds =
static_cast<double>(attackMilliseconds) / 1000.0;
558 const double releaseSeconds =
static_cast<double>(releaseMilliseconds) / 1000.0;
559 attackCoeff = attackSeconds > 0.0 ? std::exp(-1.0 / (sampleRate * attackSeconds)) : 0.0;
560 releaseCoeff = releaseSeconds > 0.0 ? std::exp(-1.0 / (sampleRate * releaseSeconds)) : 0.0;
561 current = initialValue;
562 target = initialValue;
573 const double c = (target > current) ? attackCoeff : releaseCoeff;
574 current = target + c * (current - target);
575 return static_cast<float>(current);
580 return std::abs(current - target) >
epsilon;
595 maxDelta = std::max(0.0f, maxRatePerSecond /
static_cast<float>(sampleRate));
596 current = initialValue;
597 target = initialValue;
607 float delta = target - current;
608 delta = std::clamp(delta, -maxDelta, maxDelta);
615 return std::abs(current - target) >
epsilon;
627 const double timeConstantSeconds =
static_cast<double>(timeConstantMilliseconds) / 1000.0;
628 const double fs = sampleRate;
634 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (
static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
635 fc = std::min(fc, fs * 0.49);
637 g = std::tan(dspark::pi<double> * fc / fs);
638 k = 1.0 /
static_cast<double>(std::max(q, 0.01f));
640 a1 = 1.0 / (1.0 + g * (g + k));
646 target = initialValue;
647 lowpass = initialValue;
661 const double v0 = target;
662 const double v3 = v0 - v2;
663 const double v1_new = a1 * v1 + a2 * v3;
664 const double v2_new = v2 + a2 * v1 + a3 * v3;
665 v1 = 2.0 * v1_new - v1;
666 v2 = 2.0 * v2_new - v2;
670 highpass = v0 - k * v1_new - v2_new;
672 return static_cast<float>(lowpass);
682 return std::abs(lowpass - target) >
epsilon || std::abs(bandpass) >
epsilon;
698 constexpr double kSqrt2 = 1.4142135623730950488;
699 const double timeConstantSeconds =
static_cast<double>(timeConstantMilliseconds) / 1000.0;
700 const double fs = sampleRate;
703 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (
static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
704 fc = std::min(fc, fs * 0.49);
705 const double tanw = std::tan(dspark::pi<double> * fc / fs);
706 const double tanw2 = tanw * tanw;
708 const double denom = 1.0 + kSqrt2 * tanw + tanw2;
711 b1 = 2.0 * tanw2 / denom;
714 a1 = 2.0 * (tanw2 - 1.0) / denom;
715 a2 = (1.0 - kSqrt2 * tanw + tanw2) / denom;
723 velEps =
static_cast<double>(
epsilon) * (
static_cast<double>(Constants::twoPi) * fc / fs);
729 s2 = (b2 - a2) * initialValue;
730 s1 = (b1 - a1) * initialValue + s2;
731 target = initialValue;
732 lastOut = initialValue;
733 prevOut = initialValue;
745 const double x0 = target;
746 const double y0 = b0 * x0 + s1;
747 s1 = b1 * x0 - a1 * y0 + s2;
748 s2 = b2 * x0 - a2 * y0;
752 return static_cast<float>(y0);
763 return std::abs(lastOut - target) >
epsilon
764 || std::abs(lastOut - prevOut) > velEps;
770 s2 = (b2 - a2) * target;
771 s1 = (b1 - a1) * target + s2;
A collection of real-time safe smoothing filters for parameter interpolation in audio.
Main namespace for the DSPark framework.
constexpr T sqrt2
Square root of 2 (1.41421...).
constexpr T pi
Pi (3.14159...) for the given floating-point type.
constexpr T twoPi
2 * Pi (6.28318...).
Smoother with asymmetric attack/release times.
void reset(double sampleRate, float attackMilliseconds, float releaseMilliseconds, float initialValue=0.0f) noexcept
bool isSmoothing() const noexcept
void setTargetValue(float newTarget) noexcept
float getNextValue() noexcept
float getTargetValue() const noexcept
Butterworth low-pass smoother for maximally flat response.
float getNextValue() noexcept
void setTargetValue(float newTarget) noexcept
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
float getTargetValue() const noexcept
bool isSmoothing() const noexcept
Critically damped smoother (no overshoot, exact Q=0.5).
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Exponential (multiplicative) smoother for natural, perceptual responses.
void setTargetValue(float newTarget) noexcept
bool isSmoothing() const noexcept
void setCurrentAndTargetValue(float value) noexcept
float getTargetValue() const noexcept
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=1.0f) noexcept
float getNextValue() noexcept
Linear ramp smoother for predictable, uniform interpolation.
void setCurrentAndTargetValue(float value) noexcept
void processBlock(float *buffer, int numSamples, bool multiply=false) noexcept
Block processing for SIMD optimization.
void reset(double sampleRate, float rampTimeMilliseconds, float initialValue=0.0f) noexcept
float getTargetValue() const noexcept
float getNextValue() noexcept
void setTargetValue(float newTarget) noexcept
static constexpr float epsilon
bool isSmoothing() const noexcept
float getCurrentValue() const noexcept
Templated cascaded multi-pole smoother for steeper roll-off.
bool isSmoothing() const noexcept
float getNextValue() noexcept
float getTargetValue() const noexcept
void setTargetValue(float newTarget) noexcept
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Authentic one-pole exponential IIR low-pass smoother.
float getTargetValue() const noexcept
float getNextValue() noexcept
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
bool isSmoothing() const noexcept
void setTargetValue(float newTarget) noexcept
Rate limiter to cap maximum change per sample.
void reset(double sampleRate, float maxRatePerSecond, float initialValue=0.0f) noexcept
float getTargetValue() const noexcept
float getNextValue() noexcept
bool isSmoothing() const noexcept
void setTargetValue(float newTarget) noexcept
Second-order state variable filter (SVF) smoother (TPT implementation).
void reset(double sampleRate, float timeConstantMilliseconds, float q=0.707f, float initialValue=0.0f) noexcept
void setTargetValue(float newTarget) noexcept
float getNextValue() noexcept
float getTargetValue() const noexcept
bool isSmoothing() const noexcept
float getHighPassOutput() const noexcept