use truce_params::sample::Float;
#[inline]
#[must_use]
pub fn db_to_linear<F: Float>(db: F) -> F {
(db * F::from_f64(std::f64::consts::LN_10 / 20.0)).exp()
}
#[inline]
#[must_use]
pub fn linear_to_db<F: Float>(linear: F) -> F {
F::from_f64(20.0) * linear.log10()
}
#[inline]
#[must_use]
pub fn midi_note_to_freq<F: Float>(note: u8) -> F {
let semitones = F::from_f64(f64::from(note) - 69.0);
F::from_f64(440.0) * F::from_f64(2.0).powf(semitones / F::from_f64(12.0))
}
#[inline]
#[must_use]
pub fn meter_display(linear_peak: f32) -> f32 {
if linear_peak < 1e-6 {
return 0.0;
}
let db = 20.0 * linear_peak.log10();
((db + 60.0) / 60.0).clamp(0.0, 1.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn db_linear_round_trip_f64() {
let db = -6.0_f64;
let linear = db_to_linear(db);
let back = linear_to_db(linear);
assert!((back - db).abs() < 1e-10);
}
#[test]
fn db_linear_round_trip_f32() {
let db = -6.0_f32;
let linear = db_to_linear(db);
let back = linear_to_db(linear);
assert!((back - db).abs() < 1e-5);
}
#[test]
fn zero_db_is_unity_f64() {
let linear: f64 = db_to_linear(0.0_f64);
assert!((linear - 1.0).abs() < 1e-10);
}
#[test]
fn zero_db_is_unity_f32() {
let linear: f32 = db_to_linear(0.0_f32);
assert!((linear - 1.0).abs() < 1e-6);
}
#[test]
fn a4_is_440_f64() {
let freq: f64 = midi_note_to_freq(69);
assert!((freq - 440.0).abs() < 1e-10);
}
#[test]
fn a4_is_440_f32() {
let freq: f32 = midi_note_to_freq(69);
assert!((freq - 440.0).abs() < 1e-3);
}
}