1
3
Fork
You've already forked fixed-resample
0
An easy to use crate for resampling at a fixed ratio
  • Rust 100%
2026年07月09日 17:42:28 -05:00
examples update dependencies 2026年07月09日 17:42:28 -05:00
src update dependencies 2026年07月09日 17:42:28 -05:00
test_files initial work 2025年01月22日 18:33:22 -06:00
.gitignore initial work 2025年01月22日 18:33:22 -06:00
Cargo.toml update dependencies 2026年07月09日 17:42:28 -05:00
LICENSE-APACHE add Apache-2.0 license 2026年03月16日 13:20:07 -05:00
LICENSE-MIT add Apache-2.0 license 2026年03月16日 13:20:07 -05:00
README.md update dependencies 2026年07月09日 17:42:28 -05:00

fixed-resample

Documentation Crates.io License

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

constIN_SAMPLE_RATE: u32 =44100;constOUT_SAMPLE_RATE: u32 =48000;constLEN_SECONDS: f64 =1.0;// Generate a sine wave at the input sample rate.
letmutphasor: f32 =0.0;letphasor_inc: f32 =440.0/IN_SAMPLE_RATEasf32;letlen_samples=(LEN_SECONDS*IN_SAMPLE_RATEasf64).round()asusize;letin_samples: Vec<f32>=(0..len_samples).map(|_|{phasor=(phasor+phasor_inc).fract();(phasor*std::f32::consts::TAU).sin()*0.5}).collect();letmutresampler=PacketResampler::<f32,Interleaved<f32>>::new(1,// number of channels
IN_SAMPLE_RATE,OUT_SAMPLE_RATE,Default::default(),// default config
);// Allocate a buffer for the resampled data.
letoutput_frames=resampler.out_alloc_frames(in_samples.len()asu64);letmutout_samples: Vec<f32>=Vec::with_capacity(output_framesasusize);// Resample to the output buffer. This method is realtime-safe.
resampler.process(// The input buffer. You can use one of the types in
// `fixed_resample::audioadapter_buffers::::*` to wrap your buffer into a type that
// implements `Adapter`.
&InterleavedSlice::new(&in_samples,1,len_samples).unwrap(),None,// input range
None,// active channels mask
// This method gets called whenever there is new resampled data.
|data,_frames|{out_samples.extend_from_slice(data);},// Whether or not this is the last (or only) packet of data that
// will be resampled. This ensures that any leftover samples in
// the internal resampler are flushed to the output.
Some(LastPacketInfo{// Let the resampler know that we want an exact number of output
// frames. Otherwise the resampler may add extra padded zeros
// to the end.
desired_output_frames: Some(output_framesasu64),}),// Trim the padded zeros at the beginning introduced by the internal
// resampler.
true,// trim delay
);

CPAL loopback example

usecpal::traits::{DeviceTrait,HostTrait,StreamTrait};usefixed_resample::{direct::InterleavedSlice,PushStatus,ReadStatus};fn main(){lethost=cpal::default_host();letinput_device=host.default_input_device().unwrap();letoutput_device=host.default_output_device().unwrap();letoutput_config: cpal::StreamConfig=output_device.default_output_config().unwrap().into();// Try selecting an input config that matches the output sample rate to
// avoid resampling.
letmutinput_config=None;forconfigininput_device.supported_input_configs().unwrap(){ifletSome(config)=config.try_with_sample_rate(output_config.sample_rate){input_config=Some(config);break;}}letinput_config: cpal::StreamConfig=input_config.unwrap_or_else(||input_device.default_input_config().unwrap()).into();letinput_channels=input_config.channelsasusize;letoutput_channels=output_config.channelsasusize;let(mutprod,mutcons)=fixed_resample::resampling_channel::<f32>(input_channels,input_config.sample_rate,output_config.sample_rate,true,// push_interleave_only
Default::default(),// configuration
);letinput_data_fn=move|data: &[f32],_: &cpal::InputCallbackInfo|{prod.push_interleaved(data);};letoutput_data_fn=move|data: &mut[f32],_: &cpal::OutputCallbackInfo|{letframes=data.len()/output_channels;cons.read(&mutInterleavedSlice::new_mut(data,output_channels,frames).unwrap(),None,// output_range
None,// active_channels_mask
false,// output_is_already_cleared
);};letinput_stream=input_device.build_input_stream(input_config,input_data_fn,err_fn,None).unwrap();letoutput_stream=output_device.build_output_stream(output_config,output_data_fn,err_fn,None).unwrap();// Play the streams.
input_stream.play().unwrap();output_stream.play().unwrap();// Run for 10 seconds before closing.
std::thread::sleep(std::time::Duration::from_secs(10));}fn err_fn(err: cpal::StreamError){eprintln!("an error occurred on stream: {}",err);}