| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
pub fn measure_lufs(samples: &[f32], sample_rate: u32) -> f64 { |
| 6 |
if samples.is_empty() || sample_rate == 0 { |
| 7 |
return -70.0; |
| 8 |
} |
| 9 |
let mut meter = bs1770::ChannelLoudnessMeter::new(sample_rate); |
| 10 |
meter.push(samples.iter().copied()); |
| 11 |
let windows = meter.into_100ms_windows(); |
| 12 |
let power = bs1770::gated_mean(windows.as_ref()); |
| 13 |
let lkfs = power.loudness_lkfs() as f64; |
| 14 |
if lkfs.is_nan() || lkfs.is_infinite() { |
| 15 |
-70.0 |
| 16 |
} else { |
| 17 |
lkfs |
| 18 |
} |
| 19 |
} |
| 20 |
|
| 21 |
#[cfg(test)] |
| 22 |
mod tests { |
| 23 |
use super::*; |
| 24 |
|
| 25 |
#[test] |
| 26 |
fn silence_is_very_quiet() { |
| 27 |
let silence = vec![0.0f32; 48000]; |
| 28 |
let lufs = measure_lufs(&silence, 48000); |
| 29 |
|
| 30 |
assert!(lufs < -60.0, "silence LUFS should be < -60, got {lufs}"); |
| 31 |
} |
| 32 |
|
| 33 |
#[test] |
| 34 |
fn loud_sine_is_near_zero() { |
| 35 |
let samples: Vec<f32> = (0..48000) |
| 36 |
.map(|i| (2.0 * std::f32::consts::PI * 1000.0 * i as f32 / 48000.0).sin()) |
| 37 |
.collect(); |
| 38 |
let lufs = measure_lufs(&samples, 48000); |
| 39 |
|
| 40 |
assert!( |
| 41 |
lufs > -10.0 && lufs < 0.0, |
| 42 |
"full-scale sine LUFS should be near -3, got {lufs}" |
| 43 |
); |
| 44 |
} |
| 45 |
} |
| 46 |
|