DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
Loading...
Searching...
No Matches
Smoothers.h
Go to the documentation of this file.
1// DSPark -- Professional Audio DSP Framework
2// Copyright (c) 2026 Cristian Moresi -- MIT License
3
4#pragma once
5
6#include "DspMath.h"
7
8#include <algorithm>
9#include <array>
10#include <cmath>
11
36namespace dspark {
37namespace Smoothers
38{
39
40namespace Constants
41{
42 static constexpr float pi = dspark::pi<float>;
43 static constexpr float twoPi = dspark::twoPi<float>;
44 static constexpr float sqrt2 = 1.41421356237309504880f;
45} // namespace Constants
46
47//==============================================================================
48
56{
57public:
58 static constexpr float epsilon = 1e-6f;
59
60 void reset(double sampleRate, float rampTimeMilliseconds, float initialValue = 0.0f) noexcept;
61 void setTargetValue(float newTarget) noexcept;
62 float getNextValue() noexcept;
63
64 [[nodiscard]] float getCurrentValue() const noexcept { return current; }
65 [[nodiscard]] float getTargetValue() const noexcept { return target; }
66
67 void setCurrentAndTargetValue(float value) noexcept;
68 [[nodiscard]] bool isSmoothing() const noexcept;
69 void skip() noexcept;
70
77 void processBlock(float* buffer, int numSamples, bool multiply = false) noexcept;
78
79private:
80 float current = 0.0f;
81 float target = 0.0f;
82 float step = 0.0f;
83 int stepsToGo = 0;
84 int totalSteps = 0;
85};
86
101{
102public:
103 static constexpr float epsilon = 1e-10f;
104
105 void reset(double sampleRate, float timeConstantMilliseconds, float initialValue = 1.0f) noexcept;
106 void setTargetValue(float newTarget) noexcept;
107 float getNextValue() noexcept;
108
109 [[nodiscard]] float getCurrentValue() const noexcept { return current; }
110 [[nodiscard]] float getTargetValue() const noexcept { return target; }
111
112 void setCurrentAndTargetValue(float value) noexcept;
113 [[nodiscard]] bool isSmoothing() const noexcept;
114 void skip() noexcept;
115
116private:
117 float current = 1.0f;
118 float target = 1.0f;
119 float coeff = 0.0f;
120 int stepsToGo = 0;
121 int totalSteps = 0;
122};
123
137{
138public:
139 static constexpr float epsilon = 1e-6f;
140
141 void reset(double sampleRate, float timeConstantMilliseconds, float initialValue = 0.0f) noexcept;
142 void setTargetValue(float newTarget) noexcept;
143 float getNextValue() noexcept;
144
145 [[nodiscard]] float getCurrentValue() const noexcept { return static_cast<float>(current); }
146 [[nodiscard]] float getTargetValue() const noexcept { return static_cast<float>(target); }
147 [[nodiscard]] bool isSmoothing() const noexcept;
148 void skip() noexcept;
149
150private:
151 double coeff = 0.0;
152 double current = 0.0;
153 double target = 0.0;
154};
155
160template <std::size_t N>
162{
163public:
164 static_assert(N >= 1, "MultiPoleSmoother needs at least one pole");
165
166 static constexpr float epsilon = 1e-6f;
167
168 void reset(double sampleRate, float timeConstantMilliseconds, float initialValue = 0.0f) noexcept;
169 void setTargetValue(float newTarget) noexcept;
170 float getNextValue() noexcept;
171
172 [[nodiscard]] float getCurrentValue() const noexcept { return poles.back().getCurrentValue(); }
173 [[nodiscard]] float getTargetValue() const noexcept { return poles.front().getTargetValue(); }
174 [[nodiscard]] bool isSmoothing() const noexcept;
175 void skip() noexcept;
176
177private:
178 std::array<OnePoleSmoother, N> poles;
179};
180
189{
190public:
191 static constexpr float epsilon = 1e-6f;
192
193 void reset(double sampleRate, float attackMilliseconds, float releaseMilliseconds, float initialValue = 0.0f) noexcept;
194 void setTargetValue(float newTarget) noexcept;
195 float getNextValue() noexcept;
196
197 [[nodiscard]] float getCurrentValue() const noexcept { return static_cast<float>(current); }
198 [[nodiscard]] float getTargetValue() const noexcept { return static_cast<float>(target); }
199 [[nodiscard]] bool isSmoothing() const noexcept;
200 void skip() noexcept;
201
202private:
203 // Double for the same reason as OnePoleSmoother: float recursion stalls
204 // short of the target and the settle check would never fire.
205 double attackCoeff = 0.0;
206 double releaseCoeff = 0.0;
207 double current = 0.0;
208 double target = 0.0;
209};
210
216{
217public:
218 static constexpr float epsilon = 1e-6f;
219
220 void reset(double sampleRate, float maxRatePerSecond, float initialValue = 0.0f) noexcept;
221 void setTargetValue(float newTarget) noexcept;
222 float getNextValue() noexcept;
223
224 [[nodiscard]] float getCurrentValue() const noexcept { return current; }
225 [[nodiscard]] float getTargetValue() const noexcept { return target; }
226 [[nodiscard]] bool isSmoothing() const noexcept;
227 void skip() noexcept;
228
229private:
230 float maxDelta = 0.0f;
231 float current = 0.0f;
232 float target = 0.0f;
233};
234
245{
246public:
247 static constexpr float epsilon = 1e-6f;
248
249 void reset(double sampleRate, float timeConstantMilliseconds, float q = 0.707f, float initialValue = 0.0f) noexcept;
250 void setTargetValue(float newTarget) noexcept;
251 float getNextValue() noexcept;
252
253 [[nodiscard]] float getCurrentValue() const noexcept { return static_cast<float>(lowpass); }
254 [[nodiscard]] float getTargetValue() const noexcept { return static_cast<float>(target); }
255 [[nodiscard]] bool isSmoothing() const noexcept;
256 void skip() noexcept;
257
258 [[nodiscard]] float getBandPassOutput() const noexcept { return static_cast<float>(bandpass); }
259 [[nodiscard]] float getHighPassOutput() const noexcept { return static_cast<float>(highpass); }
260
261private:
262 double v1 = 0.0;
263 double v2 = 0.0;
264 double g = 0.0;
265 double k = 0.0;
266 double a1 = 0.0;
267 double a2 = 0.0;
268 double a3 = 0.0;
269 double target = 0.0;
270 double lowpass = 0.0;
271 double bandpass = 0.0;
272 double highpass = 0.0;
273};
274
286{
287public:
288 static constexpr float epsilon = 1e-6f;
289
290 void reset(double sampleRate, float timeConstantMilliseconds, float initialValue = 0.0f) noexcept;
291 void setTargetValue(float newTarget) noexcept;
292 float getNextValue() noexcept;
293
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;
298
299private:
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;
303 double target = 0.0;
304 double lastOut = 0.0;
305 double prevOut = 0.0;
306 double velEps = 0.0;
307};
308
314{
315public:
316 void reset(double sampleRate, float timeConstantMilliseconds, float initialValue = 0.0f) noexcept;
317};
318
319} // namespace Smoothers
320
321//==============================================================================
322// Inline definitions
323//==============================================================================
324
325// --- LinearSmoother ---
326
327inline void Smoothers::LinearSmoother::reset(double sampleRate, float rampTimeMilliseconds, float initialValue) noexcept
328{
329 totalSteps = static_cast<int>(sampleRate * rampTimeMilliseconds / 1000.0);
330 stepsToGo = 0;
331 step = 0.0f;
332 current = initialValue;
333 target = initialValue;
334}
335
336inline void Smoothers::LinearSmoother::setTargetValue(float newTarget) noexcept
337{
338 if (newTarget == target) return;
339 target = newTarget;
340 stepsToGo = totalSteps;
341 if (stepsToGo > 0)
342 step = (target - current) / static_cast<float>(stepsToGo);
343 else
344 current = target;
345}
346
348{
349 if (stepsToGo <= 0) return current;
350 current += step;
351 --stepsToGo;
352 if (stepsToGo == 0) current = target;
353 return current;
354}
355
357{
358 current = value;
359 target = value;
360 step = 0.0f;
361 stepsToGo = 0;
362}
363
364inline bool Smoothers::LinearSmoother::isSmoothing() const noexcept
365{
366 return std::abs(current - target) > epsilon;
367}
368
370{
372}
373
374inline void Smoothers::LinearSmoother::processBlock(float* buffer, int numSamples, bool multiply) noexcept
375{
376 // Split the ramping section from the settled remainder so each loop is
377 // trivially auto-vectorizable (the compiler unswitches the loop-invariant
378 // multiply flag out of both bodies).
379 int stepsToProcess = std::min(numSamples, stepsToGo);
380 int i = 0;
381
382 // Ramping section
383 for (; i < stepsToProcess; ++i)
384 {
385 current += step;
386 buffer[i] = multiply ? buffer[i] * current : buffer[i] + current;
387 }
388
389 stepsToGo -= stepsToProcess;
390 if (stepsToGo == 0) current = target;
391
392 // Settled section
393 for (; i < numSamples; ++i)
394 {
395 buffer[i] = multiply ? buffer[i] * target : buffer[i] + target;
396 }
397}
398
399// --- ExponentialSmoother ---
400
401inline void Smoothers::ExponentialSmoother::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
402{
403 totalSteps = static_cast<int>(sampleRate * timeConstantMilliseconds / 1000.0);
404 stepsToGo = 0;
405 coeff = 1.0f;
406 current = initialValue;
407 target = initialValue;
408}
409
410inline void Smoothers::ExponentialSmoother::setTargetValue(float newTarget) noexcept
411{
412 if (std::abs(newTarget) < epsilon)
413 newTarget = (newTarget < 0.0f) ? -epsilon : epsilon;
414
415 if (newTarget == target) return;
416 target = newTarget;
417 stepsToGo = totalSteps;
418
419 if (stepsToGo > 0 && std::abs(current) > epsilon)
420 {
421 float safeCur = (current > 0.0f) ? std::max(current, epsilon)
422 : std::min(current, -epsilon);
423 float ratio = target / safeCur;
424 if (ratio > 0.0f)
425 coeff = std::exp(std::log(ratio) / static_cast<float>(stepsToGo));
426 else
427 current = target;
428 }
429 else
430 current = target;
431}
432
434{
435 if (stepsToGo <= 0) return current;
436 current *= coeff;
437 --stepsToGo;
438 if (stepsToGo == 0) current = target;
439 return current;
440}
441
443{
444 current = value;
445 target = value;
446 coeff = 1.0f;
447 stepsToGo = 0;
448}
449
451{
452 return std::abs(current - target) > epsilon;
453}
454
456{
458}
459
460// --- OnePoleSmoother ---
461
462inline void Smoothers::OnePoleSmoother::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
463{
464 const double timeConstantSeconds = static_cast<double>(timeConstantMilliseconds) / 1000.0;
465 const double tau = sampleRate * timeConstantSeconds;
466 coeff = tau > 0.0 ? std::exp(-1.0 / tau) : 0.0;
467 current = initialValue;
468 target = initialValue;
469}
470
471inline void Smoothers::OnePoleSmoother::setTargetValue(float newTarget) noexcept
472{
473 target = newTarget;
474}
475
477{
478 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
479 current = target + coeff * (current - target);
480 return static_cast<float>(current);
481}
482
483inline bool Smoothers::OnePoleSmoother::isSmoothing() const noexcept
484{
485 return std::abs(current - target) > epsilon;
486}
487
489{
490 current = target;
491}
492
493// --- MultiPoleSmoother ---
494
495template <std::size_t N>
496inline void Smoothers::MultiPoleSmoother<N>::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
497{
498 for (auto& pole : poles)
499 pole.reset(sampleRate, timeConstantMilliseconds, initialValue);
500}
501
502template <std::size_t N>
503inline void Smoothers::MultiPoleSmoother<N>::setTargetValue(float newTarget) noexcept
504{
505 poles[0].setTargetValue(newTarget);
506}
507
508template <std::size_t N>
510{
511 float val = poles[0].getNextValue();
512 for (std::size_t i = 1; i < N; ++i)
513 {
514 poles[i].setTargetValue(val);
515 val = poles[i].getNextValue();
516 }
517 return val;
518}
519
520template <std::size_t N>
522{
523 // Compare the chain OUTPUT against the GLOBAL target (pole 0's target).
524 // Asking the last pole alone is wrong on both ends: right after
525 // setTargetValue() only pole 0 knows the new target (the last pole would
526 // report "settled" before the ramp even starts), and mid-ramp each pole
527 // sits close to its neighbour while the whole chain is far from the goal.
528 return std::abs(poles.back().getCurrentValue()
529 - poles.front().getTargetValue()) > epsilon;
530}
531
532template <std::size_t N>
534{
535 // Propagate the global target down the chain first: inner poles still
536 // chase the PREVIOUS output of their predecessor, so skipping them in
537 // place would freeze the chain mid-way instead of landing on the target.
538 const float t = poles.front().getTargetValue();
539 for (auto& pole : poles)
540 {
541 pole.setTargetValue(t);
542 pole.skip();
543 }
544}
545
546// --- AsymmetricSmoother ---
547
548inline void Smoothers::AsymmetricSmoother::reset(double sampleRate, float attackMilliseconds, float releaseMilliseconds, float initialValue) noexcept
549{
550 const double attackSeconds = static_cast<double>(attackMilliseconds) / 1000.0;
551 const double releaseSeconds = static_cast<double>(releaseMilliseconds) / 1000.0;
552 attackCoeff = attackSeconds > 0.0 ? std::exp(-1.0 / (sampleRate * attackSeconds)) : 0.0;
553 releaseCoeff = releaseSeconds > 0.0 ? std::exp(-1.0 / (sampleRate * releaseSeconds)) : 0.0;
554 current = initialValue;
555 target = initialValue;
556}
557
558inline void Smoothers::AsymmetricSmoother::setTargetValue(float newTarget) noexcept
559{
560 target = newTarget;
561}
562
564{
565 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
566 const double c = (target > current) ? attackCoeff : releaseCoeff;
567 current = target + c * (current - target);
568 return static_cast<float>(current);
569}
570
572{
573 return std::abs(current - target) > epsilon;
574}
575
577{
578 current = target;
579}
580
581// --- SlewLimiter ---
582
583inline void Smoothers::SlewLimiter::reset(double sampleRate, float maxRatePerSecond, float initialValue) noexcept
584{
585 // Clamp at zero: a negative rate would flip the clamp bounds below
586 // (std::clamp with lo > hi is undefined behaviour). Zero legitimately
587 // means "frozen": the value is not allowed to move at all.
588 maxDelta = std::max(0.0f, maxRatePerSecond / static_cast<float>(sampleRate));
589 current = initialValue;
590 target = initialValue;
591}
592
593inline void Smoothers::SlewLimiter::setTargetValue(float newTarget) noexcept
594{
595 target = newTarget;
596}
597
599{
600 float delta = target - current;
601 delta = std::clamp(delta, -maxDelta, maxDelta);
602 current += delta;
603 return current;
604}
605
606inline bool Smoothers::SlewLimiter::isSmoothing() const noexcept
607{
608 return std::abs(current - target) > epsilon;
609}
610
611inline void Smoothers::SlewLimiter::skip() noexcept
612{
613 current = target;
614}
615
616// --- StateVariableSmoother ---
617
618inline void Smoothers::StateVariableSmoother::reset(double sampleRate, float timeConstantMilliseconds, float q, float initialValue) noexcept
619{
620 const double timeConstantSeconds = static_cast<double>(timeConstantMilliseconds) / 1000.0;
621 const double fs = sampleRate;
622 // A zero time constant means "instantaneous", not "frozen": map it to the
623 // fastest usable cutoff (fc = 0 would zero the coefficients and the
624 // output would never move again). Clamp below Nyquist so tan(pi*fc/fs)
625 // stays finite/stable for very small time constants (otherwise the TPT
626 // prewarp blows up near fs/2).
627 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
628 fc = std::min(fc, fs * 0.49);
629
630 g = std::tan(dspark::pi<double> * fc / fs);
631 k = 1.0 / static_cast<double>(std::max(q, 0.01f)); // TPT damping uses k = 1/Q.
632
633 a1 = 1.0 / (1.0 + g * (g + k));
634 a2 = g * a1;
635 a3 = g * a2;
636
637 v1 = 0.0;
638 v2 = initialValue;
639 target = initialValue;
640 lowpass = initialValue;
641 bandpass = 0.0;
642 highpass = 0.0;
643}
644
645inline void Smoothers::StateVariableSmoother::setTargetValue(float newTarget) noexcept
646{
647 target = newTarget;
648}
649
651{
652 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
653
654 const double v0 = target;
655 const double v3 = v0 - v2;
656 const double v1_new = a1 * v1 + a2 * v3;
657 const double v2_new = v2 + a2 * v1 + a3 * v3;
658 v1 = 2.0 * v1_new - v1;
659 v2 = 2.0 * v2_new - v2;
660
661 lowpass = v2_new;
662 bandpass = v1_new;
663 highpass = v0 - k * v1_new - v2_new;
664
665 return static_cast<float>(lowpass);
666}
667
669{
670 // The bandpass term is the filter's velocity. Without it, a Q > 0.5
671 // response crossing the target (overshoot) can momentarily satisfy
672 // |lowpass - target| < epsilon at full speed, and the anti-denormal
673 // check in getNextValue() would snap mid-flight, truncating the
674 // trajectory with a derivative kink.
675 return std::abs(lowpass - target) > epsilon || std::abs(bandpass) > epsilon;
676}
677
679{
680 v1 = 0.0;
681 v2 = target;
682 lowpass = target;
683 bandpass = 0.0;
684 highpass = 0.0;
685}
686
687// --- ButterworthSmoother ---
688
689inline void Smoothers::ButterworthSmoother::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
690{
691 constexpr double kSqrt2 = 1.4142135623730950488;
692 const double timeConstantSeconds = static_cast<double>(timeConstantMilliseconds) / 1000.0;
693 const double fs = sampleRate;
694 // Zero time constant maps to the fastest usable cutoff (see the SVF
695 // smoother): fc = 0 would freeze the filter short of the target forever.
696 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
697 fc = std::min(fc, fs * 0.49); // keep below Nyquist for a stable bilinear prewarp
698 const double tanw = std::tan(dspark::pi<double> * fc / fs);
699 const double tanw2 = tanw * tanw;
700
701 const double denom = 1.0 + kSqrt2 * tanw + tanw2;
702
703 b0 = tanw2 / denom;
704 b1 = 2.0 * tanw2 / denom;
705 b2 = tanw2 / denom;
706
707 a1 = 2.0 * (tanw2 - 1.0) / denom;
708 a2 = (1.0 - kSqrt2 * tanw + tanw2) / denom;
709
710 // The motion threshold for the settle check: the peak per-sample velocity
711 // of a residual oscillation of amplitude epsilon at fc. Near the target
712 // crossing of the overshoot the output moves fast in filter time but by
713 // tiny per-sample amounts (proportional to fc/fs), so comparing the
714 // one-sample difference against the plain epsilon would still snap
715 // mid-flight for small targets.
716 velEps = static_cast<double>(epsilon) * (static_cast<double>(Constants::twoPi) * fc / fs);
717
718 // TDF-II steady state for a constant input/output v requires the s2 term
719 // inside s1: s2* = (b2 - a2)*v and s1* = (b1 - a1)*v + s2*. Without the
720 // + s2 term the first sample of every new ramp overshot to ~2x the value
721 // (an audible click, the opposite of a smoother's job).
722 s2 = (b2 - a2) * initialValue;
723 s1 = (b1 - a1) * initialValue + s2;
724 target = initialValue;
725 lastOut = initialValue;
726 prevOut = initialValue;
727}
728
729inline void Smoothers::ButterworthSmoother::setTargetValue(float newTarget) noexcept
730{
731 target = newTarget;
732}
733
735{
736 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
737
738 const double x0 = target;
739 const double y0 = b0 * x0 + s1;
740 s1 = b1 * x0 - a1 * y0 + s2;
741 s2 = b2 * x0 - a2 * y0;
742
743 prevOut = lastOut;
744 lastOut = y0;
745 return static_cast<float>(y0);
746}
747
749{
750 // Error AND motion: a Butterworth response (Q = 0.707) overshoots, so
751 // the output crosses the target at full speed. Judging by the error
752 // alone, the anti-denormal check in getNextValue() would snap exactly
753 // at that crossing and truncate the remaining trajectory. Motion is
754 // measured against velEps (velocity of an epsilon-amplitude residual),
755 // the same role the bandpass term plays in the SVF smoother.
756 return std::abs(lastOut - target) > epsilon
757 || std::abs(lastOut - prevOut) > velEps;
758}
759
761{
762 // Same steady-state form as reset(): s1 must include the settled s2.
763 s2 = (b2 - a2) * target;
764 s1 = (b1 - a1) * target + s2;
765 lastOut = target;
766 prevOut = target;
767}
768
769// --- CriticallyDampedSmoother ---
770
771inline void Smoothers::CriticallyDampedSmoother::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
772{
773 // Critical damping (zeta = 1) corresponds exactly to Q = 0.5, not to the
774 // Butterworth Q = 0.707.
775 StateVariableSmoother::reset(sampleRate, timeConstantMilliseconds, 0.5f, initialValue);
776}
777
778} // namespace dspark
Core mathematical utilities for digital signal processing.
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...).
Definition DspMath.h:48
constexpr T pi
Pi (3.14159...) for the given floating-point type.
Definition DspMath.h:36
constexpr T twoPi
2 * Pi (6.28318...).
Definition DspMath.h:39
Smoother with asymmetric attack/release times.
Definition Smoothers.h:189
void reset(double sampleRate, float attackMilliseconds, float releaseMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:548
bool isSmoothing() const noexcept
Definition Smoothers.h:571
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:558
float getTargetValue() const noexcept
Definition Smoothers.h:198
Butterworth low-pass smoother for maximally flat response.
Definition Smoothers.h:286
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:729
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:689
float getTargetValue() const noexcept
Definition Smoothers.h:295
bool isSmoothing() const noexcept
Definition Smoothers.h:748
Critically damped smoother (no overshoot, exact Q=0.5).
Definition Smoothers.h:314
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:771
Exponential (multiplicative) smoother for natural, perceptual responses.
Definition Smoothers.h:101
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:410
bool isSmoothing() const noexcept
Definition Smoothers.h:450
void setCurrentAndTargetValue(float value) noexcept
Definition Smoothers.h:442
float getTargetValue() const noexcept
Definition Smoothers.h:110
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=1.0f) noexcept
Definition Smoothers.h:401
Linear ramp smoother for predictable, uniform interpolation.
Definition Smoothers.h:56
void setCurrentAndTargetValue(float value) noexcept
Definition Smoothers.h:356
void processBlock(float *buffer, int numSamples, bool multiply=false) noexcept
Block processing for SIMD optimization.
Definition Smoothers.h:374
void reset(double sampleRate, float rampTimeMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:327
float getTargetValue() const noexcept
Definition Smoothers.h:65
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:336
static constexpr float epsilon
Definition Smoothers.h:58
bool isSmoothing() const noexcept
Definition Smoothers.h:364
float getCurrentValue() const noexcept
Definition Smoothers.h:64
Templated cascaded multi-pole smoother for steeper roll-off.
Definition Smoothers.h:162
bool isSmoothing() const noexcept
Definition Smoothers.h:521
float getTargetValue() const noexcept
Definition Smoothers.h:173
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:503
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:496
Authentic one-pole exponential IIR low-pass smoother.
Definition Smoothers.h:137
float getTargetValue() const noexcept
Definition Smoothers.h:146
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:462
bool isSmoothing() const noexcept
Definition Smoothers.h:483
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:471
Rate limiter to cap maximum change per sample.
Definition Smoothers.h:216
void reset(double sampleRate, float maxRatePerSecond, float initialValue=0.0f) noexcept
Definition Smoothers.h:583
float getTargetValue() const noexcept
Definition Smoothers.h:225
float getNextValue() noexcept
Definition Smoothers.h:598
bool isSmoothing() const noexcept
Definition Smoothers.h:606
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:593
Second-order state variable filter (SVF) smoother (TPT implementation).
Definition Smoothers.h:245
void reset(double sampleRate, float timeConstantMilliseconds, float q=0.707f, float initialValue=0.0f) noexcept
Definition Smoothers.h:618
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:645
float getTargetValue() const noexcept
Definition Smoothers.h:254
float getHighPassOutput() const noexcept
Definition Smoothers.h:259