1use bliss_audio_aubio_rs::level_lin;
7use ndarray::{arr1, Axis};
8
9use crate::Feature;
10
11use super::utils::{mean, Normalize};
12
13#[derive(Default, Clone)]
31pub struct LoudnessDesc {
32 pub values: Vec<f32>,
33}
34
35impl LoudnessDesc {
36 pub const WINDOW_SIZE: usize = 1024;
37
38 pub fn do_(&mut self, chunk: &[f32]) {
39 let level = level_lin(chunk);
40 self.values.push(level);
41 }
42
43 pub fn get_value(&mut self) -> Vec<Feature> {
44 let std_value = Feature::from(
46 arr1(&self.values)
47 .std_axis(Axis(0), 0.)
48 .into_scalar()
49 .max(1e-9),
50 );
51 let mean_value = Feature::from(mean(&self.values).max(1e-9));
52 vec![
53 self.normalize(10.0 * mean_value.log10()),
54 self.normalize(10.0 * std_value.log10()),
55 ]
56 }
57}
58
59impl Normalize for LoudnessDesc {
60 const MAX_VALUE: Feature = 0.;
61 const MIN_VALUE: Feature = -90.;
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use crate::decoder::{Decoder as DecoderTrait, MecompDecoder as Decoder};
68 use std::path::Path;
69
70 #[test]
71 fn test_loudness() {
72 let song = Decoder::decode(Path::new("data/s16_mono_22_5kHz.flac")).unwrap();
73 let mut loudness_desc = LoudnessDesc::default();
74 for chunk in song.samples.chunks_exact(LoudnessDesc::WINDOW_SIZE) {
75 loudness_desc.do_(chunk);
76 }
77 let expected_values = [0.271_263, 0.257_718_1];
78 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
79 assert!(0.01 > (expected - actual).abs(), "{expected} !~= {actual}");
80 }
81 }
82
83 #[test]
84 fn test_loudness_boundaries() {
85 let mut loudness_desc = LoudnessDesc::default();
86 let silence_chunk = vec![0.; 1024];
87 loudness_desc.do_(&silence_chunk);
88 let expected_values = [-1., -1.];
89 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
90 assert!(
91 0.000_000_1 > (expected - actual).abs(),
92 "{expected} !~= {actual}"
93 );
94 }
95
96 let mut loudness_desc = LoudnessDesc::default();
97 let silence_chunk = vec![1.; 1024];
98 loudness_desc.do_(&silence_chunk);
99 let expected_values = [1., -1.];
100 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
101 assert!(
102 0.000_000_1 > (expected - actual).abs(),
103 "{expected} !~= {actual}"
104 );
105 }
106
107 let mut loudness_desc = LoudnessDesc::default();
108 let silence_chunk = vec![-1.; 1024];
109 loudness_desc.do_(&silence_chunk);
110 let expected_values = [1., -1.];
111 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
112 assert!(
113 0.000_000_1 > (expected - actual).abs(),
114 "{expected} !~= {actual}"
115 );
116 }
117 }
118}