85 void prepare(
double sampleRate,
double rampTimeMs = 20.0) noexcept
87 sampleRate_ = std::max(1.0, sampleRate);
89 rampTimeMs_ = std::max(0.0, rampTimeMs);
94 const double tauSamples = sampleRate_ * rampTimeMs_ / 1000.0;
95 expCoeff_ = (tauSamples > 0.0) ? std::exp(-1.0 / tauSamples) : 0.0;
99 rampSamples_ = std::max(1.0, rampTimeMs_ * sampleRate_ / 1000.0);
103 const double srRatio = 44100.0 / sampleRate_;
104 chaseMultDecay_ = std::pow(0.9999, srRatio);
105 chaseAddDecay_ = 0.01 * srRatio;
125 if (newTarget != newTarget)
return;
126 if (newTarget != target_)
131 chaseSpeed_ = 2500.0;
141 const double target =
static_cast<double>(target_);
142 if (current_ == target)
return target_;
148 current_ = target + expCoeff_ * (current_ - target);
153 if (current_ < target)
154 current_ = std::min(current_ + linearRate_, target);
156 current_ = std::max(current_ - linearRate_, target);
166 chaseSpeed_ = std::max(350.0, std::min(2500.0, chaseSpeed_ * chaseMultDecay_ - chaseAddDecay_));
167 current_ = (current_ * chaseSpeed_ + target) / (chaseSpeed_ + 1.0);
175 const double eps = kEpsilon * std::max(1.0, std::abs(target));
176 if (std::abs(current_ - target) < eps)
179 return static_cast<T
>(current_);
195 const double target =
static_cast<double>(target_);
196 const size_t n = buffer.size();
197 if (current_ == target)
199 std::fill(buffer.begin(), buffer.end(), target_);
207 double current = current_;
208 const double eps = kEpsilon * std::max(1.0, std::abs(target));
215 const double coeff = expCoeff_;
218 current = target + coeff * (current - target);
219 if (std::abs(current - target) < eps) { current = target;
break; }
220 buffer[i] =
static_cast<T
>(current);
227 const double rate = linearRate_;
230 if (current < target) current = std::min(current + rate, target);
231 else if (current > target) current = std::max(current - rate, target);
232 if (std::abs(current - target) < eps) { current = target;
break; }
233 buffer[i] =
static_cast<T
>(current);
244 double speed = chaseSpeed_;
245 const double multDecay = chaseMultDecay_;
246 const double addDecay = chaseAddDecay_;
249 speed = std::max(350.0, std::min(2500.0, speed * multDecay - addDecay));
250 current = (current * speed + target) / (speed + 1.0);
251 if (std::abs(current - target) < eps) { current = target;
break; }
252 buffer[i] =
static_cast<T
>(current);
273 [[nodiscard]]
bool isSmoothing() const noexcept {
return current_ !=
static_cast<double>(target_); }
276 void skip() noexcept { current_ =
static_cast<double>(target_); chaseSpeed_ = 350.0; }
282 current_ =
static_cast<double>(value);
289 prepare(sampleRate, rampTimeMs);
293 void updateLinearRate() noexcept
295 linearRate_ = std::abs(
static_cast<double>(target_) - current_) / rampSamples_;
298 double current_{ 0.0 };
303 double expCoeff_{ 0.0 };
304 double linearRate_{ 0.0 };
305 double rampSamples_{ 882.0 };
308 double chaseSpeed_{ 350.0 };
309 double chaseMultDecay_{ 0.9999 };
310 double chaseAddDecay_{ 0.01 };
312 double sampleRate_{ 44100.0 };
313 double rampTimeMs_{ 20.0 };
315 static constexpr double kEpsilon = 1e-7;
Zero-allocation parameter smoother for real-time audio.
void setSmoothingType(SmoothingType type) noexcept
Sets the smoothing algorithm.
SmoothedValue & operator=(const SmoothedValue &)=delete
void reset(T value=T(0)) noexcept
Hard-resets both current and target states to a specific value.
void setRampTime(double sampleRate, double rampTimeMs) noexcept
Updates sample rate and/or ramp time, recalculating internal steps.
T getNextValue() noexcept
Calculates and returns the next smoothed value.
T getCurrentValue() const noexcept
Returns the current internal value without advancing the state.
void skip() noexcept
Forces the current value to instantly match the target, bypassing time.
bool isSmoothing() const noexcept
Evaluates if the smoother is actively transitioning.
void setTargetValue(T newTarget) noexcept
Updates the target value. Safe to call continuously (e.g., from host automation).
@ Linear
Rate-limited ramp. Constant velocity, exact target arrival.
@ Disabled
Instant snapping, no smoothing applied.
@ Exponential
One-pole IIR. Natural, musical interpolation.
@ Chase
Adaptive speed. Gentle start after a jump, accelerating settle.
void processBlock(std::span< T > buffer) noexcept
Computes a block of smoothed values into an output buffer.
SmoothedValue(const SmoothedValue &)=delete
Prevents accidental copying of stateful DSP objects.
SmoothingType getSmoothingType() const noexcept
Returns the current smoothing algorithm.
T getTargetValue() const noexcept
Returns the set target value.
void prepare(double sampleRate, double rampTimeMs=20.0) noexcept
Precalculates internal coefficients based on sample rate and timing.
Main namespace for the DSPark framework.