fixed-resample

An easy to use crate for resampling at a fixed ratio.
It supports resampling in both realtime and in non-realtime applications, and also includes a handy realtime-safe spsc channel type that automatically resamples the input stream to match the output stream when needed.
This crate uses Rubato internally.
Dev profile performance
Note, this resampler is very slow when compiled without optimizations, which can lead to underflows in the spsc channel. Consider enabling optimization for the dev profile in your Cargo.toml:
[profile.dev]
opt-level = 1
Non-realtime example
const IN_SAMPLE_RATE: u32 = 44100;
const OUT_SAMPLE_RATE: u32 = 48000;
const LEN_SECONDS: f64 = 1.0;
let mut phasor: f32 = 0.0;
let phasor_inc: f32 = 440.0 / IN_SAMPLE_RATE as f32;
let len_samples = (LEN_SECONDS * IN_SAMPLE_RATE as f64).round() as usize;
let in_samples: Vec<f32> = (0..len_samples).map(|_| {
phasor = (phasor + phasor_inc).fract();
(phasor * std::f32::consts::TAU).sin() * 0.5
}).collect();
let mut resampler = PacketResampler::<f32, Interleaved<f32>>::new(
1, IN_SAMPLE_RATE,
OUT_SAMPLE_RATE,
Default::default(), );
let output_frames = resampler.out_alloc_frames(in_samples.len() as u64);
let mut out_samples: Vec<f32> = Vec::with_capacity(output_frames as usize);
resampler.process(
&InterleavedSlice::new(&in_samples, 1, len_samples).unwrap(),
None, None, |data, _frames| {
out_samples.extend_from_slice(data);
},
Some(LastPacketInfo {
desired_output_frames: Some(output_frames as u64),
}),
true, );
CPAL loopback example
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use fixed_resample::{direct::InterleavedSlice, PushStatus, ReadStatus};
fn main() {
let host = cpal::default_host();
let input_device = host.default_input_device().unwrap();
let output_device = host.default_output_device().unwrap();
let output_config: cpal::StreamConfig = output_device.default_output_config().unwrap().into();
let mut input_config = None;
for config in input_device.supported_input_configs().unwrap() {
if let Some(config) = config.try_with_sample_rate(output_config.sample_rate) {
input_config = Some(config);
break;
}
}
let input_config: cpal::StreamConfig = input_config
.unwrap_or_else(|| input_device.default_input_config().unwrap())
.into();
let input_channels = input_config.channels as usize;
let output_channels = output_config.channels as usize;
let (mut prod, mut cons) = fixed_resample::resampling_channel::<f32>(
input_channels,
input_config.sample_rate,
output_config.sample_rate,
true, Default::default(), );
let input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| {
prod.push_interleaved(data);
};
let output_data_fn = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
let frames = data.len() / output_channels;
cons.read(
&mut InterleavedSlice::new_mut(data, output_channels, frames).unwrap(),
None, None, false, );
};
let input_stream = input_device
.build_input_stream(&input_config, input_data_fn, err_fn, None)
.unwrap();
let output_stream = output_device
.build_output_stream(&output_config, output_data_fn, err_fn, None)
.unwrap();
input_stream.play().unwrap();
output_stream.play().unwrap();
std::thread::sleep(std::time::Duration::from_secs(10));
}
fn err_fn(err: cpal::StreamError) {
eprintln!("an error occurred on stream: {}", err);
}