use std::mem;
use crate::audio_util;
use crate::sinc_resampler::{KERNEL_SIZE, SincResampler, SincResamplerCallback};
struct SliceCallback<'a> {
source: &'a [f32],
first_pass: bool,
}
impl SincResamplerCallback for SliceCallback<'_> {
fn run(&mut self, frames: usize, destination: &mut [f32]) {
if self.first_pass {
destination[..frames].fill(0.0);
self.first_pass = false;
return;
}
assert_eq!(
self.source.len(),
frames,
"callback requested {frames} frames but source has {}",
self.source.len()
);
destination[..frames].copy_from_slice(&self.source[..frames]);
}
}
#[derive(Debug)]
pub struct PushSincResampler {
resampler: SincResampler,
source_float_buffer: Vec<f32>,
dest_float_buffer: Vec<f32>,
destination_frames: usize,
first_pass: bool,
}
impl PushSincResampler {
pub fn new(source_frames: usize, destination_frames: usize) -> Self {
assert!(source_frames > 0);
assert!(destination_frames > 0);
let ratio = source_frames as f64 / destination_frames as f64;
Self {
resampler: SincResampler::new(ratio, source_frames),
source_float_buffer: vec![0.0; source_frames],
dest_float_buffer: Vec::new(),
destination_frames,
first_pass: true,
}
}
pub fn resample(&mut self, source: &[f32], destination: &mut [f32]) -> usize {
assert_eq!(source.len(), self.resampler.request_frames());
assert!(destination.len() >= self.destination_frames);
let mut cb = SliceCallback {
source,
first_pass: self.first_pass,
};
if self.first_pass {
let chunk = self.resampler.chunk_size();
self.resampler.resample(chunk, destination, &mut cb);
}
self.resampler
.resample(self.destination_frames, destination, &mut cb);
if self.first_pass {
self.first_pass = false;
}
self.destination_frames
}
pub fn resample_i16(&mut self, source: &[i16], destination: &mut [i16]) -> usize {
assert_eq!(source.len(), self.resampler.request_frames());
assert!(destination.len() >= self.destination_frames);
for (d, &s) in self.source_float_buffer.iter_mut().zip(source) {
*d = s as f32;
}
let mut dest_buf = mem::take(&mut self.dest_float_buffer);
if dest_buf.len() < self.destination_frames {
dest_buf.resize(self.destination_frames, 0.0);
}
let source_buf = mem::take(&mut self.source_float_buffer);
self.resample(&source_buf, &mut dest_buf);
self.source_float_buffer = source_buf;
audio_util::float_s16_to_s16_slice(
&dest_buf[..self.destination_frames],
&mut destination[..self.destination_frames],
);
self.dest_float_buffer = dest_buf;
self.destination_frames
}
pub fn algorithmic_delay_seconds(source_rate_hz: u32) -> f32 {
1.0 / source_rate_hz as f32 * KERNEL_SIZE as f32 / 2.0
}
pub fn destination_frames(&self) -> usize {
self.destination_frames
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identity_passthrough() {
let frames = 480;
let mut r = PushSincResampler::new(frames, frames);
let input: Vec<f32> = (0..frames).map(|i| (i as f32 * 0.01).sin()).collect();
let mut output = vec![0.0_f32; frames];
for _ in 0..3 {
let n = r.resample(&input, &mut output);
assert_eq!(n, frames);
}
let delay = 16_usize; let check_start = delay;
let check_end = frames - delay;
let max_diff = output[check_start..check_end]
.iter()
.zip(input[..check_end - check_start].iter())
.map(|(o, i)| (o - i).abs())
.fold(0.0_f32, f32::max);
assert!(
max_diff < 0.1,
"output should track delayed input, max_diff={max_diff}"
);
}
#[test]
fn downsample_48k_to_16k() {
let src_frames = 480; let dst_frames = 160; let mut r = PushSincResampler::new(src_frames, dst_frames);
let input: Vec<f32> = (0..src_frames).map(|i| (i as f32 * 0.02).sin()).collect();
let mut output = vec![0.0_f32; dst_frames];
for _ in 0..3 {
let n = r.resample(&input, &mut output);
assert_eq!(n, dst_frames);
}
let energy: f32 = output.iter().map(|v| v * v).sum();
assert!(energy > 0.01, "output should have signal energy");
}
#[test]
fn upsample_16k_to_48k() {
let src_frames = 160;
let dst_frames = 480;
let mut r = PushSincResampler::new(src_frames, dst_frames);
let input: Vec<f32> = (0..src_frames).map(|i| (i as f32 * 0.05).sin()).collect();
let mut output = vec![0.0_f32; dst_frames];
for _ in 0..3 {
let n = r.resample(&input, &mut output);
assert_eq!(n, dst_frames);
}
let energy: f32 = output.iter().map(|v| v * v).sum();
assert!(energy > 0.01, "output should have signal energy");
}
#[test]
fn i16_resample() {
let src_frames = 480;
let dst_frames = 160;
let mut r = PushSincResampler::new(src_frames, dst_frames);
let input: Vec<i16> = (0..src_frames)
.map(|i| (1000.0 * (i as f32 * 0.02).sin()) as i16)
.collect();
let mut output = vec![0_i16; dst_frames];
for _ in 0..3 {
let n = r.resample_i16(&input, &mut output);
assert_eq!(n, dst_frames);
}
let has_signal = output.iter().any(|&v| v != 0);
assert!(has_signal, "i16 resampler should produce output");
}
#[test]
fn algorithmic_delay() {
let delay = PushSincResampler::algorithmic_delay_seconds(48000);
assert!((delay - 16.0 / 48000.0).abs() < 1e-6);
}
}