1use rubato::Resampler as _;
4use rubato::audioadapter_buffers::direct::InterleavedSlice;
5use rubato::{Fft, FixedSync};
6
7use super::CANONICAL_SAMPLE_RATE;
8
9const INPUT_CHUNK: usize = 1_024;
10
11pub struct Resampler {
13 mode: Mode,
14}
15
16enum Mode {
17 Passthrough,
18 Fft(Box<FftState>),
19}
20
21impl Resampler {
22 pub fn new(sample_rate: u32) -> Result<Self, ResampleError> {
23 let mode = if sample_rate == CANONICAL_SAMPLE_RATE {
24 Mode::Passthrough
25 } else {
26 Mode::Fft(Box::new(FftState::new(sample_rate)?))
27 };
28 Ok(Self { mode })
29 }
30
31 pub fn convert(sample_rate: u32, samples: &[f32]) -> Result<Vec<f32>, ResampleError> {
33 let mut resampler = Self::new(sample_rate)?;
34 let mut converted = Vec::new();
35 resampler.push(samples, |block| converted.extend_from_slice(block))?;
36 resampler.finish(|block| converted.extend_from_slice(block))?;
37 Ok(converted)
38 }
39
40 pub fn push(
42 &mut self,
43 samples: &[f32],
44 mut emit: impl FnMut(&[f32]),
45 ) -> Result<(), ResampleError> {
46 match &mut self.mode {
47 Mode::Passthrough => {
48 emit(samples);
49 Ok(())
50 }
51 Mode::Fft(state) => state.push(samples, &mut emit),
52 }
53 }
54
55 pub fn finish(mut self, mut emit: impl FnMut(&[f32])) -> Result<(), ResampleError> {
58 match &mut self.mode {
59 Mode::Passthrough => Ok(()),
60 Mode::Fft(state) => state.flush(&mut emit),
61 }
62 }
63}
64
65struct FftState {
66 fft: Fft<f32>,
67 sample_rate: u32,
68 chunk: Vec<f32>,
69 filled: usize,
70 output: Vec<f32>,
71 delay: usize,
72 samples_in: u64,
73 samples_out: u64,
74}
75
76impl FftState {
77 fn new(sample_rate: u32) -> Result<Self, ResampleError> {
78 let fft = Fft::<f32>::new(
79 sample_rate as usize,
80 CANONICAL_SAMPLE_RATE as usize,
81 INPUT_CHUNK,
82 1,
83 FixedSync::Input,
84 )
85 .map_err(ResampleError::initialize)?;
86 let chunk = vec![0.0; fft.input_frames_next()];
87 let output = vec![0.0; fft.output_frames_max()];
88 let delay = fft.output_delay();
89 Ok(Self {
90 fft,
91 sample_rate,
92 chunk,
93 filled: 0,
94 output,
95 delay,
96 samples_in: 0,
97 samples_out: 0,
98 })
99 }
100
101 fn push(
102 &mut self,
103 mut samples: &[f32],
104 emit: &mut impl FnMut(&[f32]),
105 ) -> Result<(), ResampleError> {
106 while !samples.is_empty() {
107 let count = samples.len().min(self.chunk.len() - self.filled);
108 self.chunk[self.filled..self.filled + count].copy_from_slice(&samples[..count]);
109 self.filled += count;
110 self.samples_in = self
111 .samples_in
112 .checked_add(count as u64)
113 .ok_or(ResampleError::SampleCountOverflow)?;
114 samples = &samples[count..];
115 if self.filled == self.chunk.len() {
116 self.process(emit)?;
117 }
118 }
119 Ok(())
120 }
121
122 fn flush(&mut self, emit: &mut impl FnMut(&[f32])) -> Result<(), ResampleError> {
124 while self.samples_out < self.target_samples() {
125 self.chunk[self.filled..].fill(0.0);
126 self.process(emit)?;
127 }
128 Ok(())
129 }
130
131 fn target_samples(&self) -> u64 {
132 (u128::from(self.samples_in) * u128::from(CANONICAL_SAMPLE_RATE)
133 / u128::from(self.sample_rate)) as u64
134 }
135
136 fn process(&mut self, emit: &mut impl FnMut(&[f32])) -> Result<(), ResampleError> {
137 let input = InterleavedSlice::new(&self.chunk, 1, self.chunk.len())
138 .map_err(ResampleError::process)?;
139 let output_capacity = self.output.len();
140 let mut output = InterleavedSlice::new_mut(&mut self.output, 1, output_capacity)
141 .map_err(ResampleError::process)?;
142 let (_, produced) = self
143 .fft
144 .process_into_buffer(&input, &mut output, None)
145 .map_err(ResampleError::process)?;
146 self.filled = 0;
147 let skipped = self.delay.min(produced);
148 self.delay -= skipped;
149 let count = (produced - skipped).min((self.target_samples() - self.samples_out) as usize);
151 emit(&self.output[skipped..skipped + count]);
152 self.samples_out += count as u64;
153 Ok(())
154 }
155}
156
157#[derive(Debug, thiserror::Error)]
158pub enum ResampleError {
159 #[error("failed to set up resampling to {CANONICAL_SAMPLE_RATE} Hz")]
160 Initialize(#[source] Box<dyn std::error::Error + Send + Sync>),
161
162 #[error("failed to resample audio to {CANONICAL_SAMPLE_RATE} Hz")]
163 Process(#[source] Box<dyn std::error::Error + Send + Sync>),
164
165 #[error("resampler input sample count overflowed")]
166 SampleCountOverflow,
167}
168
169impl ResampleError {
170 fn initialize(error: impl std::error::Error + Send + Sync + 'static) -> Self {
171 Self::Initialize(Box::new(error))
172 }
173
174 fn process(error: impl std::error::Error + Send + Sync + 'static) -> Self {
175 Self::Process(Box::new(error))
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 fn convert(rate: u32, input: &[f32], chunk: usize) -> Vec<f32> {
184 let mut resampler = Resampler::new(rate).unwrap();
185 let mut output = Vec::new();
186 for samples in input.chunks(chunk) {
187 resampler
188 .push(samples, |block| output.extend_from_slice(block))
189 .unwrap();
190 }
191 resampler
192 .finish(|block| output.extend_from_slice(block))
193 .unwrap();
194 output
195 }
196
197 #[test]
198 fn converting_a_whole_clip_matches_pushing_it_in_chunks() {
199 let input: Vec<f32> = (0..4_801)
200 .map(|index| (index as f32 * 0.01).sin())
201 .collect();
202
203 let converted = Resampler::convert(48_000, &input).unwrap();
204
205 assert_eq!(converted, convert(48_000, &input, 137));
206 }
207
208 #[test]
209 fn canonical_samples_pass_through_unchanged() {
210 let samples = [0.0, 0.25, -0.5, 1.0];
211 assert_eq!(convert(CANONICAL_SAMPLE_RATE, &samples, 3), samples);
212 }
213
214 #[test]
215 fn resampling_preserves_duration_across_chunk_boundaries() {
216 for count in [0, 1, 2, 3, 1_023, 1_024, 1_025, 48_017] {
217 let input = vec![0.5; count];
218 let output = convert(48_000, &input, 137);
219 assert_eq!(output.len(), count / 3, "input length {count}");
220 if output.len() > 1_000 {
221 assert!(
222 output[100..output.len() - 100]
223 .iter()
224 .all(|x| (x - 0.5).abs() < 0.001)
225 );
226 }
227 }
228 }
229
230 #[test]
231 fn flushing_retains_an_impulse_near_the_end() {
232 let mut input = vec![0.0; 4_101];
233 input[4_080] = 1.0;
234 let output = convert(48_000, &input, 257);
235 assert_eq!(output.len(), 1_367);
236 assert!(output[1_350..].iter().any(|sample| sample.abs() > 0.1));
237 assert_eq!(output, convert(48_000, &input, input.len()));
238 }
239}