use crate::beep::hack::StableClamp; use std::time::Duration; /// Scale function amplitude by a gain pub trait GainScale { fn scale_gain(self, gain: f32) -> Self; } impl GainScale for f32 { /// Scale by gain 0-1 fn scale_gain(self, gain: f32) -> Self { // TODO what scaling to use? // self * (1.0 + gain * 9.0).log10() // self * gain.sqrt() self * gain } } /// Value fader #[derive(Clone,Debug)] pub(crate) struct Tween { /// Actual value pub actual: f32, /// Target value, approached by adding "step" to :actual" every sample pub target: f32, /// Value added to "actual" per sample. None = set to target at the end of a cycle pub step: Option, // Min value pub min: f32, // Max value pub max: f32, /// Sample rate, used to calculate time based effects pub sample_rate : f32, } impl Tween { /// Update values for the next sample pub fn tick(&mut self) { if let Some(step) = self.step { let actual0 = self.actual; self.actual += step; if (self.target > actual0 && self.target <= self.actual) || (self.target <= actual0 && self.target > self.actual) { self.step = None; self.actual = self.target; } } } /// Get actual value pub fn actual(&self) -> f32 { self.actual } /// Check if the change from actual to target is scheduled to happen instantly /// at the end of a cycle. pub fn is_change_scheduled_at_crossover(&self) -> bool { self.target != self.actual && self.step.is_none() } /// Check if the fading is in progress pub fn is_fading(&self) -> bool { self.step.is_some() } /// Schedule change at crossover pub(crate) fn set_at_crossover(&mut self, val: f32) { self.target = val.clamp_(self.min, self.max); self.step = None; // change at the earliest convenience } /// Start fading pub(crate) fn fade(&mut self, val: f32, time: Duration) { self.target = val.clamp_(self.min, self.max); let nsamples = (time.as_secs_f32() * self.sample_rate).ceil(); self.step = Some((self.target - self.actual) / nsamples); } /// Set value immediately pub(crate) fn set_immediate(&mut self, val: f32) { self.target = val.clamp_(self.min, self.max); self.actual = self.target; self.step = None; } }