Skip to content

Commit b7d9e9d

Browse files
HEnquistclaude
andcommitted
Make README example feature-independent and stream-oriented
Switch the main example from the feature-gated `Fft` resampler to the always-available `Async` polynomial resampler so the doctest can run unconditionally (drops the `rust,ignore`). Restructure it around reusable per-chunk buffers and a general loop that works for a file or a live stream. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5815bb9 commit b7d9e9d

1 file changed

Lines changed: 51 additions & 37 deletions

File tree

README.md

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -272,51 +272,65 @@ RUST_LOG=trace cargo test --features log
272272

273273
## Example
274274

275-
Resample a dummy audio file from 44100 to 48000 Hz.
276-
This uses the `Fft` resampler, which requires the `fft_resampler` feature (enabled by default).
275+
Resample stereo audio from 44100 to 48000 Hz, one chunk at a time.
276+
The input is processed in a loop and can come from anywhere: a file read
277+
chunk by chunk, or a live stream that keeps running indefinitely.
278+
This uses the `Async` resampler with polynomial interpolation,
279+
which is always available and needs no optional features.
277280
See also the "process_f64" example that can be used to process a file from disk.
278-
```rust,ignore
281+
```rust
279282
use rubato::{
280-
Resampler, Fft, FixedSync, Indexing
283+
Resampler, Async, FixedAsync, PolynomialDegree, Indexing
281284
};
282285
use audioadapter_buffers::direct::InterleavedSlice;
283286

284-
let mut resampler = Fft::<f64>::new(48000, 44100, 1024, 2, FixedSync::Both).unwrap();
285-
286-
// create a short dummy audio clip, assuming it's stereo stored as interleaved f64 values
287-
let audio_clip = vec![0.0; 2*10000];
288-
289-
// wrap it with an InterleavedSlice Adapter
290-
let nbr_input_frames = audio_clip.len() / 2;
291-
let input_adapter = InterleavedSlice::new(&audio_clip, 2, nbr_input_frames).unwrap();
292-
293-
// create a buffer for the output
294-
let mut outdata = vec![0.0; 2*2*10000];
295-
let outdata_capacity = outdata.len() / 2;
296-
let mut output_adapter =
297-
InterleavedSlice::new_mut(&mut outdata, 2, outdata_capacity).unwrap();
298-
299-
// Preparations
300-
let mut indexing = Indexing::new();
301-
302-
let mut input_frames_left = nbr_input_frames;
303-
let mut input_frames_next = resampler.input_frames_next();
304-
305-
// Loop over all full chunks.
306-
// There will be some unprocessed input frames left after the last full chunk.
307-
// see the `process_f64` example for how to handle those
308-
// using `partial_len` of the indexing struct.
309-
// It is also possible to use the `process_all_into_buffer` method
310-
// to process the entire file (including any last partial chunk) with a single call.
311-
while input_frames_left >= input_frames_next {
312-
let (frames_read, frames_written) = resampler
287+
let channels = 2;
288+
let chunk_size = 1024;
289+
290+
let mut resampler = Async::<f64>::new_poly(
291+
48000.0 / 44100.0,
292+
1.1,
293+
PolynomialDegree::Cubic,
294+
chunk_size,
295+
channels,
296+
FixedAsync::Input,
297+
).unwrap();
298+
299+
// Reusable buffers for a single chunk, assuming interleaved f64 samples.
300+
// With `FixedAsync::Input` every call consumes `chunk_size` input frames and
301+
// produces at most `output_frames_max()` output frames.
302+
let mut indata = vec![0.0; channels * chunk_size];
303+
let mut outdata = vec![0.0; channels * resampler.output_frames_max()];
304+
let outdata_capacity = outdata.len() / channels;
305+
306+
let indexing = Indexing::new();
307+
308+
// Keep processing for as long as there is more audio to handle.
309+
// Here the source is a dummy counter that stops after a few chunks;
310+
// in a real application this stands in for "is there more data?".
311+
let mut chunks_left = 10;
312+
loop {
313+
// Fetch the next `input_frames_next()` frames from the source into `indata`.
314+
// For a file, break out of the loop once the end is reached (a shorter final
315+
// chunk is handled by setting `partial_len` on the indexing struct, see the
316+
// `process_f64` example). For an endless stream, simply never break.
317+
if chunks_left == 0 {
318+
break;
319+
}
320+
chunks_left -= 1;
321+
let frames_to_read = resampler.input_frames_next();
322+
// (read `frames_to_read` frames from the file or stream into `indata` here)
323+
324+
let input_adapter = InterleavedSlice::new(&indata, channels, frames_to_read).unwrap();
325+
let mut output_adapter =
326+
InterleavedSlice::new_mut(&mut outdata, channels, outdata_capacity).unwrap();
327+
328+
let (_frames_read, frames_written) = resampler
313329
.process_into_buffer(&input_adapter, &mut output_adapter, Some(&indexing))
314330
.unwrap();
315331

316-
indexing.input_offset += frames_read;
317-
indexing.output_offset += frames_written;
318-
input_frames_left -= frames_read;
319-
input_frames_next = resampler.input_frames_next();
332+
// Write the `frames_written` output frames to the destination file or stream.
333+
let _ = frames_written;
320334
}
321335
```
322336

0 commit comments

Comments
 (0)