1use crate::media::{AudioBuffer, Error, Result};
2
3const H: f32 = std::f32::consts::FRAC_1_SQRT_2;
4
5fn stereo_weights(channels: usize) -> Vec<[f32; 2]> {
6 let side = |left: bool, g: f32| if left { [g, 0.0] } else { [0.0, g] };
7 match channels {
8 1 => vec![[1.0, 1.0]],
9 2 => vec![[1.0, 0.0], [0.0, 1.0]],
10 3 => vec![[1.0, 0.0], [0.0, 1.0], [H, H]],
11 4 => vec![[1.0, 0.0], [0.0, 1.0], [H, 0.0], [0.0, H]],
12 5 => vec![[1.0, 0.0], [0.0, 1.0], [H, H], [H, 0.0], [0.0, H]],
13 6 => vec![
14 [1.0, 0.0],
15 [0.0, 1.0],
16 [H, H],
17 [0.0, 0.0],
18 [H, 0.0],
19 [0.0, H],
20 ],
21 7 => vec![
22 [1.0, 0.0],
23 [0.0, 1.0],
24 [H, H],
25 [0.0, 0.0],
26 [0.5, 0.5],
27 [H, 0.0],
28 [0.0, H],
29 ],
30 8 => vec![
31 [1.0, 0.0],
32 [0.0, 1.0],
33 [H, H],
34 [0.0, 0.0],
35 [H, 0.0],
36 [0.0, H],
37 [H, 0.0],
38 [0.0, H],
39 ],
40 n => (0..n)
41 .map(|i| match i {
42 0 => [1.0, 0.0],
43 1 => [0.0, 1.0],
44 _ => side(i % 2 == 0, H),
45 })
46 .collect(),
47 }
48}
49
50pub fn downmix(input: &AudioBuffer, channels: u16) -> Result<AudioBuffer> {
51 let from = input.channels as usize;
52 if from == 0 {
53 return Err(Error::Invalid("audio with no channels".into()));
54 }
55 let mut out = AudioBuffer::new(input.rate, channels);
56 if from == channels as usize {
57 out.samples = input.samples.clone();
58 return Ok(out);
59 }
60 let mut w = stereo_weights(from);
61 let norm = |w: &[[f32; 2]], k: usize| w.iter().map(|c| c[k].abs()).sum::<f32>().max(1.0);
62 let (nl, nr) = (norm(&w, 0), norm(&w, 1));
63 for c in w.iter_mut() {
64 c[0] /= nl;
65 c[1] /= nr;
66 }
67 let frames = input.frames();
68 match channels {
69 2 => {
70 out.samples.resize(frames * 2, 0.0);
71 for (o, f) in out
72 .samples
73 .chunks_exact_mut(2)
74 .zip(input.samples.chunks_exact(from))
75 {
76 let (mut l, mut r) = (0.0, 0.0);
77 for (s, c) in f.iter().zip(&w) {
78 l += s * c[0];
79 r += s * c[1];
80 }
81 o[0] = l;
82 o[1] = r;
83 }
84 }
85 1 => {
86 let m: Vec<f32> = w.iter().map(|c| 0.5 * (c[0] + c[1])).collect();
87 out.samples.resize(frames, 0.0);
88 for (o, f) in out.samples.iter_mut().zip(input.samples.chunks_exact(from)) {
89 *o = f.iter().zip(&m).map(|(s, c)| s * c).sum();
90 }
91 }
92 n => {
93 return Err(Error::Unsupported(format!(
94 "downmix to {n} channels; only mono and stereo"
95 )));
96 }
97 }
98 Ok(out)
99}
100
101pub fn gain(buffer: &mut AudioBuffer, db: f32) {
102 let g = 10f32.powf(db / 20.0);
103 buffer.samples.iter_mut().for_each(|s| *s *= g);
104}