#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Param {
pub current: f32,
pub target: f32,
pub step: f32,
}
impl Param {
pub fn new(value: f32) -> Self {
Self {
current: value,
target: value,
step: 0.0,
}
}
#[inline]
pub fn set_target(&mut self, target: f32, ramp_samples: u32) {
self.target = target;
if ramp_samples == 0 {
self.current = target;
self.step = 0.0;
} else {
self.step = (target - self.current) / ramp_samples as f32;
}
}
#[inline]
pub fn set_target_clamped(
&mut self,
target: f32,
ramp_samples: u32,
min: f32,
max: f32,
) -> f32 {
let validated = if target.is_finite() {
target.clamp(min, max)
} else {
self.current
};
self.set_target(validated, ramp_samples);
validated
}
#[inline(always)]
pub fn tick(&mut self) {
if self.step != 0.0 {
self.current += self.step;
if (self.step > 0.0 && self.current >= self.target)
|| (self.step < 0.0 && self.current <= self.target)
{
self.current = self.target;
self.step = 0.0;
}
}
}
#[inline]
pub fn fill_buffer(&mut self, out: &mut [f32]) {
if self.step == 0.0 {
out.fill(self.current);
} else {
for sample in out.iter_mut() {
*sample = self.current;
self.tick();
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ParamBlock {
pub params: [Param; 8],
pub count: usize,
}
impl ParamBlock {
pub fn new() -> Self {
Self {
params: [Param::new(0.0); 8],
count: 0,
}
}
pub fn add(&mut self, value: f32) -> usize {
let idx = self.count;
self.params[idx] = Param::new(value);
self.count += 1;
idx
}
#[inline(always)]
pub fn get(&self, idx: usize) -> &Param {
&self.params[idx]
}
#[inline(always)]
pub fn get_mut(&mut self, idx: usize) -> &mut Param {
&mut self.params[idx]
}
#[inline(always)]
pub fn tick_all(&mut self) {
for p in self.params[..self.count].iter_mut() {
p.tick();
}
}
}
impl Default for ParamBlock {
fn default() -> Self {
Self::new()
}
}
pub mod validation {
#[inline]
pub fn is_finite(value: f32) -> bool {
value.is_finite()
}
#[inline]
pub fn clamp_or_default(value: f32, min: f32, max: f32, default: f32) -> f32 {
if value.is_finite() {
value.clamp(min, max)
} else {
default
}
}
#[inline]
pub fn validate_frequency(freq: f32, sample_rate: f32) -> f32 {
const MIN_FREQ: f32 = 0.1;
let max_freq = sample_rate * 0.5; clamp_or_default(freq, MIN_FREQ, max_freq, 440.0) }
#[inline]
pub fn validate_gain(gain: f32, max_gain: f32) -> f32 {
clamp_or_default(gain, 0.0, max_gain, 1.0) }
#[inline]
pub fn validate_time_ms(time_ms: f32, min_ms: f32, max_ms: f32) -> f32 {
clamp_or_default(time_ms, min_ms, max_ms, 100.0) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_param_validation_nan() {
let mut param = Param::new(0.5);
let actual = param.set_target_clamped(f32::NAN, 0, 0.0, 1.0);
assert_eq!(actual, 0.5); assert_eq!(param.current, 0.5);
}
#[test]
fn test_param_validation_infinity() {
let mut param = Param::new(0.5);
let actual = param.set_target_clamped(f32::INFINITY, 0, 0.0, 1.0);
assert_eq!(actual, 0.5); assert_eq!(param.current, 0.5);
}
#[test]
fn test_param_validation_clamp_max() {
let mut param = Param::new(0.5);
let actual = param.set_target_clamped(1.5, 0, 0.0, 1.0);
assert_eq!(actual, 1.0); assert_eq!(param.current, 1.0);
}
#[test]
fn test_param_validation_clamp_min() {
let mut param = Param::new(0.5);
let actual = param.set_target_clamped(-0.5, 0, 0.0, 1.0);
assert_eq!(actual, 0.0); assert_eq!(param.current, 0.0);
}
#[test]
fn test_param_validation_valid_value() {
let mut param = Param::new(0.5);
let actual = param.set_target_clamped(0.75, 0, 0.0, 1.0);
assert_eq!(actual, 0.75); assert_eq!(param.current, 0.75);
}
#[test]
fn test_validation_frequency() {
use validation::validate_frequency;
assert_eq!(validate_frequency(440.0, 48000.0), 440.0);
assert_eq!(validate_frequency(-100.0, 48000.0), 0.1);
assert_eq!(validate_frequency(30000.0, 48000.0), 24000.0);
assert_eq!(validate_frequency(f32::NAN, 48000.0), 440.0);
}
#[test]
fn test_validation_gain() {
use validation::validate_gain;
assert_eq!(validate_gain(0.5, 2.0), 0.5);
assert_eq!(validate_gain(-0.5, 2.0), 0.0);
assert_eq!(validate_gain(3.0, 2.0), 2.0);
assert_eq!(validate_gain(f32::NAN, 2.0), 1.0);
}
}