#[derive(Clone, Debug)]
pub struct Parameter {
pub value: f32,
pub target: f32,
pub smoothing_coeff: f32,
}
impl Parameter {
pub fn new(initial: f32) -> Self {
Self {
value: initial,
target: initial,
smoothing_coeff: 0.005,
}
}
pub fn with_smoothing(initial: f32, coeff: f32) -> Self {
Self {
value: initial,
target: initial,
smoothing_coeff: coeff,
}
}
pub fn coeff_from_time_constant(time_constant_secs: f32, sample_rate: f32) -> f32 {
if time_constant_secs <= 0.0 {
return 1.0;
}
1.0 - (-1.0 / (time_constant_secs * sample_rate)).exp()
}
#[inline]
pub fn set(&mut self, target: f32) {
self.target = target;
}
#[inline]
pub fn reset(&mut self, value: f32) {
self.value = value;
self.target = value;
}
#[inline]
pub fn step(&mut self) {
self.value += self.smoothing_coeff * (self.target - self.value);
}
#[inline]
pub fn is_settled(&self) -> bool {
(self.value - self.target).abs() < 1e-6
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parameter_smoothing() {
let mut p = Parameter::with_smoothing(0.0, 0.1);
p.set(1.0);
for _ in 0..1000 {
p.step();
}
assert!((p.value - 1.0).abs() < 1e-4);
}
#[test]
fn test_parameter_reset() {
let mut p = Parameter::new(0.0);
p.reset(5.0);
assert_eq!(p.value, 5.0);
assert_eq!(p.target, 5.0);
}
#[test]
fn test_parameter_monotonic() {
let mut p = Parameter::with_smoothing(0.0, 0.01);
p.set(1.0);
let mut prev = 0.0;
for _ in 0..100 {
p.step();
assert!(p.value >= prev, "parameter decreased");
prev = p.value;
}
}
}