pub trait ParamSet: 'static + Send + Sync {
type Id: Copy + Into<ParamId>;
const SPECS: &'static [ParamSpec];
fn spec(id: Self::Id) -> Option<&'static ParamSpec>;
fn iter() -> impl Iterator<Item = &'static ParamSpec>;
fn count() -> usize {
Self::SPECS.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ParamId(pub u32);
impl From<u32> for ParamId {
fn from(id: u32) -> Self {
ParamId(id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum WavecraftParamId {
Gain = 0,
}
impl From<WavecraftParamId> for ParamId {
fn from(id: WavecraftParamId) -> Self {
ParamId(id as u32)
}
}
#[derive(Debug, Clone)]
pub struct ParamSpec {
pub id: ParamId,
pub name: &'static str,
pub short_name: &'static str,
pub unit: &'static str,
pub default: f32,
pub min: f32,
pub max: f32,
pub step: f32,
}
pub const PARAM_SPECS: &[ParamSpec] = &[ParamSpec {
id: ParamId(0),
name: "Gain",
short_name: "Gain",
unit: "dB",
default: 0.0,
min: -24.0,
max: 24.0,
step: 0.1,
}];
pub struct WavecraftParams;
impl ParamSet for WavecraftParams {
type Id = WavecraftParamId;
const SPECS: &'static [ParamSpec] = PARAM_SPECS;
fn spec(id: Self::Id) -> Option<&'static ParamSpec> {
Self::SPECS.iter().find(|s| s.id.0 == id as u32)
}
fn iter() -> impl Iterator<Item = &'static ParamSpec> {
Self::SPECS.iter()
}
}
#[inline]
pub fn db_to_linear(db: f32) -> f32 {
10.0_f32.powf(db / 20.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_db_to_linear_unity() {
let result = db_to_linear(0.0);
assert!((result - 1.0).abs() < 1e-6, "0 dB should equal 1.0 linear");
}
#[test]
fn test_db_to_linear_minus_6db() {
let result = db_to_linear(-6.0);
assert!(
(result - 0.501187).abs() < 0.001,
"-6 dB should be approximately 0.5"
);
}
#[test]
fn test_db_to_linear_plus_6db() {
let result = db_to_linear(6.0);
assert!(
(result - 1.9953).abs() < 0.001,
"+6 dB should be approximately 2.0"
);
}
#[test]
fn test_db_to_linear_minus_infinity() {
let result = db_to_linear(-100.0);
assert!(result < 1e-4, "-100 dB should be nearly silent");
}
}