sva_samples/measure/
sharpness.rs1use super::loudness_pattern;
4
5pub const MODEL: &str = "sharpness per frame of `loudness_pattern` at the same `frame` and \
6 `spl`, DIN 45692 (2009)'s weighted first moment: S = 0.11 sum N'(z) \
7 g(z) z / sum N'(z) acum, z each band's centre in Bark by Traunmüller \
8 (1990), z = 26.81 f / (1960 + f) - 0.53, raised by 0.15 (2 - z) \
9 under 2 and by 0.22 (z - 20.1) over 20.1 as he corrects its ends, so \
10 every band stands above 0 Bark, g(z) = 1 to 15.8 Bark and \
11 0.15 e^(0.42 (z - 15.8)) + 0.85 above, N' each Cam band's specific \
12 loudness standing for its stretch of the Bark scale; null where no \
13 band is over the pattern's `threshold_sone`. Measured, not DIN 45692: \
14 the standard weights Zwicker's specific loudness per Bark (ISO 532-1) \
15 and this one Moore and Glasberg's per Cam, so its acum are the \
16 standard's weighting on another loudness model.";
17
18#[derive(Clone, Copy, Debug, PartialEq)]
19pub struct SharpnessFrame {
20 pub t_secs: f64,
21 pub acum: Option<f64>,
22}
23
24#[derive(Clone, Debug, PartialEq)]
25pub struct Sharpness {
26 pub frame_secs: f64,
27 pub window_secs: f64,
28 pub spl_db: f64,
29 pub frames: Vec<SharpnessFrame>,
30}
31
32pub fn measure(planes: &[&[f64]], sr: f64, start: f64, hop_secs: f64, spl_db: f64) -> Sharpness {
33 let pattern = loudness_pattern::measure(planes, sr, start, hop_secs, spl_db);
34 let frames = pattern
35 .frames
36 .iter()
37 .map(|f| SharpnessFrame {
38 t_secs: f.t_secs,
39 acum: f
40 .specific
41 .iter()
42 .any(|n| *n > pattern.threshold_sone)
43 .then(|| acum(&pattern.centres_hz, &f.specific)),
44 })
45 .collect();
46 Sharpness {
47 frame_secs: pattern.frame_secs,
48 window_secs: pattern.window_secs,
49 spl_db,
50 frames,
51 }
52}
53
54fn bark(hz: f64) -> f64 {
55 let z = 26.81 * hz / (1960.0 + hz) - 0.53;
56 match z {
57 z if z < 2.0 => z + 0.15 * (2.0 - z),
58 z if z > 20.1 => z + 0.22 * (z - 20.1),
59 z => z,
60 }
61}
62
63fn acum(centres_hz: &[f64], specific: &[f64]) -> f64 {
64 let moment: f64 = centres_hz
65 .iter()
66 .zip(specific)
67 .map(|(&hz, &n)| {
68 let z = bark(hz);
69 let g = match z > 15.8 {
70 true => 0.15 * libm::exp(0.42 * (z - 15.8)) + 0.85,
71 false => 1.0,
72 };
73 n * g * z
74 })
75 .sum();
76 0.11 * moment / specific.iter().sum::<f64>()
77}