use rubato::{FftFixedIn, Resampler}; use std::time::Duration; // Make this a constant you can tweak const RESAMPLER_CHUNK_SIZE: usize = 1024; pub struct FrameResampler { resampler: Option>, chunk_in: usize, in_buf: Vec, frame_samples: usize, pending: Vec, } impl FrameResampler { pub fn new(in_hz: usize, out_hz: usize, frame_dur: Duration) -> Self { let frame_samples = ((out_hz as f64 * frame_dur.as_secs_f64()).round()) as usize; assert!(frame_samples > 0, "frame duration too short"); // Use fixed chunk size instead of GCD-based let chunk_in = RESAMPLER_CHUNK_SIZE; let resampler = (in_hz != out_hz).then(|| { FftFixedIn::::new(in_hz, out_hz, chunk_in, 1, 1) .expect("Failed to create resampler") }); Self { resampler, chunk_in, in_buf: Vec::with_capacity(chunk_in), frame_samples, pending: Vec::with_capacity(frame_samples), } } pub fn push(&mut self, mut src: &[f32], mut emit: impl FnMut(&[f32])) { if self.resampler.is_none() { self.emit_frames(src, &mut emit); return; } while !src.is_empty() { let space = self.chunk_in - self.in_buf.len(); let take = space.min(src.len()); self.in_buf.extend_from_slice(&src[..take]); src = &src[take..]; if self.in_buf.len() == self.chunk_in { // let start = std::time::Instant::now(); if let Ok(out) = self .resampler .as_mut() .unwrap() .process(&[&self.in_buf[..]], None) { // let duration = start.elapsed(); // println!("Resampler took: {:?}", duration); self.emit_frames(&out[0], &mut emit); } self.in_buf.clear(); } } } pub fn finish(&mut self, mut emit: impl FnMut(&[f32])) { // Process any remaining input samples if let Some(ref mut resampler) = self.resampler { if !self.in_buf.is_empty() { // Pad with zeros to reach chunk size self.in_buf.resize(self.chunk_in, 0.0); if let Ok(out) = resampler.process(&[&self.in_buf[..]], None) { self.emit_frames(&out[0], &mut emit); } } } // Emit any remaining pending frame (padded with zeros) if !self.pending.is_empty() { self.pending.resize(self.frame_samples, 0.0); emit(&self.pending); self.pending.clear(); } } fn emit_frames(&mut self, mut data: &[f32], emit: &mut impl FnMut(&[f32])) { while !data.is_empty() { let space = self.frame_samples - self.pending.len(); let take = space.min(data.len()); self.pending.extend_from_slice(&data[..take]); data = &data[take..]; if self.pending.len() == self.frame_samples { emit(&self.pending); self.pending.clear(); } } } }