Skip to content

Commit eeccae4

Browse files
committed
Documentation updates based on feedback
1 parent 63c2107 commit eeccae4

3 files changed

Lines changed: 99 additions & 22 deletions

File tree

README.md

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ For example, converting a file from 44.1 kHz to 48 kHz.
6060
The ratio, 48 kHz / 44.1 kHz (equivalent to 160 / 147),
6161
is fixed and constant throughout the process.
6262

63-
Synchronous resampling is implemented via FFT. The data is FFT:ed, the spectrum modified,
64-
and then inverse FFT:ed to get the resampled data.
65-
This type of resampler is considerably faster but doesn't support changing the resampling ratio.
63+
Synchronous resampling is implemented via FFT (Fast Fourier Transform).
64+
The audio data is transformed into the frequency domain,
65+
the spectrum is scaled to match the target sample rate,
66+
and then transformed back to produce the resampled output.
67+
This type of resampler is considerably faster than sinc-based approaches
68+
but doesn't support changing the resampling ratio.
6669

6770
## Usage
6871
The resamplers provided by this library are intended to support processing streams of audio.
@@ -74,21 +77,28 @@ This gives a good compromise between efficiency and memory usage.
7477
### Chunk size and fixed size options
7578

7679
Rubato processes audio in chunks.
77-
The size of these chunks is determined by the chunk size parameter given to the resampler constructor.
78-
Depending on the configuration, this parameter determines
79-
the number of frames in the input or output chunk, or both.
80+
The `chunk_size` parameter given to the resampler constructor sets the target size
81+
for the **fixed side**the side that always has the same number of frames per call.
82+
The other side is variable and will differ from call to call.
8083

8184
The resamplers allow specifying which side should have a fixed size.
8285

83-
* **Fixed input**: The input chunk size is fixed to the given value.
84-
The output chunk size will vary depending on how many samples can be calculated using the available input data.
85-
This is convenient to use for resampling data from a source that delivers data in fixed size chunks.
86-
* **Fixed output**: The output chunk size is fixed to the given value.
87-
The input chunk size will vary depending on how many new samples the resampler needs to calculate the output.
88-
This is meant to be used for resampling data that will be sent to some target that requires fixed size chunks.
89-
* **Both input and output fixed**: Both input and output chunk sizes are fixed.
86+
* **Fixed input** (`FixedAsync::Input` / `FixedSync::Input`):
87+
The input chunk size is fixed to `chunk_size` frames per call.
88+
The output chunk size varies depending on how many samples can be calculated
89+
from the available input data.
90+
This is convenient when the audio source delivers data in fixed-size chunks
91+
(e.g. a hardware capture callback).
92+
* **Fixed output** (`FixedAsync::Output` / `FixedSync::Output`):
93+
The output chunk size is fixed to `chunk_size` frames per call.
94+
The input chunk size varies depending on how many new samples the resampler
95+
needs to fill the output.
96+
This is useful when the audio destination consumes fixed-size chunks
97+
(e.g. a hardware playback callback).
98+
* **Both input and output fixed** (`FixedSync::Both`):
99+
Both input and output chunk sizes are fixed.
90100
This mode is only available for the synchronous resampler.
91-
In this mode, the chunk size parameter is used as a hint,
101+
In this mode, the `chunk_size` parameter is used as a hint,
92102
and the actual chunk sizes are calculated to fit the resampling ratio exactly.
93103
For example, a 44.1 kHz to 48 kHz resampler must use an input chunk size that is a multiple of 147,
94104
and an output chunk size that is a multiple of 160, in order to maintain the correct resampling ratio.
@@ -99,6 +109,24 @@ The resamplers allow specifying which side should have a fixed size.
99109
For asynchronous resamplers, fixing both input and output chunk sizes is not possible
100110
since the resampling ratio can change, requiring at least one side to be variable.
101111

112+
### Input and output sizes per call
113+
114+
The `chunk_size` constructor parameter is only the *target* size for the fixed side.
115+
Always call `input_frames_next()` before providing data to `process_into_buffer`
116+
to find out the exact number of input frames required for that call.
117+
Similarly, call `output_frames_next()` to find the exact number of output frames that will be written.
118+
119+
* With fixed input, `input_frames_next()` always returns `chunk_size`.
120+
`output_frames_next()` varies and must be checked each call.
121+
* With fixed output, `output_frames_next()` always returns `chunk_size`.
122+
`input_frames_next()` varies and must be checked each call.
123+
* With `FixedSync::Both`, both values are fixed, but they may differ from `chunk_size`
124+
because they are rounded to fit the exact sample-rate ratio.
125+
126+
The input and output buffers must be large enough to hold at least the number of frames
127+
reported by `input_frames_next()` and `output_frames_next()` respectively.
128+
Both buffers may be larger than required — only the needed frames are read or written.
129+
102130
### Resampling quality
103131
The synchronous resampler has no quality settings, it always delivers the best quality.
104132

src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,41 @@ pub use crate::windows::{calculate_cutoff, WindowFunction};
6767

6868
/// A struct for providing optional parameters when calling
6969
/// [process_into_buffer](Resampler::process_into_buffer).
70+
///
71+
/// All fields have sensible defaults: zero offsets, no partial length, and all channels active.
72+
/// Pass `None` as the `indexing` argument to use these defaults without constructing the struct.
7073
#[derive(Debug, Clone)]
7174
pub struct Indexing {
75+
/// Number of frames to skip at the beginning of the input buffer before reading.
76+
/// Use this to process a sub-region of a larger buffer without copying data.
77+
/// Defaults to `0` (read from the start of the buffer).
7278
pub input_offset: usize,
79+
80+
/// Number of frames to skip at the beginning of the output buffer before writing.
81+
/// Use this to write results into a sub-region of a larger buffer.
82+
/// Defaults to `0` (write from the start of the buffer).
7383
pub output_offset: usize,
84+
85+
/// Set to `Some(n)` when the input buffer contains fewer valid frames than
86+
/// [Resampler::input_frames_next] requires.
87+
/// The resampler will read the first `n` frames from the input buffer and
88+
/// treat the remaining frames as silence.
89+
/// This is useful for processing the very last (partial) chunk of a stream or audio clip.
90+
///
91+
/// **Important:** even with `partial_len` set, the output buffer must still be large enough
92+
/// to hold at least [Resampler::output_frames_next] frames (plus `output_offset`),
93+
/// because the resampler always produces a full output chunk.
94+
///
95+
/// Set to `Some(0)` to process a chunk of pure silence (e.g. to flush the resampler delay).
96+
///
97+
/// Defaults to `None`, meaning the full [Resampler::input_frames_next] frames are read.
7498
pub partial_len: Option<usize>,
99+
100+
/// Optional per-channel processing mask.
101+
/// When `Some(vec)`, each element corresponds to one channel:
102+
/// `true` means the channel is processed normally,
103+
/// `false` means the channel is skipped and its output is left unchanged.
104+
/// When `None`, all channels are processed.
75105
pub active_channels_mask: Option<Vec<bool>>,
76106
}
77107

src/synchro.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,33 @@ where
203203
/// In this mode, the chunk size is not arbitrarily selectable.
204204
/// Instead, it is automatically calculated based on the provided value as a reference.
205205
///
206-
/// The delay from the resampler depends on the length of the FFT.
207-
/// It can be reduced by increasing the `sub_chunks` value.
208-
/// This determines how many sub chunks each chunk should be split into while processing.
209-
/// The actual number may be different,
210-
/// based on what is possible for the given input and output sample rates.
211-
/// A large number of sub chunks (i.e. short sub chunks) reduces the cutoff frequency
212-
/// of the anti-aliasing filter.
213-
/// It is recommended to set `sub_chunks` to 1 unless this leads to an unacceptably large delay.
206+
/// The resampler introduces a delay equal to half the internal FFT block size,
207+
/// expressed as a number of output frames.
208+
/// This delay can be queried via [Resampler::output_delay].
209+
///
210+
/// The `sub_chunks` parameter controls how many sub-chunks each processing chunk is divided into.
211+
/// A higher value uses a smaller FFT block size, which reduces the delay proportionally.
212+
/// However, a smaller FFT block also lowers the cutoff frequency of the anti-aliasing filter,
213+
/// which may affect quality at high frequencies.
214+
///
215+
/// The actual number of sub-chunks used may differ from the requested value,
216+
/// since the FFT block size must be a valid multiple of the minimum block size
217+
/// for the given sample rate pair.
218+
///
219+
/// A sub-chunk size (i.e. `chunk_size / sub_chunks`) between roughly 100 and 1000 frames
220+
/// is generally a good target. The best value depends on the delay tolerance and quality
221+
/// requirements of the application. If uncertain, start with a sub-chunk size of a few hundred
222+
/// frames — meaning `sub_chunks = chunk_size / desired_sub_chunk_size` — and adjust as needed.
223+
///
224+
/// The requested value is treated as a hint: the actual sub-chunk size is rounded up to the
225+
/// nearest integer multiple of the minimum valid block size for the given sample rate pair.
226+
/// The minimum block size is determined by the GCD (Greatest Common Divisor) of the two
227+
/// sample rates: `rate / gcd(rate_in, rate_out)` for each side.
228+
/// For example, a 44100 → 48000 resampler has a GCD of 300, giving minimum blocks of 147
229+
/// (input) and 160 (output), so sub-chunk sizes can only be multiples of those values.
230+
/// A 48000 → 96000 resampler has a GCD of 48000, giving minimum blocks of 1 and 2,
231+
/// so the sub-chunk size can be chosen almost freely.
232+
/// A value of `0` is treated as `1`.
214233
///
215234
/// Parameters are:
216235
/// - `sample_rate_input`: Input sample rate, must be > 0.

0 commit comments

Comments
 (0)