use crate::tween::Tweenable;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Volume {
Amplitude(f64),
Decibels(f64),
}
impl Volume {
pub const MIN_DECIBELS: f64 = -60.0;
pub fn as_amplitude(&self) -> f64 {
match self {
Volume::Amplitude(amplitude) => *amplitude,
Volume::Decibels(db) => {
if *db == 0.0 {
return 1.0;
}
if *db <= Self::MIN_DECIBELS {
return 0.0;
}
10.0f64.powf(*db / 20.0)
}
}
}
pub fn as_decibels(&self) -> f64 {
match self {
Volume::Amplitude(amplitude) => {
if *amplitude <= 0.0 {
return Self::MIN_DECIBELS;
}
20.0 * amplitude.log10()
}
Volume::Decibels(db) => *db,
}
}
}
impl From<f64> for Volume {
fn from(amplitude: f64) -> Self {
Self::Amplitude(amplitude)
}
}
impl Tweenable for Volume {
fn lerp(a: Self, b: Self, amount: f64) -> Self {
match b {
Volume::Amplitude(b) => Volume::Amplitude(Tweenable::lerp(a.as_amplitude(), b, amount)),
Volume::Decibels(b) => Volume::Decibels(Tweenable::lerp(a.as_decibels(), b, amount)),
}
}
}
#[cfg(test)]
#[test]
#[allow(clippy::float_cmp)]
fn test() {
const TEST_CALCULATIONS: [(f64, f64); 6] = [
(0.0, 1.0),
(3.0, 1.4125375446227544),
(12.0, 3.9810717055349722),
(-3.0, 0.7079457843841379),
(-12.0, 0.251188643150958),
(Volume::MIN_DECIBELS, 0.0),
];
for (db, amplitude) in TEST_CALCULATIONS {
assert_eq!(Volume::Amplitude(amplitude).as_amplitude(), amplitude);
assert!((Volume::Amplitude(amplitude).as_decibels() - db).abs() < 0.00001);
assert_eq!(Volume::Decibels(db).as_decibels(), db);
assert!((Volume::Decibels(db).as_amplitude() - amplitude).abs() < 0.00001);
}
assert_eq!(
Volume::Decibels(Volume::MIN_DECIBELS - 100.0).as_amplitude(),
0.0
);
assert_eq!(Volume::Amplitude(-1.0).as_decibels(), Volume::MIN_DECIBELS);
}