1use crate::media::AudioBuffer;
2use std::f64::consts::PI;
3use std::sync::Arc;
4
5const K: f32 = std::f32::consts::FRAC_1_SQRT_2;
6
7fn stereo_matrix(channels: usize) -> Vec<(f32, f32)> {
8 match channels {
9 1 => vec![(1.0, 1.0)],
10 2 => vec![(1.0, 0.0), (0.0, 1.0)],
11 3 => vec![(1.0, 0.0), (0.0, 1.0), (K, K)],
12 4 => vec![(1.0, 0.0), (0.0, 1.0), (K, K), (0.5, 0.5)],
13 5 => vec![(1.0, 0.0), (0.0, 1.0), (K, K), (K, 0.0), (0.0, K)],
14 6 => vec![
15 (1.0, 0.0),
16 (0.0, 1.0),
17 (K, K),
18 (0.0, 0.0),
19 (K, 0.0),
20 (0.0, K),
21 ],
22 7 => vec![
23 (1.0, 0.0),
24 (0.0, 1.0),
25 (K, K),
26 (0.0, 0.0),
27 (0.5, 0.5),
28 (K, 0.0),
29 (0.0, K),
30 ],
31 8 => vec![
32 (1.0, 0.0),
33 (0.0, 1.0),
34 (K, K),
35 (0.0, 0.0),
36 (K, 0.0),
37 (0.0, K),
38 (K, 0.0),
39 (0.0, K),
40 ],
41 n => {
42 let left = n.div_ceil(2) as f32;
43 let right = (n / 2).max(1) as f32;
44 (0..n)
45 .map(|c| {
46 if c % 2 == 0 {
47 (1.0 / left, 0.0)
48 } else {
49 (0.0, 1.0 / right)
50 }
51 })
52 .collect()
53 }
54 }
55}
56
57pub fn downmix_stereo(audio: &AudioBuffer) -> AudioBuffer {
58 let channels = audio.channels as usize;
59 if channels == 2 || channels == 0 {
60 return audio.clone();
61 }
62 let m = stereo_matrix(channels);
63 let mut out = AudioBuffer::new(audio.rate, 2);
64 out.samples.reserve(audio.frames() * 2);
65 for frame in audio.samples.chunks_exact(channels) {
66 let (mut l, mut r) = (0.0f32, 0.0f32);
67 for (x, (a, b)) in frame.iter().zip(&m) {
68 l += x * a;
69 r += x * b;
70 }
71 out.samples.push(l);
72 out.samples.push(r);
73 }
74 out
75}
76
77pub fn mono(audio: &AudioBuffer) -> AudioBuffer {
78 if audio.channels <= 1 {
79 return audio.clone();
80 }
81 let stereo = downmix_stereo(audio);
82 let mut out = AudioBuffer::new(audio.rate, 1);
83 out.samples = stereo
84 .samples
85 .chunks_exact(2)
86 .map(|f| 0.5 * (f[0] + f[1]))
87 .collect();
88 out
89}
90
91pub fn gain(audio: &mut AudioBuffer, db: f32) {
92 let g = 10f32.powf(db / 20.0);
93 for s in &mut audio.samples {
94 *s *= g;
95 }
96}
97
98pub fn resample(audio: &AudioBuffer, rate: u32) -> AudioBuffer {
99 if audio.rate == rate || audio.channels == 0 {
100 let mut out = audio.clone();
101 out.rate = rate;
102 return out;
103 }
104 let mut r = Resampler::new(audio.rate, rate, audio.channels);
105 let mut out = r.process(audio);
106 out.samples.extend(r.flush().samples);
107 out
108}
109
110const ZERO_CROSSINGS: f64 = 12.0;
111const ROLLOFF: f64 = 0.92;
112const BETA: f64 = 8.6;
113const MAX_PHASES: u64 = 4096;
114const MAX_WEIGHTS: u64 = 1 << 22;
115
116struct Kernel {
117 taps: usize,
118 phases: u64,
119 weights: Vec<f32>,
120}
121
122fn bessel_i0(x: f64) -> f64 {
123 let mut sum = 1.0;
124 let mut term = 1.0;
125 let q = x * x / 4.0;
126 for k in 1..64 {
127 term *= q / (k * k) as f64;
128 sum += term;
129 if term < sum * 1e-17 {
130 break;
131 }
132 }
133 sum
134}
135
136impl Kernel {
137 fn new(from: u32, to: u32, phases: u64) -> Self {
138 let fc = 0.5 * ROLLOFF * (to as f64 / from as f64).clamp(1.0 / 256.0, 1.0);
139 let half = (ZERO_CROSSINGS / (2.0 * fc)).ceil() as usize;
140 let taps = 2 * half;
141 let phases = phases.min(MAX_WEIGHTS / taps as u64).max(1);
142 let norm = bessel_i0(BETA);
143 let mut weights = Vec::with_capacity(taps * phases as usize);
144 for p in 0..phases {
145 let frac = p as f64 / phases as f64;
146 let start = weights.len();
147 for j in 0..taps {
148 let x = j as f64 - (half as f64 - 1.0) - frac;
149 let w = x / (half as f64 + 1.0);
150 let window = if w.abs() >= 1.0 {
151 0.0
152 } else {
153 bessel_i0(BETA * (1.0 - w * w).sqrt()) / norm
154 };
155 let arg = 2.0 * fc * x;
156 let sinc = if arg.abs() < 1e-12 {
157 1.0
158 } else {
159 (PI * arg).sin() / (PI * arg)
160 };
161 weights.push((2.0 * fc * sinc * window) as f32);
162 }
163 let sum: f32 = weights[start..].iter().sum();
164 if sum.abs() > 1e-9 {
165 for w in &mut weights[start..] {
166 *w /= sum;
167 }
168 }
169 }
170 Self {
171 taps,
172 phases,
173 weights,
174 }
175 }
176}
177
178pub struct Resampler {
179 from: u32,
180 to: u32,
181 channels: usize,
182 up: u64,
183 down: u64,
184 kernel: Arc<Kernel>,
185 input: Vec<f32>,
186 base: i64,
187 total_in: u64,
188 next_out: u64,
189}
190
191fn gcd(a: u64, b: u64) -> u64 {
192 if b == 0 { a } else { gcd(b, a % b) }
193}
194
195impl Resampler {
196 pub fn new(from: u32, to: u32, channels: u16) -> Self {
197 let (from, to) = (from.max(1), to.max(1));
198 let g = gcd(from as u64, to as u64);
199 let (up, down) = (to as u64 / g, from as u64 / g);
200 let kernel = Arc::new(Kernel::new(from, to, up.min(MAX_PHASES)));
201 let channels = channels.max(1) as usize;
202 let half = kernel.taps / 2;
203 Self {
204 from,
205 to,
206 channels,
207 up,
208 down,
209 kernel,
210 input: vec![0.0; half * channels],
211 base: -(half as i64),
212 total_in: 0,
213 next_out: 0,
214 }
215 }
216
217 pub fn rates(&self) -> (u32, u32) {
218 (self.from, self.to)
219 }
220
221 pub fn process(&mut self, audio: &AudioBuffer) -> AudioBuffer {
222 let take = audio.samples.len() / self.channels * self.channels;
223 self.input.extend_from_slice(&audio.samples[..take]);
224 self.total_in += (take / self.channels) as u64;
225 self.run(false)
226 }
227
228 pub fn flush(&mut self) -> AudioBuffer {
229 let pad = self.kernel.taps * self.channels;
230 self.input.extend(std::iter::repeat_n(0.0, pad));
231 self.run(true)
232 }
233
234 fn run(&mut self, last: bool) -> AudioBuffer {
235 let ch = self.channels;
236 let taps = self.kernel.taps;
237 let half = (taps / 2) as i64;
238 let available = self.base + (self.input.len() / ch) as i64;
239 let limit = if last {
240 (self.total_in as u128 * self.up as u128).div_ceil(self.down as u128) as u64
241 } else {
242 u64::MAX
243 };
244 let mut out = AudioBuffer::new(self.to, ch as u16);
245 let mut acc = vec![0.0f32; ch];
246 while self.next_out < limit {
247 let pos = self.next_out as u128 * self.down as u128;
248 let i = (pos / self.up as u128) as i64;
249 let rem = (pos % self.up as u128) as u64;
250 let phase = if self.kernel.phases == self.up {
251 rem
252 } else {
253 ((rem as u128 * self.kernel.phases as u128 + self.up as u128 / 2) / self.up as u128)
254 as u64
255 };
256 let (i, phase) = if phase == self.kernel.phases {
257 (i + 1, 0)
258 } else {
259 (i, phase)
260 };
261 let first = i - half + 1;
262 if first + taps as i64 > available {
263 break;
264 }
265 let w = &self.kernel.weights[phase as usize * taps..(phase as usize + 1) * taps];
266 let start = (first - self.base) as usize * ch;
267 acc.fill(0.0);
268 if ch == 1 {
269 let x = &self.input[start..start + taps];
270 acc[0] = x.iter().zip(w).map(|(a, b)| a * b).sum();
271 } else {
272 for (j, wj) in w.iter().enumerate() {
273 let frame = &self.input[start + j * ch..start + (j + 1) * ch];
274 for (a, x) in acc.iter_mut().zip(frame) {
275 *a += x * wj;
276 }
277 }
278 }
279 out.samples.extend_from_slice(&acc);
280 self.next_out += 1;
281 }
282 let pos = self.next_out as u128 * self.down as u128;
283 let keep_from = (pos / self.up as u128) as i64 - half;
284 let drop = (keep_from - self.base).clamp(0, (self.input.len() / ch) as i64) as usize;
285 self.input.drain(..drop * ch);
286 self.base += drop as i64;
287 out
288 }
289}