DSPark 1.8.0
Header-only C++20 DSP for real-time and offline audio
Loading...
Searching...
No Matches
Smoothers.h
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
418 if (totalSteps > 0 && std::abs(current) > epsilon)
419 {
420 float safeCur = (current > 0.0f) ? std::max(current, epsilon)
421 : std::min(current, -epsilon);
422 float ratio = target / safeCur;
423 if (ratio > 0.0f)
424 {
425 stepsToGo = totalSteps;
426 coeff = std::exp(std::log(ratio) / static_cast<float>(stepsToGo));
427 return;
428 }
429 }
430
431 // Snap. The ramp must also stop here: leaving stepsToGo armed replays the
432 // previous ramp's coefficient, dragging the value away from the target
433 // for a whole ramp and then jumping back (a sign flip after a 1 -> 2 ramp
434 // went -1 -> -1.997 -> -1).
435 current = target;
436 coeff = 1.0f;
437 stepsToGo = 0;
438}
439
441{
442 if (stepsToGo <= 0) return current;
443 current *= coeff;
444 --stepsToGo;
445 if (stepsToGo == 0) current = target;
446 return current;
447}
448
450{
451 current = value;
452 target = value;
453 coeff = 1.0f;
454 stepsToGo = 0;
455}
456
458{
459 return std::abs(current - target) > epsilon;
460}
461
463{
465}
466
467// --- OnePoleSmoother ---
468
469inline void Smoothers::OnePoleSmoother::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
470{
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;
476}
477
478inline void Smoothers::OnePoleSmoother::setTargetValue(float newTarget) noexcept
479{
480 target = newTarget;
481}
482
484{
485 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
486 current = target + coeff * (current - target);
487 return static_cast<float>(current);
488}
489
490inline bool Smoothers::OnePoleSmoother::isSmoothing() const noexcept
491{
492 return std::abs(current - target) > epsilon;
493}
494
496{
497 current = target;
498}
499
500// --- MultiPoleSmoother ---
501
502template <std::size_t N>
503inline void Smoothers::MultiPoleSmoother<N>::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
504{
505 for (auto& pole : poles)
506 pole.reset(sampleRate, timeConstantMilliseconds, initialValue);
507}
508
509template <std::size_t N>
510inline void Smoothers::MultiPoleSmoother<N>::setTargetValue(float newTarget) noexcept
511{
512 poles[0].setTargetValue(newTarget);
513}
514
515template <std::size_t N>
517{
518 float val = poles[0].getNextValue();
519 for (std::size_t i = 1; i < N; ++i)
520 {
521 poles[i].setTargetValue(val);
522 val = poles[i].getNextValue();
523 }
524 return val;
525}
526
527template <std::size_t N>
529{
530 // Compare the chain OUTPUT against the GLOBAL target (pole 0's target).
531 // Asking the last pole alone is wrong on both ends: right after
532 // setTargetValue() only pole 0 knows the new target (the last pole would
533 // report "settled" before the ramp even starts), and mid-ramp each pole
534 // sits close to its neighbour while the whole chain is far from the goal.
535 return std::abs(poles.back().getCurrentValue()
536 - poles.front().getTargetValue()) > epsilon;
537}
538
539template <std::size_t N>
541{
542 // Propagate the global target down the chain first: inner poles still
543 // chase the PREVIOUS output of their predecessor, so skipping them in
544 // place would freeze the chain mid-way instead of landing on the target.
545 const float t = poles.front().getTargetValue();
546 for (auto& pole : poles)
547 {
548 pole.setTargetValue(t);
549 pole.skip();
550 }
551}
552
553// --- AsymmetricSmoother ---
554
555inline void Smoothers::AsymmetricSmoother::reset(double sampleRate, float attackMilliseconds, float releaseMilliseconds, float initialValue) noexcept
556{
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;
563}
564
565inline void Smoothers::AsymmetricSmoother::setTargetValue(float newTarget) noexcept
566{
567 target = newTarget;
568}
569
571{
572 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
573 const double c = (target > current) ? attackCoeff : releaseCoeff;
574 current = target + c * (current - target);
575 return static_cast<float>(current);
576}
577
579{
580 return std::abs(current - target) > epsilon;
581}
582
584{
585 current = target;
586}
587
588// --- SlewLimiter ---
589
590inline void Smoothers::SlewLimiter::reset(double sampleRate, float maxRatePerSecond, float initialValue) noexcept
591{
592 // Clamp at zero: a negative rate would flip the clamp bounds below
593 // (std::clamp with lo > hi is undefined behaviour). Zero legitimately
594 // means "frozen": the value is not allowed to move at all.
595 maxDelta = std::max(0.0f, maxRatePerSecond / static_cast<float>(sampleRate));
596 current = initialValue;
597 target = initialValue;
598}
599
600inline void Smoothers::SlewLimiter::setTargetValue(float newTarget) noexcept
601{
602 target = newTarget;
603}
604
606{
607 float delta = target - current;
608 delta = std::clamp(delta, -maxDelta, maxDelta);
609 current += delta;
610 return current;
611}
612
613inline bool Smoothers::SlewLimiter::isSmoothing() const noexcept
614{
615 return std::abs(current - target) > epsilon;
616}
617
618inline void Smoothers::SlewLimiter::skip() noexcept
619{
620 current = target;
621}
622
623// --- StateVariableSmoother ---
624
625inline void Smoothers::StateVariableSmoother::reset(double sampleRate, float timeConstantMilliseconds, float q, float initialValue) noexcept
626{
627 const double timeConstantSeconds = static_cast<double>(timeConstantMilliseconds) / 1000.0;
628 const double fs = sampleRate;
629 // A zero time constant means "instantaneous", not "frozen": map it to the
630 // fastest usable cutoff (fc = 0 would zero the coefficients and the
631 // output would never move again). Clamp below Nyquist so tan(pi*fc/fs)
632 // stays finite/stable for very small time constants (otherwise the TPT
633 // prewarp blows up near fs/2).
634 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
635 fc = std::min(fc, fs * 0.49);
636
637 g = std::tan(dspark::pi<double> * fc / fs);
638 k = 1.0 / static_cast<double>(std::max(q, 0.01f)); // TPT damping uses k = 1/Q.
639
640 a1 = 1.0 / (1.0 + g * (g + k));
641 a2 = g * a1;
642 a3 = g * a2;
643
644 v1 = 0.0;
645 v2 = initialValue;
646 target = initialValue;
647 lowpass = initialValue;
648 bandpass = 0.0;
649 highpass = 0.0;
650}
651
652inline void Smoothers::StateVariableSmoother::setTargetValue(float newTarget) noexcept
653{
654 target = newTarget;
655}
656
658{
659 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
660
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;
667
668 lowpass = v2_new;
669 bandpass = v1_new;
670 highpass = v0 - k * v1_new - v2_new;
671
672 return static_cast<float>(lowpass);
673}
674
676{
677 // The bandpass term is the filter's velocity. Without it, a Q > 0.5
678 // response crossing the target (overshoot) can momentarily satisfy
679 // |lowpass - target| < epsilon at full speed, and the anti-denormal
680 // check in getNextValue() would snap mid-flight, truncating the
681 // trajectory with a derivative kink.
682 return std::abs(lowpass - target) > epsilon || std::abs(bandpass) > epsilon;
683}
684
686{
687 v1 = 0.0;
688 v2 = target;
689 lowpass = target;
690 bandpass = 0.0;
691 highpass = 0.0;
692}
693
694// --- ButterworthSmoother ---
695
696inline void Smoothers::ButterworthSmoother::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
697{
698 constexpr double kSqrt2 = 1.4142135623730950488;
699 const double timeConstantSeconds = static_cast<double>(timeConstantMilliseconds) / 1000.0;
700 const double fs = sampleRate;
701 // Zero time constant maps to the fastest usable cutoff (see the SVF
702 // smoother): fc = 0 would freeze the filter short of the target forever.
703 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
704 fc = std::min(fc, fs * 0.49); // keep below Nyquist for a stable bilinear prewarp
705 const double tanw = std::tan(dspark::pi<double> * fc / fs);
706 const double tanw2 = tanw * tanw;
707
708 const double denom = 1.0 + kSqrt2 * tanw + tanw2;
709
710 b0 = tanw2 / denom;
711 b1 = 2.0 * tanw2 / denom;
712 b2 = tanw2 / denom;
713
714 a1 = 2.0 * (tanw2 - 1.0) / denom;
715 a2 = (1.0 - kSqrt2 * tanw + tanw2) / denom;
716
717 // The motion threshold for the settle check: the peak per-sample velocity
718 // of a residual oscillation of amplitude epsilon at fc. Near the target
719 // crossing of the overshoot the output moves fast in filter time but by
720 // tiny per-sample amounts (proportional to fc/fs), so comparing the
721 // one-sample difference against the plain epsilon would still snap
722 // mid-flight for small targets.
723 velEps = static_cast<double>(epsilon) * (static_cast<double>(Constants::twoPi) * fc / fs);
724
725 // TDF-II steady state for a constant input/output v requires the s2 term
726 // inside s1: s2* = (b2 - a2)*v and s1* = (b1 - a1)*v + s2*. Without the
727 // + s2 term the first sample of every new ramp overshot to ~2x the value
728 // (an audible click, the opposite of a smoother's job).
729 s2 = (b2 - a2) * initialValue;
730 s1 = (b1 - a1) * initialValue + s2;
731 target = initialValue;
732 lastOut = initialValue;
733 prevOut = initialValue;
734}
735
736inline void Smoothers::ButterworthSmoother::setTargetValue(float newTarget) noexcept
737{
738 target = newTarget;
739}
740
742{
743 if (!isSmoothing()) { skip(); return static_cast<float>(target); } // Anti-denormal check
744
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;
749
750 prevOut = lastOut;
751 lastOut = y0;
752 return static_cast<float>(y0);
753}
754
756{
757 // Error AND motion: a Butterworth response (Q = 0.707) overshoots, so
758 // the output crosses the target at full speed. Judging by the error
759 // alone, the anti-denormal check in getNextValue() would snap exactly
760 // at that crossing and truncate the remaining trajectory. Motion is
761 // measured against velEps (velocity of an epsilon-amplitude residual),
762 // the same role the bandpass term plays in the SVF smoother.
763 return std::abs(lastOut - target) > epsilon
764 || std::abs(lastOut - prevOut) > velEps;
765}
766
768{
769 // Same steady-state form as reset(): s1 must include the settled s2.
770 s2 = (b2 - a2) * target;
771 s1 = (b1 - a1) * target + s2;
772 lastOut = target;
773 prevOut = target;
774}
775
776// --- CriticallyDampedSmoother ---
777
778inline void Smoothers::CriticallyDampedSmoother::reset(double sampleRate, float timeConstantMilliseconds, float initialValue) noexcept
779{
780 // Critical damping (zeta = 1) corresponds exactly to Q = 0.5, not to the
781 // Butterworth Q = 0.707.
782 StateVariableSmoother::reset(sampleRate, timeConstantMilliseconds, 0.5f, initialValue);
783}
784
785} // namespace dspark
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:57
constexpr T pi
Pi (3.14159...) for the given floating-point type.
Definition DspMath.h:45
constexpr T twoPi
2 * Pi (6.28318...).
Definition DspMath.h:48
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:555
bool isSmoothing() const noexcept
Definition Smoothers.h:578
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:565
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:736
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:696
float getTargetValue() const noexcept
Definition Smoothers.h:295
bool isSmoothing() const noexcept
Definition Smoothers.h:755
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:778
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:457
void setCurrentAndTargetValue(float value) noexcept
Definition Smoothers.h:449
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:528
float getTargetValue() const noexcept
Definition Smoothers.h:173
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:510
void reset(double sampleRate, float timeConstantMilliseconds, float initialValue=0.0f) noexcept
Definition Smoothers.h:503
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:469
bool isSmoothing() const noexcept
Definition Smoothers.h:490
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:478
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:590
float getTargetValue() const noexcept
Definition Smoothers.h:225
float getNextValue() noexcept
Definition Smoothers.h:605
bool isSmoothing() const noexcept
Definition Smoothers.h:613
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:600
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:625
void setTargetValue(float newTarget) noexcept
Definition Smoothers.h:652
float getTargetValue() const noexcept
Definition Smoothers.h:254
float getHighPassOutput() const noexcept
Definition Smoothers.h:259