Skip to main content

pito_codecs/aac/
pcm.rs

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}