1use bliss_audio_aubio_rs::level_lin;
7use ndarray::{Axis, arr1};
8
9use crate::Feature;
10
11use super::utils::{Normalize, mean};
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) -> [Feature; 2] {
46 const MIN_VALUE: f32 = 1e-9;
47
48 let std_value = arr1(&self.values)
50 .std_axis(Axis(0), 0.)
51 .into_scalar()
52 .max(MIN_VALUE);
53 let mean_value = mean(&self.values).max(MIN_VALUE);
54 [
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::new()
75 .unwrap()
76 .decode(Path::new("data/s16_mono_22_5kHz.flac"))
77 .unwrap();
78 let mut loudness_desc = LoudnessDesc::default();
79 for chunk in song.samples.chunks_exact(LoudnessDesc::WINDOW_SIZE) {
80 loudness_desc.do_(chunk);
81 }
82 let expected_values = [0.271_263, 0.257_718_1];
83 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
84 assert!(0.01 > (expected - actual).abs(), "{expected} !~= {actual}");
85 }
86 }
87
88 #[test]
89 fn test_loudness_boundaries() {
90 let mut loudness_desc = LoudnessDesc::default();
91 let silence_chunk = vec![0.; 1024];
92 loudness_desc.do_(&silence_chunk);
93 let expected_values = [-1., -1.];
94 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
95 assert!(
96 0.000_000_1 > (expected - actual).abs(),
97 "{expected} !~= {actual}"
98 );
99 }
100
101 let mut loudness_desc = LoudnessDesc::default();
102 let silence_chunk = vec![1.; 1024];
103 loudness_desc.do_(&silence_chunk);
104 let expected_values = [1., -1.];
105 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
106 assert!(
107 0.000_000_1 > (expected - actual).abs(),
108 "{expected} !~= {actual}"
109 );
110 }
111
112 let mut loudness_desc = LoudnessDesc::default();
113 let silence_chunk = vec![-1.; 1024];
114 loudness_desc.do_(&silence_chunk);
115 let expected_values = [1., -1.];
116 for (expected, actual) in expected_values.iter().zip(loudness_desc.get_value().iter()) {
117 assert!(
118 0.000_000_1 > (expected - actual).abs(),
119 "{expected} !~= {actual}"
120 );
121 }
122 }
123}