1use crate::config::{PhaseResponse, ResampleQuality};
4use rayon::prelude::*;
5use soxr::{
6 format::Mono,
7 params::{QualityFlags, QualityRecipe, QualitySpec, Rolloff, RuntimeSpec},
8 Soxr,
9};
10
11#[derive(Debug, Clone)]
13pub enum ResamplerError {
14 InitializationFailed(String),
16 ProcessFailed(String),
18}
19
20impl std::fmt::Display for ResamplerError {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 ResamplerError::InitializationFailed(msg) => {
24 write!(f, "Soxr initialization failed: {}", msg)
25 }
26 ResamplerError::ProcessFailed(msg) => write!(f, "Resampling process failed: {}", msg),
27 }
28 }
29}
30
31impl std::error::Error for ResamplerError {}
32
33pub struct Resampler {
35 channels: usize,
36 from_rate: u32,
37 to_rate: u32,
38}
39
40fn quality_to_recipe(quality: ResampleQuality) -> QualityRecipe {
44 match quality {
45 ResampleQuality::Low => QualityRecipe::Low, ResampleQuality::Standard => QualityRecipe::high(), ResampleQuality::High => QualityRecipe::high(), ResampleQuality::UltraHigh => QualityRecipe::very_high(), }
50}
51
52fn make_quality_spec(recipe: QualityRecipe, phase: PhaseResponse) -> QualitySpec {
54 QualitySpec::configure(recipe, Rolloff::default(), QualityFlags::HighPrecisionClock)
55 .with_phase_response(phase.to_soxr_value())
56}
57
58fn deinterleave_frame_major(
59 input: &[f64],
60 channels: usize,
61 frames: usize,
62 channel_inputs: &mut [Vec<f64>],
63) {
64 for frame in input[..frames * channels].chunks_exact(channels) {
65 for (ch, &sample) in frame.iter().enumerate() {
66 channel_inputs[ch].push(sample);
67 }
68 }
69}
70
71fn channel_outputs_have_frames(
72 channel_outputs: &[Vec<f64>],
73 channels: usize,
74 frames: usize,
75) -> bool {
76 channel_outputs
77 .iter()
78 .take(channels)
79 .all(|channel| channel.len() >= frames)
80}
81
82fn interleave_channel_outputs_to_vec(
83 channel_outputs: &[Vec<f64>],
84 channels: usize,
85 output: &mut Vec<f64>,
86) -> usize {
87 if channel_outputs.is_empty() || channel_outputs[0].is_empty() {
88 output.clear();
89 return 0;
90 }
91
92 let out_frames = channel_outputs[0].len();
93 output.clear();
94 output.reserve(out_frames * channels);
95
96 if channel_outputs_have_frames(channel_outputs, channels, out_frames) {
97 for frame in 0..out_frames {
98 for channel in channel_outputs.iter().take(channels) {
99 output.push(channel[frame]);
100 }
101 }
102 } else {
103 for frame in 0..out_frames {
104 for channel in channel_outputs.iter().take(channels) {
105 output.push(channel.get(frame).copied().unwrap_or(0.0));
106 }
107 }
108 }
109
110 out_frames
111}
112
113fn interleave_channel_outputs_to_vec_with_max_frames(
114 channel_outputs: &[Vec<f64>],
115 channels: usize,
116 output: &mut Vec<f64>,
117) -> usize {
118 let out_frames = channel_outputs
119 .iter()
120 .take(channels)
121 .map(Vec::len)
122 .max()
123 .unwrap_or(0);
124 output.clear();
125 if out_frames == 0 {
126 return 0;
127 }
128
129 output.reserve(out_frames * channels);
130 for frame in 0..out_frames {
131 for channel in channel_outputs.iter().take(channels) {
132 output.push(channel.get(frame).copied().unwrap_or(0.0));
133 }
134 }
135
136 out_frames
137}
138
139fn interleave_channel_outputs_to_slice(
140 channel_outputs: &[Vec<f64>],
141 channels: usize,
142 output: &mut [f64],
143) -> usize {
144 if channel_outputs.is_empty() || channel_outputs[0].is_empty() {
145 return 0;
146 }
147
148 let out_frames = channel_outputs[0].len();
149
150 if output.len() >= out_frames * channels
151 && channel_outputs_have_frames(channel_outputs, channels, out_frames)
152 {
153 for (frame, out_frame) in output
154 .chunks_exact_mut(channels)
155 .take(out_frames)
156 .enumerate()
157 {
158 for (dst, channel) in out_frame
159 .iter_mut()
160 .zip(channel_outputs.iter().take(channels))
161 {
162 *dst = channel[frame];
163 }
164 }
165 } else {
166 for frame in 0..out_frames {
167 for (ch, channel) in channel_outputs.iter().take(channels).enumerate() {
168 let idx = frame * channels + ch;
169 if idx < output.len() {
170 output[idx] = channel.get(frame).copied().unwrap_or(0.0);
171 }
172 }
173 }
174 }
175
176 out_frames
177}
178
179impl Resampler {
180 pub fn new(channels: usize, from_rate: u32, to_rate: u32) -> Self {
181 Self {
182 channels,
183 from_rate,
184 to_rate,
185 }
186 }
187
188 pub fn resample_parallel(
201 &self,
202 input: &[f64],
203 phase: PhaseResponse,
204 quality: ResampleQuality,
205 ) -> Result<Vec<f64>, ResamplerError> {
206 if self.from_rate == self.to_rate {
207 return Ok(input.to_vec());
208 }
209
210 if self.from_rate == 0 || self.to_rate == 0 {
212 return Err(ResamplerError::InitializationFailed(format!(
213 "Invalid sample rate: from_rate={}, to_rate={}",
214 self.from_rate, self.to_rate
215 )));
216 }
217
218 let frames = input.len() / self.channels;
220 let mut plan_channels: Vec<Vec<f64>> = vec![Vec::with_capacity(frames); self.channels];
221 deinterleave_frame_major(input, self.channels, frames, &mut plan_channels);
222
223 let resampled_channels: Result<Vec<Vec<f64>>, ResamplerError> = plan_channels
225 .into_par_iter()
226 .enumerate()
227 .map(|(ch_idx, channel_data)| {
228 let quality_spec = make_quality_spec(quality_to_recipe(quality), phase);
231
232 let runtime_spec = RuntimeSpec::new(1); let mut soxr = Soxr::<Mono<f64>>::new_with_params(
235 self.from_rate as f64,
236 self.to_rate as f64,
237 quality_spec,
238 runtime_spec,
239 )
240 .map_err(|e| {
241 ResamplerError::InitializationFailed(format!("Channel {}: {:?}", ch_idx, e))
242 })?;
243
244 let expected_frames = (channel_data.len() as f64 * self.to_rate as f64
246 / self.from_rate as f64)
247 .ceil() as usize
248 + 100;
249 let mut channel_output = Vec::with_capacity(expected_frames);
250
251 let inner_chunk_size = 8192;
254 let mut output_scratch = vec![0.0; (inner_chunk_size as f64 * 1.5) as usize]; let total_chunks = channel_data.len() / inner_chunk_size + 1;
257
258 if ch_idx == 0 {
260 log::info!(
261 "Starting resampling on thread. Total chunks: {}, Phase: {:?}",
262 total_chunks,
263 phase
264 );
265 }
266
267 for (i, chunk) in channel_data.chunks(inner_chunk_size).enumerate() {
268 let processed = soxr.process(chunk, &mut output_scratch).map_err(|e| {
269 ResamplerError::ProcessFailed(format!(
270 "Channel {} chunk {}: {:?}",
271 ch_idx, i, e
272 ))
273 })?;
274
275 if processed.output_frames > 0 {
276 channel_output
277 .extend_from_slice(&output_scratch[..processed.output_frames]);
278 }
279
280 if ch_idx == 0 && i > 0 && i % (total_chunks.max(10) / 10).max(1) == 0 {
282 log::debug!("Resampling progress: {}%", i * 100 / total_chunks);
283 }
284 }
285
286 let mut flush_scratch = vec![0.0; 4096];
288 if let Ok(processed) = soxr.process(&[], &mut flush_scratch) {
289 if processed.output_frames > 0 {
290 channel_output.extend_from_slice(&flush_scratch[..processed.output_frames]);
291 }
292 }
293
294 Ok(channel_output)
295 })
296 .collect();
297
298 let resampled_channels = resampled_channels?;
299
300 if resampled_channels.is_empty() {
302 return Ok(Vec::new());
303 }
304
305 let mut final_output = Vec::with_capacity(resampled_channels[0].len() * self.channels);
306 interleave_channel_outputs_to_vec(&resampled_channels, self.channels, &mut final_output);
307
308 Ok(final_output)
309 }
310}
311
312pub struct StreamingResampler {
317 soxr_instances: Vec<Soxr<Mono<f64>>>,
318 channels: usize,
319 from_rate: u32,
320 to_rate: u32,
321 output_scratch: Vec<f64>,
323 channel_inputs: Vec<Vec<f64>>,
325 channel_outputs: Vec<Vec<f64>>,
327 interleaved_output: Vec<f64>,
329}
330
331pub struct ResampleOutput<'a> {
332 pub samples: &'a [f64],
333 pub frames: usize,
334}
335
336impl StreamingResampler {
337 pub fn from_rate(&self) -> u32 {
338 self.from_rate
339 }
340
341 pub fn to_rate(&self) -> u32 {
342 self.to_rate
343 }
344
345 pub fn max_output_len_for_input(&self, input_samples: usize) -> usize {
354 if self.channels == 0 {
355 return 0;
356 }
357 let input_frames = input_samples / self.channels;
358 let ratio = self.to_rate as f64 / self.from_rate as f64;
359 (input_frames as f64 * ratio).ceil() as usize * self.channels + self.channels * 64
360 }
361
362 pub fn max_output_samples_per_chunk(&self) -> usize {
370 self.interleaved_output.capacity()
371 }
372
373 pub fn input_frames_for_output_frames(&self, output_frames: usize) -> usize {
374 if output_frames == 0 || self.to_rate == 0 {
375 return 0;
376 }
377
378 let ratio = self.from_rate as f64 / self.to_rate as f64;
379 (output_frames as f64 * ratio).ceil() as usize + 64
380 }
381
382 pub fn new(channels: usize, from_rate: u32, to_rate: u32) -> Result<Self, ResamplerError> {
384 Self::with_phase(channels, from_rate, to_rate, PhaseResponse::default())
385 }
386
387 pub fn with_phase(
391 channels: usize,
392 from_rate: u32,
393 to_rate: u32,
394 phase: PhaseResponse,
395 ) -> Result<Self, ResamplerError> {
396 Self::with_quality(channels, from_rate, to_rate, phase, ResampleQuality::High)
397 }
398
399 pub fn with_quality(
406 channels: usize,
407 from_rate: u32,
408 to_rate: u32,
409 phase: PhaseResponse,
410 quality: ResampleQuality,
411 ) -> Result<Self, ResamplerError> {
412 if from_rate == 0 || to_rate == 0 {
414 return Err(ResamplerError::InitializationFailed(format!(
415 "Invalid sample rate: from_rate={}, to_rate={}",
416 from_rate, to_rate
417 )));
418 }
419
420 let mut soxr_instances = Vec::with_capacity(channels);
421 for ch_idx in 0..channels {
422 let quality_spec = make_quality_spec(quality_to_recipe(quality), phase);
425 let runtime_spec = RuntimeSpec::new(1);
426
427 match Soxr::<Mono<f64>>::new_with_params(
428 from_rate as f64,
429 to_rate as f64,
430 quality_spec,
431 runtime_spec,
432 ) {
433 Ok(soxr) => soxr_instances.push(soxr),
434 Err(e) => {
435 return Err(ResamplerError::InitializationFailed(format!(
436 "Soxr failed for channel {}: {:?} (from={}Hz, to={}Hz)",
437 ch_idx, e, from_rate, to_rate
438 )));
439 }
440 }
441 }
442
443 let max_input_frames = 16384; let max_ratio = to_rate as f64 / from_rate as f64;
450 let max_output_per_channel = (max_input_frames as f64 * max_ratio).ceil() as usize + 64;
451
452 let channel_inputs: Vec<Vec<f64>> = (0..channels)
454 .map(|_| Vec::with_capacity(max_input_frames))
455 .collect();
456 let channel_outputs: Vec<Vec<f64>> = (0..channels)
457 .map(|_| Vec::with_capacity(max_output_per_channel))
458 .collect();
459 let interleaved_output = Vec::with_capacity(max_output_per_channel * channels);
460
461 Ok(Self {
462 soxr_instances,
463 channels,
464 from_rate,
465 to_rate,
466 output_scratch: vec![0.0; max_output_per_channel],
467 channel_inputs,
468 channel_outputs,
469 interleaved_output,
470 })
471 }
472
473 fn process_chunk_to_internal_output(&mut self, input: &[f64]) -> usize {
474 for ch_buf in &mut self.channel_inputs {
476 ch_buf.clear();
477 }
478
479 let input_frames = input.len() / self.channels;
480
481 deinterleave_frame_major(input, self.channels, input_frames, &mut self.channel_inputs);
484
485 for ch_buf in &mut self.channel_outputs {
487 ch_buf.clear();
488 }
489
490 for (ch, channel_data) in self.channel_inputs.iter().enumerate() {
492 let expected_output = (channel_data.len() as f64 * self.to_rate as f64
499 / self.from_rate as f64)
500 .ceil() as usize
501 + 64;
502 let cap = expected_output.min(self.output_scratch.len());
503
504 let processed = match self.soxr_instances[ch]
505 .process(channel_data, &mut self.output_scratch[..cap])
506 {
507 Ok(p) => p,
508 Err(e) => {
509 log::error!(
510 "Resampler process_chunk failed (ch={}, in_frames={}): {:?}",
511 ch,
512 channel_data.len(),
513 e
514 );
515 self.interleaved_output.clear();
516 return 0;
517 }
518 };
519
520 self.channel_outputs[ch]
521 .extend_from_slice(&self.output_scratch[..processed.output_frames]);
522 }
523
524 interleave_channel_outputs_to_vec(
525 &self.channel_outputs,
526 self.channels,
527 &mut self.interleaved_output,
528 )
529 }
530
531 pub fn process_chunk_borrowed<'a>(&'a mut self, input: &'a [f64]) -> ResampleOutput<'a> {
538 if self.from_rate == self.to_rate {
539 return ResampleOutput {
540 samples: input,
541 frames: input.len() / self.channels,
542 };
543 }
544
545 let input_frames = input.len() / self.channels;
546 if input_frames == 0 {
547 self.interleaved_output.clear();
548 return ResampleOutput {
549 samples: &self.interleaved_output,
550 frames: 0,
551 };
552 }
553
554 let frames = self.process_chunk_to_internal_output(input);
555 ResampleOutput {
556 samples: &self.interleaved_output,
557 frames,
558 }
559 }
560
561 pub fn process_chunk_append(&mut self, input: &[f64], output: &mut Vec<f64>) -> usize {
563 let result = self.process_chunk_borrowed(input);
564 output.extend_from_slice(result.samples);
565 result.frames
566 }
567
568 pub fn process_chunk_into(&mut self, input: &[f64], output: &mut [f64]) -> usize {
573 if self.from_rate == self.to_rate {
574 let copy_len = input.len().min(output.len());
575 output[..copy_len].copy_from_slice(&input[..copy_len]);
576 return copy_len / self.channels;
577 }
578
579 let input_frames = input.len() / self.channels;
580 if input_frames == 0 {
581 return 0;
582 }
583
584 for ch_buf in &mut self.channel_inputs {
586 ch_buf.clear();
587 }
588
589 deinterleave_frame_major(input, self.channels, input_frames, &mut self.channel_inputs);
592
593 for ch_buf in &mut self.channel_outputs {
595 ch_buf.clear();
596 }
597
598 for (ch, channel_data) in self.channel_inputs.iter().enumerate() {
600 let expected_output = (channel_data.len() as f64 * self.to_rate as f64
603 / self.from_rate as f64)
604 .ceil() as usize
605 + 64;
606 let cap = expected_output.min(self.output_scratch.len());
607
608 let processed = match self.soxr_instances[ch]
609 .process(channel_data, &mut self.output_scratch[..cap])
610 {
611 Ok(p) => p,
612 Err(e) => {
613 log::error!(
614 "Resampler process_chunk_into failed (ch={}, in_frames={}): {:?}",
615 ch,
616 channel_data.len(),
617 e
618 );
619 return 0;
620 }
621 };
622
623 self.channel_outputs[ch]
624 .extend_from_slice(&self.output_scratch[..processed.output_frames]);
625 }
626
627 interleave_channel_outputs_to_slice(&self.channel_outputs, self.channels, output)
628 }
629
630 pub fn reset(&mut self) {
631 for ch_buf in &mut self.channel_inputs {
632 ch_buf.clear();
633 }
634 for ch_buf in &mut self.channel_outputs {
635 ch_buf.clear();
636 }
637 self.interleaved_output.clear();
638 }
639
640 pub fn flush_borrowed(&mut self) -> ResampleOutput<'_> {
642 for channel_output in &mut self.channel_outputs {
643 channel_output.clear();
644 }
645
646 for ch in 0..self.channels {
647 loop {
649 match self.soxr_instances[ch].process(&[], &mut self.output_scratch) {
650 Ok(processed) if processed.output_frames > 0 => {
651 self.channel_outputs[ch]
652 .extend_from_slice(&self.output_scratch[..processed.output_frames]);
653 }
654 _ => break,
655 }
656 }
657 }
658
659 let frames = interleave_channel_outputs_to_vec_with_max_frames(
660 &self.channel_outputs,
661 self.channels,
662 &mut self.interleaved_output,
663 );
664 ResampleOutput {
665 samples: &self.interleaved_output,
666 frames,
667 }
668 }
669
670 pub fn flush_into(&mut self, output: &mut Vec<f64>) -> usize {
672 let result = self.flush_borrowed();
673 output.extend_from_slice(result.samples);
674 result.frames
675 }
676
677 pub fn flush(&mut self) -> Vec<f64> {
679 self.flush_borrowed().samples.to_vec()
680 }
681}
682
683#[cfg(test)]
684mod tests {
685 use super::*;
686
687 #[test]
688 fn deinterleave_frame_major_preserves_order_and_truncates_partial_frame() {
689 let input = vec![1.0, 2.0, 3.0, 4.0, 5.0, 99.0];
690 let mut channels = vec![Vec::new(), Vec::new()];
691
692 deinterleave_frame_major(&input, 2, input.len() / 2, &mut channels);
693
694 assert_eq!(channels[0], vec![1.0, 3.0, 5.0]);
695 assert_eq!(channels[1], vec![2.0, 4.0, 99.0]);
696
697 let input = vec![1.0, 2.0, 3.0, 4.0, 5.0];
698 let mut channels = vec![Vec::new(), Vec::new()];
699
700 deinterleave_frame_major(&input, 2, input.len() / 2, &mut channels);
701
702 assert_eq!(channels[0], vec![1.0, 3.0]);
703 assert_eq!(channels[1], vec![2.0, 4.0]);
704 }
705
706 #[test]
707 fn interleave_channel_outputs_fast_path_preserves_multichannel_order() {
708 let channel_outputs = vec![
709 vec![1.0, 4.0, 7.0],
710 vec![2.0, 5.0, 8.0],
711 vec![3.0, 6.0, 9.0],
712 ];
713 let mut output = Vec::new();
714
715 let frames = interleave_channel_outputs_to_vec(&channel_outputs, 3, &mut output);
716
717 assert_eq!(frames, 3);
718 assert_eq!(output, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]);
719 }
720
721 #[test]
722 fn interleave_channel_outputs_pads_short_channels() {
723 let channel_outputs = vec![vec![1.0, 3.0, 5.0], vec![2.0]];
724 let mut output = Vec::new();
725
726 let frames = interleave_channel_outputs_to_vec(&channel_outputs, 2, &mut output);
727
728 assert_eq!(frames, 3);
729 assert_eq!(output, vec![1.0, 2.0, 3.0, 0.0, 5.0, 0.0]);
730 }
731
732 #[test]
733 fn interleave_channel_outputs_with_max_frames_pads_short_channels() {
734 let channel_outputs = vec![vec![1.0], vec![2.0, 4.0, 6.0]];
735 let mut output = Vec::new();
736
737 let frames =
738 interleave_channel_outputs_to_vec_with_max_frames(&channel_outputs, 2, &mut output);
739
740 assert_eq!(frames, 3);
741 assert_eq!(output, vec![1.0, 2.0, 0.0, 4.0, 0.0, 6.0]);
742 }
743
744 #[test]
745 fn interleave_channel_outputs_to_slice_preserves_tail_when_output_is_longer() {
746 let channel_outputs = vec![vec![1.0, 3.0], vec![2.0, 4.0]];
747 let mut output = vec![42.0; 6];
748
749 let frames = interleave_channel_outputs_to_slice(&channel_outputs, 2, &mut output);
750
751 assert_eq!(frames, 2);
752 assert_eq!(output, vec![1.0, 2.0, 3.0, 4.0, 42.0, 42.0]);
753 }
754
755 #[test]
756 fn process_chunk_borrowed_equal_rate_reports_complete_frames_and_full_input() {
757 let mut resampler = StreamingResampler::new(2, 48_000, 48_000).unwrap();
758 let input = vec![1.0, 2.0, 3.0, 4.0, 5.0];
759
760 let result = resampler.process_chunk_borrowed(&input);
761
762 assert_eq!(result.frames, 2);
763 assert_eq!(result.samples, input.as_slice());
764 }
765
766 #[test]
767 fn input_frames_for_output_frames_tracks_rate_ratio_with_margin() {
768 let upsampler = StreamingResampler::new(2, 44_100, 384_000).unwrap();
769 let expected_up = ((512.0_f64 * 44_100.0 / 384_000.0).ceil() as usize) + 64;
770 assert_eq!(upsampler.input_frames_for_output_frames(512), expected_up);
771
772 let downsampler = StreamingResampler::new(2, 96_000, 48_000).unwrap();
773 assert_eq!(downsampler.input_frames_for_output_frames(2112), 4288);
774 assert_eq!(downsampler.input_frames_for_output_frames(0), 0);
775 }
776
777 #[test]
778 fn process_chunk_append_equal_rate_preserves_prefix_and_full_input() {
779 let mut resampler = StreamingResampler::new(2, 48_000, 48_000).unwrap();
780 let input = vec![1.0, 2.0, 3.0, 4.0, 5.0];
781 let mut output = vec![-1.0, -2.0];
782
783 let frames = resampler.process_chunk_append(&input, &mut output);
784
785 assert_eq!(frames, 2);
786 assert_eq!(output, vec![-1.0, -2.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
787 }
788
789 #[test]
790 fn process_chunk_append_matches_borrowed_for_resampling() {
791 let input = (0..2048)
792 .map(|sample| sample as f64 / 2048.0)
793 .collect::<Vec<_>>();
794 let mut borrowed_resampler = StreamingResampler::new(2, 44_100, 48_000).unwrap();
795 let mut append_resampler = StreamingResampler::new(2, 44_100, 48_000).unwrap();
796 let expected = borrowed_resampler
797 .process_chunk_borrowed(&input)
798 .samples
799 .to_vec();
800 let mut actual = vec![99.0];
801
802 let frames = append_resampler.process_chunk_append(&input, &mut actual);
803
804 assert_eq!(frames * 2, expected.len());
805 assert_eq!(&actual[..1], &[99.0]);
806 assert_eq!(&actual[1..], expected.as_slice());
807 }
808
809 #[test]
810 fn process_chunk_into_reuses_internal_capacity_after_warmup() {
811 let input = (0..4096)
812 .map(|sample| sample as f64 / 4096.0)
813 .collect::<Vec<_>>();
814 let mut resampler = StreamingResampler::new(2, 44_100, 48_000).unwrap();
815 let mut output = vec![0.0; resampler.max_output_len_for_input(input.len())];
816
817 let _ = resampler.process_chunk_into(&input, &mut output);
818 let warmed_input_caps = resampler
819 .channel_inputs
820 .iter()
821 .map(Vec::capacity)
822 .collect::<Vec<_>>();
823 let warmed_output_caps = resampler
824 .channel_outputs
825 .iter()
826 .map(Vec::capacity)
827 .collect::<Vec<_>>();
828 let warmed_interleaved_cap = resampler.interleaved_output.capacity();
829 let warmed_scratch_len = resampler.output_scratch.len();
830
831 let _ = resampler.process_chunk_into(&input, &mut output);
832
833 assert_eq!(
834 resampler
835 .channel_inputs
836 .iter()
837 .map(Vec::capacity)
838 .collect::<Vec<_>>(),
839 warmed_input_caps
840 );
841 assert_eq!(
842 resampler
843 .channel_outputs
844 .iter()
845 .map(Vec::capacity)
846 .collect::<Vec<_>>(),
847 warmed_output_caps
848 );
849 assert_eq!(
850 resampler.interleaved_output.capacity(),
851 warmed_interleaved_cap
852 );
853 assert_eq!(resampler.output_scratch.len(), warmed_scratch_len);
854 }
855
856 #[test]
857 fn flush_into_matches_flush_and_preserves_prefix() {
858 let input = (0..2048)
859 .map(|sample| sample as f64 / 2048.0)
860 .collect::<Vec<_>>();
861 let mut wrapper_resampler = StreamingResampler::new(2, 44_100, 48_000).unwrap();
862 let mut append_resampler = StreamingResampler::new(2, 44_100, 48_000).unwrap();
863 let mut scratch = Vec::new();
864 let _ = wrapper_resampler.process_chunk_append(&input, &mut scratch);
865 scratch.clear();
866 let _ = append_resampler.process_chunk_append(&input, &mut scratch);
867 let expected = wrapper_resampler.flush();
868 let mut actual = vec![99.0];
869
870 let frames = append_resampler.flush_into(&mut actual);
871
872 assert_eq!(frames * 2, expected.len());
873 assert_eq!(&actual[..1], &[99.0]);
874 assert_eq!(&actual[1..], expected.as_slice());
875 }
876
877 #[test]
878 fn flush_into_reuses_warmed_output_capacity() {
879 let input = (0..4096)
880 .map(|sample| sample as f64 / 4096.0)
881 .collect::<Vec<_>>();
882 let mut resampler = StreamingResampler::new(2, 44_100, 48_000).unwrap();
883 let mut scratch = Vec::new();
884 let _ = resampler.process_chunk_append(&input, &mut scratch);
885 let mut output = Vec::with_capacity(resampler.max_output_len_for_input(input.len()));
886
887 let _ = resampler.flush_into(&mut output);
888 let warmed_capacity = output.capacity();
889 output.clear();
890 scratch.clear();
891 let _ = resampler.process_chunk_append(&input, &mut scratch);
892 let _ = resampler.flush_into(&mut output);
893
894 assert_eq!(output.capacity(), warmed_capacity);
895 }
896
897 #[test]
898 fn flush_into_reuses_internal_capacity_after_warmup() {
899 let input = (0..4096)
900 .map(|sample| sample as f64 / 4096.0)
901 .collect::<Vec<_>>();
902 let mut resampler = StreamingResampler::new(2, 44_100, 48_000).unwrap();
903 let mut output = Vec::with_capacity(resampler.max_output_len_for_input(input.len()));
904 let mut scratch = Vec::new();
905 let _ = resampler.process_chunk_append(&input, &mut scratch);
906 let _ = resampler.flush_into(&mut output);
907 let warmed_channel_caps = resampler
908 .channel_outputs
909 .iter()
910 .map(Vec::capacity)
911 .collect::<Vec<_>>();
912 let warmed_interleaved_cap = resampler.interleaved_output.capacity();
913 let warmed_scratch_len = resampler.output_scratch.len();
914
915 output.clear();
916 scratch.clear();
917 let _ = resampler.process_chunk_append(&input, &mut scratch);
918 let _ = resampler.flush_into(&mut output);
919
920 assert_eq!(
921 resampler
922 .channel_outputs
923 .iter()
924 .map(Vec::capacity)
925 .collect::<Vec<_>>(),
926 warmed_channel_caps
927 );
928 assert_eq!(
929 resampler.interleaved_output.capacity(),
930 warmed_interleaved_cap
931 );
932 assert_eq!(resampler.output_scratch.len(), warmed_scratch_len);
933 }
934}