Skip to main content

max / audiofiles

1.6 KB · 60 lines History Blame Raw
1 //! Gain adjustment: apply a dB offset with clipping protection.
2
3 /// Apply a gain offset in dB to all samples, clamping to [-1.0, 1.0].
4 pub fn apply_gain(samples: &mut [f32], db: f64) {
5 if samples.is_empty() || db == 0.0 || !db.is_finite() {
6 return;
7 }
8
9 let scale = 10.0_f64.powf(db / 20.0) as f32;
10 for sample in samples.iter_mut() {
11 *sample = (*sample * scale).clamp(-1.0, 1.0);
12 }
13 }
14
15 #[cfg(test)]
16 mod tests {
17 use super::*;
18
19 #[test]
20 fn gain_plus_6db() {
21 let mut samples = vec![0.25, -0.25, 0.1];
22 apply_gain(&mut samples, 6.0);
23 // +6 dB ≈ 2x
24 let scale = 10.0_f64.powf(6.0 / 20.0) as f32;
25 assert!((samples[0] - 0.25 * scale).abs() < 0.001);
26 assert!((samples[1] - (-0.25 * scale)).abs() < 0.001);
27 }
28
29 #[test]
30 fn gain_minus_6db() {
31 let mut samples = vec![1.0, -1.0, 0.5];
32 apply_gain(&mut samples, -6.0);
33 let scale = 10.0_f64.powf(-6.0 / 20.0) as f32;
34 assert!((samples[0] - scale).abs() < 0.001);
35 }
36
37 #[test]
38 fn gain_zero_is_noop() {
39 let original = vec![0.5, -0.5, 0.25];
40 let mut samples = original.clone();
41 apply_gain(&mut samples, 0.0);
42 assert_eq!(samples, original);
43 }
44
45 #[test]
46 fn gain_clips_to_bounds() {
47 let mut samples = vec![0.5, -0.5];
48 apply_gain(&mut samples, 24.0); // +24 dB ≈ 15.85x, will clip
49 assert_eq!(samples[0], 1.0);
50 assert_eq!(samples[1], -1.0);
51 }
52
53 #[test]
54 fn gain_empty() {
55 let mut samples: Vec<f32> = vec![];
56 apply_gain(&mut samples, 6.0);
57 assert!(samples.is_empty());
58 }
59 }
60