1#![allow(clippy::needless_range_loop)]
38
39use alloc::vec::Vec;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum Quality {
44 Linear,
48 HighQuality,
51}
52
53#[derive(Debug)]
55#[non_exhaustive]
56pub enum ResampleError {
57 Rubato(rubato::ResampleError),
59 Construction(rubato::ResamplerConstructionError),
61 UnalignedBuffer {
63 samples: usize,
65 channels: usize,
67 },
68}
69
70impl core::fmt::Display for ResampleError {
71 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
72 match self {
73 ResampleError::Rubato(e) => write!(f, "rubato: {e}"),
74 ResampleError::Construction(e) => write!(f, "rubato construction: {e}"),
75 ResampleError::UnalignedBuffer { samples, channels } => write!(
76 f,
77 "buffer length {samples} is not a multiple of channel count {channels}"
78 ),
79 }
80 }
81}
82
83impl std::error::Error for ResampleError {}
84
85impl From<rubato::ResampleError> for ResampleError {
86 fn from(e: rubato::ResampleError) -> Self {
87 ResampleError::Rubato(e)
88 }
89}
90impl From<rubato::ResamplerConstructionError> for ResampleError {
91 fn from(e: rubato::ResamplerConstructionError) -> Self {
92 ResampleError::Construction(e)
93 }
94}
95
96enum Inner {
99 Linear(rubato::FastFixedIn<f32>),
100 Sinc(rubato::SincFixedIn<f32>),
101}
102
103impl Inner {
104 fn input_frames_next(&self) -> usize {
105 use rubato::Resampler;
106 match self {
107 Inner::Linear(r) => r.input_frames_next(),
108 Inner::Sinc(r) => r.input_frames_next(),
109 }
110 }
111 fn output_frames_next(&self) -> usize {
112 use rubato::Resampler;
113 match self {
114 Inner::Linear(r) => r.output_frames_next(),
115 Inner::Sinc(r) => r.output_frames_next(),
116 }
117 }
118 fn process_into_buffer(
119 &mut self,
120 input: &[&[f32]],
121 output: &mut [&mut [f32]],
122 ) -> Result<(usize, usize), rubato::ResampleError> {
123 use rubato::Resampler;
124 match self {
125 Inner::Linear(r) => r.process_into_buffer(input, output, None),
126 Inner::Sinc(r) => r.process_into_buffer(input, output, None),
127 }
128 }
129}
130
131pub struct Resampler {
134 inner: Inner,
135 in_rate: u32,
136 out_rate: u32,
137 channels: usize,
138 pending: Vec<Vec<f32>>,
141}
142
143impl core::fmt::Debug for Resampler {
144 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
145 f.debug_struct("Resampler")
146 .field("in_rate", &self.in_rate)
147 .field("out_rate", &self.out_rate)
148 .field("channels", &self.channels)
149 .finish_non_exhaustive()
150 }
151}
152
153impl Resampler {
154 pub fn new(
162 in_rate: u32,
163 out_rate: u32,
164 channels: usize,
165 quality: Quality,
166 ) -> Result<Self, ResampleError> {
167 let ratio = out_rate as f64 / in_rate as f64;
168 let chunk_size = 1024;
169 let inner = match quality {
170 Quality::Linear => {
171 use rubato::{FastFixedIn, PolynomialDegree};
172 Inner::Linear(FastFixedIn::new(
173 ratio,
174 1.0,
175 PolynomialDegree::Linear,
176 chunk_size,
177 channels,
178 )?)
179 }
180 Quality::HighQuality => {
181 use rubato::{
182 SincFixedIn, SincInterpolationParameters, SincInterpolationType, WindowFunction,
183 };
184 let params = SincInterpolationParameters {
185 sinc_len: 128,
186 f_cutoff: 0.95,
187 interpolation: SincInterpolationType::Quadratic,
188 oversampling_factor: 256,
189 window: WindowFunction::Blackman2,
190 };
191 Inner::Sinc(SincFixedIn::new(ratio, 1.0, params, chunk_size, channels)?)
192 }
193 };
194 Ok(Self {
195 inner,
196 in_rate,
197 out_rate,
198 channels,
199 pending: (0..channels).map(|_| Vec::new()).collect(),
200 })
201 }
202
203 pub fn process_interleaved(&mut self, input: &[f32]) -> Result<Vec<f32>, ResampleError> {
210 if self.channels == 0 {
211 return Ok(Vec::new());
212 }
213 if input.len() % self.channels != 0 {
214 return Err(ResampleError::UnalignedBuffer {
215 samples: input.len(),
216 channels: self.channels,
217 });
218 }
219 let frames = input.len() / self.channels;
221 for f in 0..frames {
222 for c in 0..self.channels {
223 self.pending[c].push(input[f * self.channels + c]);
224 }
225 }
226 let mut output_frames: Vec<Vec<f32>> = (0..self.channels).map(|_| Vec::new()).collect();
227
228 loop {
230 let needed = self.inner.input_frames_next();
231 if self.pending[0].len() < needed {
232 break;
233 }
234 let chunks: Vec<&[f32]> = (0..self.channels)
236 .map(|c| &self.pending[c][..needed])
237 .collect();
238 let mut out: Vec<Vec<f32>> = (0..self.channels)
239 .map(|_| Vec::with_capacity(self.inner.output_frames_next()))
240 .collect();
241 for buf in &mut out {
242 buf.resize(self.inner.output_frames_next(), 0.0);
243 }
244 let mut out_slices: Vec<&mut [f32]> =
245 out.iter_mut().map(|v| v.as_mut_slice()).collect();
246 let (consumed_in, produced_out) =
247 self.inner.process_into_buffer(&chunks, &mut out_slices)?;
248 for c in 0..self.channels {
250 self.pending[c].drain(..consumed_in);
251 output_frames[c].extend_from_slice(&out[c][..produced_out]);
252 }
253 }
254
255 let frames_out = output_frames[0].len();
257 let mut out = Vec::with_capacity(frames_out * self.channels);
258 for f in 0..frames_out {
259 for c in 0..self.channels {
260 out.push(output_frames[c][f]);
261 }
262 }
263 Ok(out)
264 }
265
266 #[must_use]
268 pub fn input_rate(&self) -> u32 {
269 self.in_rate
270 }
271 #[must_use]
273 pub fn output_rate(&self) -> u32 {
274 self.out_rate
275 }
276 #[must_use]
278 pub fn channels(&self) -> usize {
279 self.channels
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn upsample_8k_to_48k_preserves_signal_length_approximately() {
289 let mut r = Resampler::new(8_000, 48_000, 2, Quality::Linear).unwrap();
290 let input = vec![0.1f32; 16_000];
292 let out = r.process_interleaved(&input).unwrap();
293 assert!(
297 out.len() > 80_000 && out.len() < 100_000,
298 "got {} output samples, expected near 96000",
299 out.len()
300 );
301 }
302
303 #[test]
304 fn highquality_path_works_at_44_1_to_48() {
305 let mut r = Resampler::new(44_100, 48_000, 2, Quality::HighQuality).unwrap();
306 let n = 22_050usize;
308 let omega = 2.0 * std::f32::consts::PI * 1000.0 / 44_100.0;
309 let mut input = Vec::with_capacity(n * 2);
310 for i in 0..n {
311 let v = 0.5 * (omega * i as f32).sin();
312 input.push(v);
313 input.push(v);
314 }
315 let out = r.process_interleaved(&input).unwrap();
316 assert!(!out.is_empty());
317 }
318
319 #[test]
320 fn unaligned_buffer_rejected() {
321 let mut r = Resampler::new(48_000, 48_000, 2, Quality::Linear).unwrap();
322 let err = r.process_interleaved(&[0.0, 0.0, 0.0]).unwrap_err();
323 assert!(matches!(err, ResampleError::UnalignedBuffer { .. }));
324 }
325}