Skip to content

Commit

Permalink
Fix max output length for FftFixedIn, improve tests for it
Browse files Browse the repository at this point in the history
  • Loading branch information
HEnquist committed Jun 23, 2023
1 parent cdf67cc commit a547ce2
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ The `rubato` crate requires rustc version 1.61 or newer.

## Changelog

- v0.14.1
- More bugfixes for buffer allocation and max output length calculation.
- v0.14.0
- Add argument to let `input/output_buffer_allocate()` optionally pre-fill buffers with zeros.
- Add convenience methods for managing buffers.
Expand Down
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
//!
//! # Changelog
//!
//! - v0.14.1
//! - More bugfixes for buffer allocation and max output length calculation.
//! - v0.14.0
//! - Add argument to let `input/output_buffer_allocate()` optionally pre-fill buffers with zeros.
//! - Add convenience methods for managing buffers.
Expand Down Expand Up @@ -728,8 +730,13 @@ pub mod tests {
($name:ident, $resampler:ident) => {
let mut val = 0.0;
let mut prev_last = -0.1;
for n in 0..5 {
let max_input_len = $resampler.input_frames_max();
let max_output_len = $resampler.output_frames_max();
for n in 0..50 {
let frames = $resampler.input_frames_next();
// Check that lengths are within the reported max values
assert!(frames <= max_input_len);
assert!($resampler.output_frames_next() <= max_output_len);
let mut waves = vec![vec![0.0f64; frames]; 2];
for m in 0..frames {
for ch in 0..2 {
Expand Down
95 changes: 93 additions & 2 deletions src/synchro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,8 +642,10 @@ where
}

fn output_frames_max(&self) -> usize {
(2.0 * self.chunk_size_in as f32 / self.fft_size_in as f32).floor() as usize
* self.fft_size_out
let max_stored_frames = self.fft_size_in - 1;
let max_available_frames = max_stored_frames + self.chunk_size_in;
let max_subchunks_to_process = max_available_frames / self.fft_size_in;
max_subchunks_to_process * self.fft_size_out
}

fn output_frames_next(&self) -> usize {
Expand Down Expand Up @@ -943,4 +945,93 @@ mod tests {
let mut resampler = FftFixedInOut::<f64>::new(44100, 48000, 4096, 2).unwrap();
check_output!(check_fo_output, resampler);
}

#[test]
fn check_fi_max_output_length() {
// parameters:
// - rate in
// - rate out
// - requested chunksize
// - requested number of subchunks
// - expected fft input length
// - expected fft output length
let params_to_test = [
// fft sizes < chunksize
[44100, 48000, 1024, 4, 294, 320],
[48000, 44100, 1024, 4, 320, 294],
// fft sizes << chunksize
[44000, 48000, 1024, 100, 11, 12],
// fft sizes > chunksize
[32728, 32000, 1024, 4, 4091, 4000],
[32000, 32728, 1024, 4, 4000, 4091],
// fft sizes >> chunksize
[37199, 39119, 1024, 4, 37199, 39119],
[39119, 37199, 1024, 4, 39119, 37199],
];
for params in params_to_test {
println!("params: {:?}", params);
let [rate_in, rate_out, chunksize, subchunks, fft_in_len, fft_out_len] = params;
let resampler =
FftFixedIn::<f64>::new(rate_in, rate_out, chunksize, subchunks, 1).unwrap();
assert_eq!(resampler.fft_size_in, fft_in_len);
assert_eq!(resampler.fft_size_out, fft_out_len);
let resampler_max_output_len = resampler.output_frames_max();
println!(
"Resampler reports max output length: {}",
resampler_max_output_len
);
assert!(resampler.output_frames_max() >= fft_out_len);
// expected length
let max_stored_frames = fft_in_len - 1;
let max_available_samples = max_stored_frames + chunksize;
let max_subchunks_to_process = max_available_samples / fft_in_len;
let expected_max_out_len = max_subchunks_to_process * fft_out_len;
println!("Max stored frames: {}, max avail frames: {}, max ready subchunks: {}, expected max output len: {}", max_stored_frames, max_available_samples, max_subchunks_to_process, expected_max_out_len);
assert_eq!(resampler.output_frames_max(), expected_max_out_len);
}
}

#[test]
fn check_fo_max_input_length() {
// parameters:
// - rate in
// - rate out
// - requested chunksize
// - requested number of subchunks
// - expected fft input length
// - expected fft output length
let params_to_test = [
// fft sizes < chunksize
[44100, 48000, 1024, 4, 294, 320],
[48000, 44100, 1024, 4, 320, 294],
// fft sizes << chunksize
[44000, 48000, 1024, 100, 11, 12],
// fft sizes > chunksize
[32728, 32000, 1024, 4, 4091, 4000],
[32000, 32728, 1024, 4, 4000, 4091],
// fft sizes >> chunksize
[37199, 39119, 1024, 4, 37199, 39119],
[39119, 37199, 1024, 4, 39119, 37199],
];
for params in params_to_test {
println!("params: {:?}", params);
let [rate_in, rate_out, chunksize, subchunks, fft_in_len, fft_out_len] = params;
let resampler =
FftFixedOut::<f64>::new(rate_in, rate_out, chunksize, subchunks, 1).unwrap();
assert_eq!(resampler.fft_size_in, fft_in_len);
assert_eq!(resampler.fft_size_out, fft_out_len);
let resampler_max_input_len = resampler.input_frames_max();
println!(
"Resampler reports max input length: {}",
resampler_max_input_len
);
assert!(resampler.input_frames_max() >= fft_in_len);
// max needed is when we have none stored
let max_frames_needed = chunksize;
let max_subchunks_needed = (max_frames_needed as f32 / fft_out_len as f32).ceil() as usize;
let expected_max_in_len = max_subchunks_needed * fft_in_len;
println!("Max frames needed: {}, max subchunks_needed: {}, expected max input len: {}", max_frames_needed, max_subchunks_needed, expected_max_in_len);
assert_eq!(resampler.input_frames_max(), expected_max_in_len);
}
}
}

0 comments on commit a547ce2

Please sign in to comment.