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