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