audio_lab/signal/ops/
time.rs1use crate::signal::{TimeSignal, math::db_to_gain};
2
3impl TimeSignal {
4 pub fn energy_per_channel(&self) -> Vec<f64> {
5 self.channel_iter()
6 .map(|channel| channel.iter().map(|sample| sample * sample).sum())
7 .collect()
8 }
9
10 pub fn power_per_channel(&self) -> Vec<f64> {
11 let num_time_steps = self.num_time_steps();
12 if num_time_steps == 0 {
13 return vec![0.0; self.num_channels()];
14 }
15
16 self.energy_per_channel()
17 .into_iter()
18 .map(|energy| energy / num_time_steps as f64)
19 .collect()
20 }
21
22 pub fn rms_per_channel(&self) -> Vec<f64> {
23 self.power_per_channel()
24 .into_iter()
25 .map(f64::sqrt)
26 .collect()
27 }
28
29 pub fn normalize_peak(&mut self, peak_level: f64) {
30 let max = self.iter().map(|s| s.abs()).fold(0.0_f64, f64::max);
31 if max == 0.0 {
32 return;
33 }
34 let gain = peak_level / max;
35 self.iter_mut().for_each(|s| *s *= gain);
36 }
37
38 pub fn normalize_peak_db(&mut self, peak_db: f64) {
39 self.normalize_peak(db_to_gain(peak_db));
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use approx::assert_abs_diff_eq;
46 use ndarray::arr2;
47
48 use crate::signal::TimeSignal;
49
50 #[test]
51 fn energy_power_and_rms_are_computed_per_channel() {
52 let signal = TimeSignal::new(arr2(&[[1.0, -1.0, 2.0], [0.0, 3.0, 4.0]]), 48_000.0).unwrap();
53
54 assert_eq!(signal.energy_per_channel().len(), 2);
55 assert_abs_diff_eq!(signal.energy_per_channel()[0], 6.0, epsilon = 1e-12);
56 assert_abs_diff_eq!(signal.energy_per_channel()[1], 25.0, epsilon = 1e-12);
57 assert_abs_diff_eq!(signal.power_per_channel()[0], 2.0, epsilon = 1e-12);
58 assert_abs_diff_eq!(signal.power_per_channel()[1], 25.0 / 3.0, epsilon = 1e-12);
59 assert_abs_diff_eq!(signal.rms_per_channel()[0], 2.0_f64.sqrt(), epsilon = 1e-12);
60 assert_abs_diff_eq!(
61 signal.rms_per_channel()[1],
62 (25.0_f64 / 3.0).sqrt(),
63 epsilon = 1e-12
64 );
65 }
66
67 #[test]
68 fn power_and_rms_return_zero_for_empty_signals() {
69 let signal = TimeSignal::zeros(2, 0, 48_000.0).unwrap();
70
71 assert_eq!(signal.energy_per_channel(), vec![0.0, 0.0]);
72 assert_eq!(signal.power_per_channel(), vec![0.0, 0.0]);
73 assert_eq!(signal.rms_per_channel(), vec![0.0, 0.0]);
74 }
75
76 #[test]
77 fn normalize_scales_to_requested_peak_linear() {
78 let mut signal = TimeSignal::new(arr2(&[[0.25, -0.5, 1.0]]), 48_000.0).unwrap();
79 signal.normalize_peak(0.5);
80
81 assert_abs_diff_eq!(signal.channel(0)[0], 0.125, epsilon = 1e-12);
82 assert_abs_diff_eq!(signal.channel(0)[1], -0.25, epsilon = 1e-12);
83 assert_abs_diff_eq!(signal.channel(0)[2], 0.5, epsilon = 1e-12);
84 }
85
86 #[test]
87 fn normalize_scales_to_requested_peak_db() {
88 let mut signal = TimeSignal::new(arr2(&[[0.25, -0.5, 1.0]]), 48_000.0).unwrap();
89 signal.normalize_peak_db(-6.0);
90
91 let expected_peak = 10.0_f64.powf(-6.0 / 20.0);
92 assert_abs_diff_eq!(signal.channel(0)[0], 0.25 * expected_peak, epsilon = 1e-12);
93 assert_abs_diff_eq!(signal.channel(0)[1], -0.5 * expected_peak, epsilon = 1e-12);
94 assert_abs_diff_eq!(signal.channel(0)[2], expected_peak, epsilon = 1e-12);
95 }
96}