pub const VOLUME_TABLE: [u16; 16] = [
20, 53, 88, 125, 193, 258, 385, 525, 753, 1029, 1523, 2077, 3110, 4395, 7073, 10922,
];
pub const VOLUME_SCALE: f32 = 1.0 / 32767.0;
#[inline]
pub fn get_volume(amplitude: u8) -> f32 {
VOLUME_TABLE[(amplitude & 0x0F) as usize] as f32 * VOLUME_SCALE
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_volume_table_edge_values() {
assert!(get_volume(0) < 0.001, "Lowest level should be near silence");
assert!(
(get_volume(15) - (10922.0 / 32767.0)).abs() < 1e-6,
"Max level should match ST reference"
);
}
#[test]
fn test_volume_table_monotonic_increasing() {
for i in 1..16 {
assert!(
VOLUME_TABLE[i] > VOLUME_TABLE[i - 1],
"Volume table not monotonic: STSOUND_VOLUME_TABLE[{}] ({}) <= STSOUND_VOLUME_TABLE[{}] ({})",
i,
VOLUME_TABLE[i],
i - 1,
VOLUME_TABLE[i - 1]
);
}
}
#[test]
fn test_volume_table_all_values_in_range() {
for amplitude in 0u8..=15u8 {
let val = get_volume(amplitude);
assert!(
(0.0..=1.0).contains(&val),
"Volume table value {val} at index {amplitude} out of range [0.0, 1.0]"
);
}
}
#[test]
fn test_volume_table_size() {
assert_eq!(VOLUME_TABLE.len(), 16);
}
#[test]
fn test_get_volume_basic_values() {
assert!(get_volume(0) < 0.001, "get_volume(0) should be near silent");
assert!((get_volume(15) - (10922.0 / 32767.0)).abs() < 1e-6);
assert!(
get_volume(7) > 0.015 && get_volume(7) < 0.017,
"get_volume(7) should be ~1.6%"
);
}
#[test]
fn test_get_volume_with_mask() {
assert_eq!(
get_volume(0x0F),
get_volume(0xFF),
"0xFF should mask to 0x0F"
);
assert_eq!(
get_volume(0x07),
get_volume(0x87),
"0x87 should mask to 0x07"
);
assert_eq!(
get_volume(0x00),
get_volume(0xF0),
"0xF0 should mask to 0x00"
);
}
#[test]
fn test_get_volume_all_values() {
for (amplitude, &raw) in VOLUME_TABLE.iter().enumerate() {
let expected = raw as f32 * VOLUME_SCALE;
let actual = get_volume(amplitude as u8);
assert!((actual - expected).abs() < f32::EPSILON * 2.0);
}
}
#[test]
fn test_volume_table_exponential_progression() {
let mut ratios = Vec::new();
for i in 1..VOLUME_TABLE.len() {
let prev = VOLUME_TABLE[i - 1];
if prev > 0 {
ratios.push(VOLUME_TABLE[i] as f32 / prev as f32);
}
}
let avg_ratio = ratios.iter().sum::<f32>() / ratios.len() as f32;
assert!(avg_ratio > 1.2 && avg_ratio < 1.7);
}
}