pub struct Smoother { /* private fields */ }Expand description
Per-parameter smoother. All methods take &self for interior
mutability, enabling use through Arc<Params>.
Threading. The audio thread is the sole writer of current
(via next / snap) and the sole reader of coeff. The
editor / main thread is the sole writer of sample_rate and
coeff via Self::set_sample_rate, which computes the new
coefficient locally from the supplied sr before storing -
so a concurrent audio block sees either the old (sample_rate,
coeff) pair or the new one, never a mid-update split. The
stored sample_rate field is informational; it isn’t read in
the audio path, only by future writers as a freshness check.
Implementations§
Source§impl Smoother
impl Smoother
pub fn new(style: SmoothingStyle) -> Self
pub fn set_sample_rate(&self, sr: f64)
Sourcepub fn is_converged(&self, target: f64) -> bool
pub fn is_converged(&self, target: f64) -> bool
True when the smoother’s internal state matches target
closely enough that further smoothing would be a no-op.
SmoothingStyle::None always returns true. For Linear
/ Exponential, the comparison uses the same snap threshold
next() applies: (target.abs() * 1e-6).max(1e-8).
Exponential smoothing asymptotes but never lands exactly
on target; the threshold gates “close enough that any
further step is denormal-territory”.
Costs one atomic load. Plugin authors typically reach this
through crate::types::FloatParam::is_smoothing which
loads the target and inverts the answer.
Sourcepub fn next_after(&self, target: f64, n_samples: usize) -> f32
pub fn next_after(&self, target: f64, n_samples: usize) -> f32
Advance the smoother by n_samples samples in one call,
returning only the final value. Use for block-rate
consumers (hard gates, mode switches, anything that needs a
single smoothed value per audio block) where the intermediate
envelope from Self::next_block is wasted work.
One atomic load and one atomic store regardless of
n_samples. For Exponential, uses the closed-form
current + (target - current) * (1 - (1 - coeff)^N) (one
powf per call) instead of looping; for Linear, loops
because the snap-when-close-enough check breaks any clean
closed form.
Semantics match next step-for-step: equivalent to calling
next(target) n_samples times and returning the last
result, but without paying per-sample atomic costs.
Sourcepub fn next_block<const N: usize>(&self, target: f64) -> [f32; N]
pub fn next_block<const N: usize>(&self, target: f64) -> [f32; N]
Advance the smoother by N samples in one call, returning the
intermediate per-sample values as a stack-allocated array.
Issues exactly one atomic load and one atomic store
against current, regardless of N. The inner stepping runs
in a register-resident loop the optimizer can unroll and (for
Exponential / None) vectorize. Compare with Self::next
which costs one load + one store per sample and therefore
forces the compiler to keep current in memory across
iterations.
Semantics match next step-for-step: the i-th element of the
returned array is what next(target) would have produced if
called for the i-th time in sequence.