sva_analysis/experimental/
gain_reduction.rs1use sva_samples::EnvelopeFrame;
4
5use crate::decibels::to_db;
6
7#[derive(Clone, Copy, Debug, PartialEq)]
8pub struct GainReductionFrame {
9 pub t_secs: f64,
10 pub gain_db: f64,
11 pub input_db: Option<f64>,
12 pub output_db: Option<f64>,
13}
14
15#[derive(Clone, Debug, PartialEq)]
16pub struct GainReduction {
17 pub frames: Vec<GainReductionFrame>,
18}
19
20pub fn analyze(gain: &[EnvelopeFrame], input: Option<&[EnvelopeFrame]>) -> GainReduction {
23 let frames = gain
24 .iter()
25 .enumerate()
26 .map(|(i, g)| {
27 let gain_db = to_db(g.rms);
28 let input_db = input.and_then(|f| f.get(i)).map(|f| to_db(f.rms));
29 GainReductionFrame {
30 t_secs: g.t_secs,
31 gain_db,
32 input_db,
33 output_db: input_db.map(|d| d + gain_db),
34 }
35 })
36 .collect();
37 GainReduction { frames }
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 fn envelope(rms: &[f64]) -> Vec<EnvelopeFrame> {
45 rms.iter()
46 .enumerate()
47 .map(|(i, &r)| EnvelopeFrame {
48 t_secs: i as f64 * 0.01,
49 rms: r,
50 peak: r,
51 })
52 .collect()
53 }
54
55 #[test]
56 fn a_half_gain_node_alone_reads_about_minus_6_db() {
57 let g = analyze(&envelope(&[0.5, 0.5]), None);
58 assert!((g.frames[0].gain_db + 6.0206).abs() < 0.01);
59 assert!(g.frames[0].input_db.is_none());
60 assert!(g.frames[0].output_db.is_none());
61 }
62
63 #[test]
64 fn with_an_input_the_output_is_input_plus_gain_in_db() {
65 let gain = envelope(&[0.5]);
66 let input = envelope(&[1.0]);
67 let g = analyze(&gain, Some(&input));
68 let f = g.frames[0];
69 assert!((f.input_db.unwrap() - 0.0).abs() < 1e-9);
70 assert!((f.gain_db + 6.0206).abs() < 0.01);
71 assert!((f.output_db.unwrap() - f.gain_db).abs() < 1e-9);
72 }
73
74 #[test]
75 fn a_shorter_input_leaves_the_tail_frames_without_a_before_after_reading() {
76 let gain = envelope(&[0.5, 0.5, 0.5]);
77 let input = envelope(&[1.0]);
78 let g = analyze(&gain, Some(&input));
79 assert!(g.frames[0].input_db.is_some());
80 assert!(g.frames[1].input_db.is_none());
81 assert!(g.frames[2].output_db.is_none());
82 }
83}