1use rubato::audioadapter_buffers::direct::SequentialSliceOfVecs;
9use rubato::{
10 Async, FixedAsync, Resampler as RubatoTrait, SincInterpolationParameters, SincInterpolationType, WindowFunction,
11};
12
13use crate::Error;
14
15pub struct Resampler {
17 resampler: Async<f32>,
18 chunk_frames: usize,
19 ratio: f64,
21 delay: usize,
23 started: bool,
26 skip: usize,
29 channels: usize,
30 input_planar: Vec<Vec<f32>>,
31 output_planar: Vec<Vec<f32>>,
32 output_frames_max: usize,
33 pending: Vec<f32>,
34}
35
36impl Resampler {
37 pub fn new(input_rate: u32, output_rate: u32, channels: u32, chunk_frames: usize) -> Result<Self, Error> {
44 if chunk_frames == 0 {
45 return Err(Error::Unsupported("chunk_frames must be > 0".into()));
46 }
47
48 let params = SincInterpolationParameters {
49 sinc_len: 128,
50 f_cutoff: Some(0.95),
51 interpolation: SincInterpolationType::Linear,
52 oversampling_factor: 128,
53 window: WindowFunction::BlackmanHarris2,
54 };
55 let ratio = output_rate as f64 / input_rate as f64;
56 let resampler =
57 Async::<f32>::new_sinc(ratio, 1.0, ¶ms, chunk_frames, channels as usize, FixedAsync::Input)?;
58
59 let delay = resampler.output_delay();
60 let input_planar = (0..channels as usize).map(|_| vec![0.0f32; chunk_frames]).collect();
61 let output_frames_max = resampler.output_frames_max();
62 let output_planar = vec![vec![0.0f32; output_frames_max]; channels as usize];
63
64 Ok(Self {
65 resampler,
66 chunk_frames,
67 ratio,
68 delay,
69 started: false,
70 skip: delay,
71 channels: channels as usize,
72 input_planar,
73 output_planar,
74 output_frames_max,
75 pending: Vec::new(),
76 })
77 }
78
79 pub fn skipped(&self) -> usize {
85 self.delay - self.skip
86 }
87
88 pub fn pending_frames(&self) -> usize {
93 self.pending.len() / self.channels
94 }
95
96 pub fn reset(&mut self) {
103 self.resampler.reset();
104 self.pending.clear();
105 self.skip = self.delay;
106 self.started = false;
107 }
108
109 pub fn flush(mut self) -> Result<Vec<f32>, Error> {
122 if !self.started {
126 return Ok(Vec::new());
127 }
128
129 let pending = self.pending_frames();
130
131 let repaid = self.delay - self.skip;
142 let wanted = ((pending as f64 * self.ratio).round() as usize + repaid) * self.channels;
143
144 let mut out = Vec::new();
145 while out.len() < wanted {
146 let skip_before = self.skip;
151 self.pending.resize(self.chunk_frames * self.channels, 0.0);
152 let produced = self.process(&[])?;
153 if produced.is_empty() && self.skip == skip_before {
154 break;
155 }
156 out.extend_from_slice(&produced);
157 }
158
159 out.truncate(wanted);
160 Ok(out)
161 }
162
163 pub fn process(&mut self, samples: &[f32]) -> Result<Vec<f32>, Error> {
168 if !samples.len().is_multiple_of(self.channels) {
169 return Err(Error::Misaligned {
170 got: samples.len(),
171 expected: samples.len().next_multiple_of(self.channels),
172 });
173 }
174
175 self.started |= !samples.is_empty();
176 self.pending.extend_from_slice(samples);
177
178 let chunk_samples = self.chunk_frames * self.channels;
179 let mut out = Vec::new();
180 while self.pending.len() >= chunk_samples {
181 for (frame_idx, frame) in self.pending[..chunk_samples].chunks_exact(self.channels).enumerate() {
182 for (ch, &sample) in frame.iter().enumerate() {
183 self.input_planar[ch][frame_idx] = sample;
184 }
185 }
186
187 let input = SequentialSliceOfVecs::new(&self.input_planar, self.channels, self.chunk_frames)
188 .expect("resampler input buffer dimensions");
189 let mut output =
190 SequentialSliceOfVecs::new_mut(&mut self.output_planar, self.channels, self.output_frames_max)
191 .expect("resampler output buffer dimensions");
192 let (_, produced) = self.resampler.process_into_buffer(&input, &mut output, None)?;
193
194 let prev_len = out.len();
195 out.resize(prev_len + produced * self.channels, 0.0);
196 for frame_idx in 0..produced {
197 for ch in 0..self.channels {
198 out[prev_len + frame_idx * self.channels + ch] = self.output_planar[ch][frame_idx];
199 }
200 }
201
202 self.pending.drain(..chunk_samples);
203 }
204
205 if self.skip > 0 {
209 let drop = self.skip.min(out.len() / self.channels) * self.channels;
210 out.drain(..drop);
211 self.skip -= drop / self.channels;
212 }
213
214 Ok(out)
215 }
216}
217
218pub(crate) fn validate_channels(count: u32) -> Result<(), Error> {
221 match count {
222 1 | 2 => Ok(()),
223 other => Err(Error::Unsupported(format!(
224 "channel remix only supports mono and stereo (got {other})"
225 ))),
226 }
227}
228
229pub(crate) fn remix(samples: &[f32], input_channels: u32, output_channels: u32) -> Result<Vec<f32>, Error> {
231 match (input_channels, output_channels) {
232 (1, 1) | (2, 2) => Ok(samples.to_vec()),
233 (1, 2) => {
234 let mut output = Vec::with_capacity(samples.len() * 2);
235 for &sample in samples {
236 output.extend_from_slice(&[sample, sample]);
237 }
238 Ok(output)
239 }
240 (2, 1) => Ok(samples.chunks_exact(2).map(|pair| (pair[0] + pair[1]) * 0.5).collect()),
241 _ => Err(Error::Unsupported(format!(
242 "channel remix only supports mono and stereo (got {input_channels} to {output_channels})"
243 ))),
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn rejects_zero_chunk_frames() {
253 let r = Resampler::new(48_000, 48_000, 2, 0);
254 assert!(matches!(r, Err(Error::Unsupported(_))));
255 }
256
257 #[test]
258 fn upsample_44100_to_48000_preserves_energy_roughly() {
259 let mut r = Resampler::new(44_100, 48_000, 1, 1024).unwrap();
260 let input: Vec<f32> = (0..44_100)
261 .map(|i| (2.0 * std::f32::consts::PI * 440.0 * i as f32 / 44_100.0).sin() * 0.5)
262 .collect();
263 let mut out = r.process(&input).unwrap();
264 out.extend(r.process(&vec![0.0; 1024]).unwrap());
265 assert!(
266 (47_000..50_000).contains(&out.len()),
267 "expected ~48k samples, got {}",
268 out.len()
269 );
270 }
271
272 #[test]
277 fn flush_drains_the_delayed_tail() {
278 let mut r = Resampler::new(44_100, 48_000, 1, 882).unwrap();
279
280 let mut input = vec![0.0f32; 1024];
285 input[1000] = 1.0;
286
287 let body = r.process(&input).unwrap();
288 let tail = r.flush().unwrap();
289
290 let peak = |samples: &[f32]| samples.iter().fold(0.0f32, |max, s| max.max(s.abs()));
291 assert!(peak(&body) < 0.01, "the sample emerged early: peak {}", peak(&body));
292 assert!(peak(&tail) > 0.5, "the tail lost the sample: peak {}", peak(&tail));
293 }
294
295 #[test]
300 fn flush_drains_on_an_exact_chunk_boundary() {
301 let mut r = Resampler::new(44_100, 48_000, 1, 882).unwrap();
302
303 let mut input = vec![0.0f32; 1764];
305 input[1750] = 1.0;
306
307 let body = r.process(&input).unwrap();
308 assert_eq!(r.pending_frames(), 0, "the input should divide evenly");
309
310 let tail = r.flush().unwrap();
311
312 let peak = |samples: &[f32]| samples.iter().fold(0.0f32, |max, s| max.max(s.abs()));
315 assert!(peak(&body) < 0.01, "the sample emerged early: peak {}", peak(&body));
316 assert!(peak(&tail) > 0.5, "the tail lost the sample: peak {}", peak(&tail));
317 }
318
319 #[test]
324 fn flush_sizes_a_stream_shorter_than_a_chunk() {
325 let mut r = Resampler::new(44_100, 48_000, 1, 882).unwrap();
326
327 let body = r.process(&[0.25f32; 441]).unwrap();
328 let tail = r.flush().unwrap();
329
330 let total = body.len() + tail.len();
332 assert!((475..=485).contains(&total), "unexpected total: {total}");
333 }
334
335 #[test]
340 fn flush_survives_a_chunk_smaller_than_the_delay() {
341 let mut r = Resampler::new(44_100, 48_000, 1, 32).unwrap();
342
343 let body = r.process(&[0.5f32; 20]).unwrap();
344 let tail = r.flush().unwrap();
345
346 let total = body.len() + tail.len();
347 assert!((18..=26).contains(&total), "unexpected total: {total}");
348 assert!(
349 tail.iter().any(|s| s.abs() > 0.25),
350 "the stream came back silent: peak {}",
351 tail.iter().fold(0.0f32, |m, s| m.max(s.abs()))
352 );
353 }
354
355 #[test]
356 fn remix_mono_to_stereo_duplicates_samples() {
357 assert_eq!(remix(&[1.0, 2.0], 1, 2).unwrap(), [1.0, 1.0, 2.0, 2.0]);
358 }
359
360 #[test]
361 fn remix_stereo_to_mono_averages_channels() {
362 assert_eq!(remix(&[1.0, 3.0, 2.0, 4.0], 2, 1).unwrap(), [2.0, 3.0]);
363 }
364}