1fn bounds(width: usize) -> (f64, f64) {
15 match width {
16 1 => (-128.0, 127.0),
17 2 => (-32768.0, 32767.0),
18 4 => (-2147483648.0, 2147483647.0),
19 _ => panic!("audioop: unsupported sample width {width}"),
20 }
21}
22
23fn int_bounds(width: usize) -> (i64, i64) {
24 match width {
25 1 => (-128, 127),
26 2 => (-32768, 32767),
27 4 => (i32::MIN as i64, i32::MAX as i64),
28 _ => panic!("audioop: unsupported sample width {width}"),
29 }
30}
31
32fn fbound(val: f64, minval: f64, maxval: f64) -> f64 {
34 if val > maxval {
35 maxval
36 } else if val < minval + 1.0 {
37 minval
38 } else {
39 val
40 }
41}
42
43pub fn get_raw(data: &[u8], width: usize, pos: usize) -> i32 {
45 match width {
46 1 => data[pos] as i8 as i32,
47 2 => i16::from_le_bytes([data[pos], data[pos + 1]]) as i32,
48 4 => i32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]),
49 _ => panic!("audioop: unsupported sample width {width}"),
50 }
51}
52
53pub fn put_raw(out: &mut Vec<u8>, width: usize, val: i32) {
55 match width {
56 1 => out.push(val as i8 as u8),
57 2 => out.extend_from_slice(&(val as i16).to_le_bytes()),
58 4 => out.extend_from_slice(&val.to_le_bytes()),
59 _ => panic!("audioop: unsupported sample width {width}"),
60 }
61}
62
63fn get_sample32(data: &[u8], width: usize, pos: usize) -> i32 {
65 match width {
66 1 => (data[pos] as i8 as i32) << 24,
67 2 => (i16::from_le_bytes([data[pos], data[pos + 1]]) as i32) << 16,
68 4 => get_raw(data, 4, pos),
69 _ => panic!("audioop: unsupported sample width {width}"),
70 }
71}
72
73fn put_sample32(out: &mut Vec<u8>, width: usize, val: i32) {
75 match width {
76 1 => out.push((val >> 24) as i8 as u8),
77 2 => out.extend_from_slice(&((val >> 16) as i16).to_le_bytes()),
78 4 => out.extend_from_slice(&val.to_le_bytes()),
79 _ => panic!("audioop: unsupported sample width {width}"),
80 }
81}
82
83fn check_len(len: usize, width: usize) {
84 assert!(
85 len % width == 0,
86 "audioop.error: not a whole number of frames"
87 );
88}
89
90pub fn mul(data: &[u8], width: usize, factor: f64) -> Vec<u8> {
92 check_len(data.len(), width);
93 let (lo, hi) = bounds(width);
94 let mut out = Vec::with_capacity(data.len());
95 for pos in (0..data.len()).step_by(width) {
96 let val = get_raw(data, width, pos) as f64;
97 put_raw(&mut out, width, fbound(val * factor, lo, hi).floor() as i32);
98 }
99 out
100}
101
102pub fn add(a: &[u8], b: &[u8], width: usize) -> Vec<u8> {
104 check_len(a.len(), width);
105 assert_eq!(
106 a.len(),
107 b.len(),
108 "audioop.error: Lengths should be the same"
109 );
110 let (lo, hi) = int_bounds(width);
111 let mut out = Vec::with_capacity(a.len());
112 for pos in (0..a.len()).step_by(width) {
113 let v = get_raw(a, width, pos) as i64 + get_raw(b, width, pos) as i64;
114 put_raw(&mut out, width, v.clamp(lo, hi) as i32);
115 }
116 out
117}
118
119pub fn add_into(a: &mut [u8], b: &[u8], width: usize) {
122 assert!(a.len() >= b.len());
123 let (lo, hi) = int_bounds(width);
124 match width {
125 2 => {
126 for (x, y) in a.chunks_exact_mut(2).zip(b.chunks_exact(2)) {
127 let v = i16::from_le_bytes([x[0], x[1]]) as i32
128 + i16::from_le_bytes([y[0], y[1]]) as i32;
129 x.copy_from_slice(&(v.clamp(lo as i32, hi as i32) as i16).to_le_bytes());
130 }
131 }
132 _ => {
133 for pos in (0..b.len()).step_by(width) {
134 let v = get_raw(a, width, pos) as i64 + get_raw(b, width, pos) as i64;
135 let mut buf = Vec::with_capacity(width);
136 put_raw(&mut buf, width, v.clamp(lo, hi) as i32);
137 a[pos..pos + width].copy_from_slice(&buf);
138 }
139 }
140 }
141}
142
143pub fn tostereo(data: &[u8], width: usize, lfactor: f64, rfactor: f64) -> Vec<u8> {
145 check_len(data.len(), width);
146 let (lo, hi) = bounds(width);
147 let mut out = Vec::with_capacity(data.len() * 2);
148 for pos in (0..data.len()).step_by(width) {
149 let val = get_raw(data, width, pos) as f64;
150 put_raw(
151 &mut out,
152 width,
153 fbound(val * lfactor, lo, hi).floor() as i32,
154 );
155 put_raw(
156 &mut out,
157 width,
158 fbound(val * rfactor, lo, hi).floor() as i32,
159 );
160 }
161 out
162}
163
164pub fn tomono(data: &[u8], width: usize, lfactor: f64, rfactor: f64) -> Vec<u8> {
166 check_len(data.len(), width * 2);
167 let (lo, hi) = bounds(width);
168 let mut out = Vec::with_capacity(data.len() / 2);
169 for pos in (0..data.len()).step_by(width * 2) {
170 let v1 = get_raw(data, width, pos) as f64;
171 let v2 = get_raw(data, width, pos + width) as f64;
172 let val = v1 * lfactor + v2 * rfactor;
173 put_raw(&mut out, width, fbound(val, lo, hi).floor() as i32);
174 }
175 out
176}
177
178pub fn lin2lin(data: &[u8], width: usize, newwidth: usize) -> Vec<u8> {
180 check_len(data.len(), width);
181 if width == newwidth {
182 return data.to_vec();
183 }
184 let mut out = Vec::with_capacity(data.len() / width * newwidth);
185 for pos in (0..data.len()).step_by(width) {
186 put_sample32(&mut out, newwidth, get_sample32(data, width, pos));
187 }
188 out
189}
190
191fn gcd(mut a: i64, mut b: i64) -> i64 {
192 while b > 0 {
193 let t = a % b;
194 a = b;
195 b = t;
196 }
197 a
198}
199
200pub fn ratecv(data: &[u8], width: usize, nchannels: usize, inrate: u32, outrate: u32) -> Vec<u8> {
204 let bytes_per_frame = width * nchannels;
205 assert!(
206 data.len() % bytes_per_frame == 0,
207 "audioop.error: not a whole number of frames"
208 );
209 let (mut inrate, mut outrate) = (inrate as i64, outrate as i64);
210 let d0 = gcd(inrate, outrate);
211 inrate /= d0;
212 outrate /= d0;
213 let (weight_a, weight_b) = (1.0f64, 0.0f64);
215
216 let mut prev_i = vec![0i32; nchannels];
217 let mut cur_i = vec![0i32; nchannels];
218 let mut len = (data.len() / bytes_per_frame) as i64;
219 let mut d = -outrate;
220 let capacity = if len == 0 {
221 0
222 } else {
223 ((1 + (len - 1) / inrate) * outrate) as usize * bytes_per_frame
224 };
225 let mut out = Vec::with_capacity(capacity);
226 let mut cp = 0usize;
227
228 loop {
229 while d < 0 {
230 if len == 0 {
231 return out;
232 }
233 for chan in 0..nchannels {
234 prev_i[chan] = cur_i[chan];
235 cur_i[chan] = get_sample32(data, width, cp);
236 cp += width;
237 cur_i[chan] = ((weight_a * cur_i[chan] as f64 + weight_b * prev_i[chan] as f64)
240 / (weight_a + weight_b)) as i32;
241 }
242 len -= 1;
243 d += outrate;
244 }
245 while d >= 0 {
246 for chan in 0..nchannels {
247 let cur_o = ((prev_i[chan] as f64 * d as f64
248 + cur_i[chan] as f64 * (outrate - d) as f64)
249 / outrate as f64) as i32;
250 put_sample32(&mut out, width, cur_o);
251 }
252 d -= inrate;
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 fn s(v: &[i16]) -> Vec<u8> {
262 v.iter().flat_map(|x| x.to_le_bytes()).collect()
263 }
264 fn u(b: &[u8]) -> Vec<i16> {
265 b.chunks_exact(2)
266 .map(|c| i16::from_le_bytes([c[0], c[1]]))
267 .collect()
268 }
269
270 #[test]
273 fn mul_floors_and_saturates() {
274 assert_eq!(
275 u(&mul(&s(&[3, -3, 1, -1, 32767, -32768, 100, -100]), 2, 0.5)),
276 [1, -2, 0, -1, 16383, -16384, 50, -50]
277 );
278 assert_eq!(
279 u(&mul(&s(&[3, -3, 32767, -32768, -21846, 21845]), 2, 1.5)),
280 [4, -5, 32767, -32768, -32768, 32767]
281 );
282 assert_eq!(
283 u(&mul(&s(&[-32767, -32768, 32767]), 2, 1.00001)),
284 [-32768, -32768, 32767]
285 );
286 assert_eq!(u(&mul(&s(&[5, -5]), 2, -0.3)), [-2, 1]);
287 }
288
289 #[test]
290 fn add_saturates() {
291 assert_eq!(
292 u(&add(
293 &s(&[30000, -30000, 1, -32768]),
294 &s(&[30000, -30000, -1, -1]),
295 2
296 )),
297 [32767, -32768, 0, -32768]
298 );
299 let mut a = s(&[30000, -30000, 1, -32768, 7]);
300 add_into(&mut a, &s(&[30000, -30000, -1, -1]), 2);
301 assert_eq!(u(&a), [32767, -32768, 0, -32768, 7]);
302 }
303
304 #[test]
305 fn channel_conversions_floor() {
306 assert_eq!(
307 u(&tostereo(&s(&[3, -3, 32767, -32768]), 2, 0.5, 1.0)),
308 [1, 3, -2, -3, 16383, 32767, -16384, -32768]
309 );
310 assert_eq!(
311 u(&tomono(
312 &s(&[3, 4, -3, -4, 32767, 32767, -32768, -32768, 1, -2]),
313 2,
314 0.5,
315 0.5
316 )),
317 [3, -4, 32767, -32768, -1]
318 );
319 }
320
321 #[test]
322 fn lin2lin_scales_by_shift() {
323 assert!(lin2lin(&[], 1, 2).is_empty());
324 assert_eq!(lin2lin(&[1, 255], 1, 2), vec![0x00, 0x01, 0x00, 0xff]);
325 }
326
327 fn ramp(n: usize) -> Vec<u8> {
328 s(&(0..n)
329 .map(|i| ((i * 1237) % 65536) as i64 - 32768)
330 .map(|v| v as i16)
331 .collect::<Vec<_>>())
332 }
333
334 #[test]
335 fn ratecv_matches_cpython_lengths_and_values() {
336 let cases: &[(usize, u32, u32, usize, &[i16])] = &[
337 (
338 1,
339 11025,
340 44100,
341 145,
342 &[
343 -32768, -32459, -32150, -31841, -31531, -31222, -30913, -30604, -30294, -29985,
344 -29676, -29367,
345 ],
346 ),
347 (
348 1,
349 44100,
350 48000,
351 40,
352 &[
353 -32768, -31632, -30496, -29359, -28223, -27086, -25950, -24813, -23677, -22540,
354 -21404, -20267,
355 ],
356 ),
357 (
358 2,
359 44100,
360 48000,
361 80,
362 &[
363 -32768, -31531, -30496, -29259, -28223, -26986, -25950, -24713, -23677, -22440,
364 -21404, -20167,
365 ],
366 ),
367 (
368 1,
369 48000,
370 44100,
371 34,
372 &[
373 -32768, -31422, -30076, -28729, -27383, -26037, -24690, -23344, -21997, -20651,
374 -19305, -17958,
375 ],
376 ),
377 (
378 2,
379 22050,
380 44100,
381 146,
382 &[
383 -32768, -31531, -31531, -30294, -30294, -29057, -29057, -27820, -27820, -26583,
384 -26583, -25346,
385 ],
386 ),
387 (
388 1,
389 8000,
390 44100,
391 199,
392 &[
393 -32768, -32544, -32320, -32095, -31871, -31647, -31422, -31198, -30973, -30749,
394 -30525, -30300,
395 ],
396 ),
397 ];
398 for &(ch, a, b, out_len, head) in cases {
399 let out = u(&ratecv(&ramp(ch * 37), 2, ch, a, b));
400 assert_eq!(out.len(), out_len, "{ch}ch {a}->{b}");
401 assert_eq!(&out[..12], head, "{ch}ch {a}->{b}");
402 }
403 assert!(ratecv(&[], 2, 1, 11025, 44100).is_empty());
404 }
405}