wip audio synthesizer based on the rust crate cpal
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
beeper/src/beep/tween.rs

89 lines
2.4 KiB

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<f32>,
// 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;
}
}