Conversation
Bump audioadapter to 5.0 and audioadapter-buffers/audioadapter-sample to 5.1, raise the MSRV to 1.87 to match, and replace the commented-out path dependencies with a commented-out [patch.crates-io] block.
Expose fft_size_in, fft_size_out and cutoff on Fft, and cutoff on Async. The cutoff is relative to the input Nyquist frequency, and is None for the polynomial variant of Async since it uses no anti-aliasing filter. Extract the cutoff resolution into resolve_cutoff so make_interpolator and new_sinc share one definition, and pass the resolved value to new_with_sinc_interpolator. Also fix the Debug impl of Fft, which named itself Fast.
A small CLI that reads a wav file with waveadapter, resamples it with the FFT resampler, and writes it back in any sample format waveadapter supports. Options for output format, gain, chunk size and window, parsed with clap.
The public docs for Slip::new linked to a private constant, which rustdoc warns about and readers cannot follow. State the values instead.
Async and Fft both printed themselves as Fast, left over from when the separate Fast and FFT resamplers were united, and both had trailing commas inside the field name strings.
Replace the manual modulo checks with is_multiple_of, which recent clippy versions flag, and allow the argument count of new_with_sinc_interpolator now that it also takes the filter cutoff. CI runs clippy with -D warnings, so both were fatal.
process_all_f64 only differed from process_f64 by calling process_all_into_buffer, which is now shown by a doc example on the method itself. process_i16 duplicated process_f64 apart from wrapping the input in an integer byte adapter, which audioadapter documents. Removing it also drops the audioadapter-sample dev dependency.
The examples took a long list of positional arguments with no help text. Give them all named options with defaults instead, matching the new resample_wav example. Also merge fixedout_ramp64 and polyfixedin_ramp64 into ramp_ratio_f64. They only differed by resampler type and which side is fixed, both of which are now options, so the merged example also covers the two combinations neither of them had.
The section still described a 16-bit integer example that is gone, and did not mention that resample_wav reads and writes wav files directly. List the four examples and what each one is for.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates rubato to v5.0.0 by bumping MSRV and audioadapter dependencies, and extends the resampler APIs/docs/examples with more introspection and CLI tooling.
Changes:
- Bump crate version to 5.0.0, raise MSRV to Rust 1.87, and update
audioadapter/audioadapter-buffers. - Expose/report filter cutoff and FFT block sizes; refactor sinc cutoff resolution and add corresponding tests.
- Refresh/remove older examples and add new Clap-based examples, including a WAV resampling CLI.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/synchro.rs | Stores/exposes FFT resampler cutoff + FFT block size getters; improves Debug formatting; adds tests. |
| src/slip.rs | Updates constructor docs for crossfade sizing details. |
| src/sinc_interpolator/sinc_interpolator_sse.rs | Uses is_multiple_of(8) for clearer alignment assertion. |
| src/sinc_interpolator/sinc_interpolator_neon.rs | Uses is_multiple_of(8) for clearer alignment assertion. |
| src/sinc_interpolator/sinc_interpolator_avx.rs | Uses is_multiple_of(8) for clearer alignment assertion. |
| src/sinc_interpolator/mod.rs | Formats/clarifies the multiple-of-8 assertion. |
| src/lib.rs | Adds a Rustdoc example for process_all_into_buffer. |
| src/asynchro_sinc.rs | Extracts rounding + cutoff resolution helpers for sinc interpolators. |
| src/asynchro.rs | Tracks/exposes sinc cutoff for async resamplers; updates Debug; adds tests. |
| examples/resample_wav.rs | Adds a minimal WAV resampling CLI example (FFT resampler). |
| examples/ramp_ratio_f64.rs | Adds a Clap-based example for ramping adjustable ratio over time. |
| examples/process_i16.rs | Removes an older raw i16 processing example. |
| examples/process_f64.rs | Modernizes CLI parsing (Clap) and supports multiple resampler types via enums. |
| examples/process_all_f64.rs | Removes an older process_all_into_buffer-focused example. |
| examples/polyfixedin_ramp64.rs | Removes an older ramping example superseded by newer equivalents. |
| examples/fixedout_ramp64.rs | Removes an older ramping example superseded by newer equivalents. |
| examples/adjust_ratio_f64.rs | Modernizes CLI parsing (Clap) and improves example docs. |
| benches/resamplers.rs | Updates bench call site for new sinc cutoff parameter plumbed through constructors. |
| README.md | Updates docs.rs links, MSRV note, and adds v5.0.0 changelog entries. |
| Cargo.toml | Bumps version/MSRV, updates deps, adds new dev-deps and example metadata. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Drop a duplicated sentence in the docs for Fft::cutoff, and pin the crossfade values that the docs for Slip::new quote with an assert, so they cannot drift unnoticed. Replace the float rounding used to size the FFT blocks and the sinc filter with integer arithmetic. Dividing as f32 loses precision for large values, and div_ceil says what is meant. Rounding down to zero blocks now falls back to a single block instead of panicking further down.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Suppressed comments (3)
examples/ramp_ratio_f64.rs:104
read_fileusesRead::readinto a fixed-size buffer but ignoresbytes_read. A short read (or a truncated final sample) will causefrom_le_bytesto decode stale bytes from the previous iteration and silently corrupt the input. Useread_exact(and handleUnexpectedEof) so each decoded f64 is backed by exactly 8 bytes.
let bytes_read = inbuffer.read(&mut buffer).unwrap();
if bytes_read == 0 {
break;
}
let value = f64::from_le_bytes(buffer.as_slice().try_into().unwrap());
examples/resample_wav.rs:166
- This comment is misleading: with
Fft::new_custom, the internal FFT block sizes may be rounded up from--chunkto a valid block size for the rate pair (e.g. 44100→48000). Consider clarifying that--chunkis a requested size, whilefft_size_in/out()report the actual block sizes used.
// One sub chunk per chunk, so the chunk size is also the FFT block size.
examples/ramp_ratio_f64.rs:218
- The processing loop stops when
frames_left <= input_frames_next(), but the remaining frames are never processed. This can drop the last chunk (or the last partial chunk) of audio. Add a finalpartial_lencall after the loop (mirroringResampler::process_all_into_buffer).
}
The read helper decoded the buffer whatever the read returned, so a short read or a truncated trailing sample decoded stale bytes from the previous iteration as audio. Use read_exact and stop at a clean end of file, like adjust_ratio_f64 already did. Also correct the comment in resample_wav that claimed the chunk size is the FFT block size. It is the requested size, rounded up to a block size valid for the sample rate pair.
The loop stopped as soon as fewer frames than a full chunk were left, so the tail of the clip was silently dropped. Add the partial call after the loop, like process_f64 does.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Suppressed comments (2)
examples/process_f64.rs:107
- The
read_fileloop treats anyUnexpectedEoffromread_exactas a clean EOF, but the comment says a partial trailing read means a malformed file. Withread_exact,UnexpectedEofalso occurs for a truly partial trailing sample, so the current code silently drops trailing bytes instead of failing as documented. Consider reading incrementally so you can distinguish clean EOF (0 bytes read) from a partial sample and error out on the latter.
// A clean end of file stops the loop; a partial trailing read means a malformed file.
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
Err(e) => panic!("Error reading input file: {}", e),
examples/ramp_ratio_f64.rs:107
- Same issue as in
process_f64:read_exactreturningUnexpectedEofis treated as a clean EOF, but it also happens for a partial trailing sample. This means malformed inputs with a non-multiple-of-8 byte length will be silently truncated despite the comment saying they are malformed.
// A clean end of file stops the loop; a partial trailing read means a malformed file.
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
Err(e) => panic!("Error reading input file: {}", e),
Updates the audioadapter dependencies and bumps rubato to 5.0.0.
audioadapter5.0,audioadapter-buffersandaudioadapter-sample5.1[patch.crates-io]block