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;
417 stepsToGo = totalSteps;
419 if (stepsToGo > 0 && std::abs(current) >
epsilon)
421 float safeCur = (current > 0.0f) ? std::max(current,
epsilon)
423 float ratio = target / safeCur;
425 coeff = std::exp(std::log(ratio) /
static_cast<float>(stepsToGo));
435 if (stepsToGo <= 0)
return current;
438 if (stepsToGo == 0) current = target;
452 return std::abs(current - target) >
epsilon;
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;
479 current = target + coeff * (current - target);
480 return static_cast<float>(current);
485 return std::abs(current - target) >
epsilon;
495template <std::
size_t N>
498 for (
auto& pole : poles)
499 pole.reset(sampleRate, timeConstantMilliseconds, initialValue);
502template <std::
size_t N>
505 poles[0].setTargetValue(newTarget);
508template <std::
size_t N>
511 float val = poles[0].getNextValue();
512 for (std::size_t i = 1; i < N; ++i)
514 poles[i].setTargetValue(val);
515 val = poles[i].getNextValue();
520template <std::
size_t N>
528 return std::abs(poles.back().getCurrentValue()
529 - poles.front().getTargetValue()) >
epsilon;
532template <std::
size_t N>
538 const float t = poles.front().getTargetValue();
539 for (
auto& pole : poles)
541 pole.setTargetValue(t);
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;
566 const double c = (target > current) ? attackCoeff : releaseCoeff;
567 current = target + c * (current - target);
568 return static_cast<float>(current);
573 return std::abs(current - target) >
epsilon;
588 maxDelta = std::max(0.0f, maxRatePerSecond /
static_cast<float>(sampleRate));
589 current = initialValue;
590 target = initialValue;
600 float delta = target - current;
601 delta = std::clamp(delta, -maxDelta, maxDelta);
608 return std::abs(current - target) >
epsilon;
620 const double timeConstantSeconds =
static_cast<double>(timeConstantMilliseconds) / 1000.0;
621 const double fs = sampleRate;
627 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (
static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
628 fc = std::min(fc, fs * 0.49);
630 g = std::tan(dspark::pi<double> * fc / fs);
631 k = 1.0 /
static_cast<double>(std::max(q, 0.01f));
633 a1 = 1.0 / (1.0 + g * (g + k));
639 target = initialValue;
640 lowpass = initialValue;
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;
663 highpass = v0 - k * v1_new - v2_new;
665 return static_cast<float>(lowpass);
675 return std::abs(lowpass - target) >
epsilon || std::abs(bandpass) >
epsilon;
691 constexpr double kSqrt2 = 1.4142135623730950488;
692 const double timeConstantSeconds =
static_cast<double>(timeConstantMilliseconds) / 1000.0;
693 const double fs = sampleRate;
696 double fc = timeConstantSeconds > 1e-9 ? 1.0 / (
static_cast<double>(Constants::twoPi) * timeConstantSeconds) : fs;
697 fc = std::min(fc, fs * 0.49);
698 const double tanw = std::tan(dspark::pi<double> * fc / fs);
699 const double tanw2 = tanw * tanw;
701 const double denom = 1.0 + kSqrt2 * tanw + tanw2;
704 b1 = 2.0 * tanw2 / denom;
707 a1 = 2.0 * (tanw2 - 1.0) / denom;
708 a2 = (1.0 - kSqrt2 * tanw + tanw2) / denom;
716 velEps =
static_cast<double>(
epsilon) * (
static_cast<double>(Constants::twoPi) * fc / fs);
722 s2 = (b2 - a2) * initialValue;
723 s1 = (b1 - a1) * initialValue + s2;
724 target = initialValue;
725 lastOut = initialValue;
726 prevOut = initialValue;
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;
745 return static_cast<float>(y0);
756 return std::abs(lastOut - target) >
epsilon
757 || std::abs(lastOut - prevOut) > velEps;
763 s2 = (b2 - a2) * target;
764 s1 = (b1 - a1) * target + s2;
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...).
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