Skip to content

Commit

Permalink
Merge pull request #62 from HEnquist/allocatebug
Browse files Browse the repository at this point in the history
Allocatebug
  • Loading branch information
HEnquist authored Jun 10, 2023
2 parents 10fd519 + 926c66a commit cdf67cc
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rubato"
version = "0.13.0"
version = "0.14.0"
rust-version = "1.61"
authors = ["HEnquist <henrik.enquist@gmail.com>"]
description = "Asynchronous resampling library intended for audio data"
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ The `rubato` crate requires rustc version 1.61 or newer.

## Changelog

- v0.14.0
- Add argument to let `input/output_buffer_allocate()` optionally pre-fill buffers with zeros.
- Add convenience methods for managing buffers.
- Bugfixes for buffer allocation and max output length calculation.
- v0.13.0
- Switch to slices of references for input and output data.
- Add faster (lower quality) asynchronous resamplers.
Expand Down
10 changes: 5 additions & 5 deletions examples/fastfixedin_ramp64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ fn read_frames<R: Read + Seek>(inbuffer: &mut R, nbr: usize, channels: usize) ->
let mut value: f64;
for _frame in 0..nbr {
for wf in wfs.iter_mut().take(channels) {
inbuffer.read(&mut buffer).unwrap();
value = f64::from_le_bytes(buffer.as_slice().try_into().unwrap()) as f64;
inbuffer.read_exact(&mut buffer).unwrap();
value = f64::from_le_bytes(buffer.as_slice().try_into().unwrap());
//idx += 8;
wf.push(value);
}
Expand All @@ -51,10 +51,10 @@ fn read_frames<R: Read + Seek>(inbuffer: &mut R, nbr: usize, channels: usize) ->
fn write_frames<W: Write + Seek>(waves: Vec<Vec<f64>>, outbuffer: &mut W, channels: usize) {
let nbr = waves[0].len();
for frame in 0..nbr {
for chan in 0..channels {
let value64 = waves[chan][frame];
for wave in waves.iter().take(channels) {
let value64 = wave[frame];
let bytes = value64.to_le_bytes();
outbuffer.write(&bytes).unwrap();
outbuffer.write_all(&bytes).unwrap();
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions examples/fixedout_ramp64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ fn read_frames<R: Read + Seek>(inbuffer: &mut R, nbr: usize, channels: usize) ->
if inbuffer.read(&mut buffer).unwrap() < 8 {
return wfs;
}
value = f64::from_le_bytes(buffer.as_slice().try_into().unwrap()) as f64;
value = f64::from_le_bytes(buffer.as_slice().try_into().unwrap());
//idx += 8;
wf.push(value);
}
Expand All @@ -56,10 +56,10 @@ fn read_frames<R: Read + Seek>(inbuffer: &mut R, nbr: usize, channels: usize) ->
fn write_frames<W: Write + Seek>(waves: Vec<Vec<f64>>, outbuffer: &mut W, channels: usize) {
let nbr = waves[0].len();
for frame in 0..nbr {
for chan in 0..channels {
let value64 = waves[chan][frame];
for wave in waves.iter().take(channels) {
let value64 = wave[frame];
let bytes = value64.to_le_bytes();
outbuffer.write(&bytes).unwrap();
outbuffer.write_all(&bytes).unwrap();
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions examples/process_f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn read_file<R: Read + Seek>(inbuffer: &mut R, channels: usize) -> Vec<Vec<f64>>
if bytes_read == 0 {
break 'outer;
}
let value = f64::from_le_bytes(buffer.as_slice().try_into().unwrap()) as f64;
let value = f64::from_le_bytes(buffer.as_slice().try_into().unwrap());
//idx += 8;
wf.push(value);
}
Expand All @@ -65,10 +65,10 @@ fn write_frames<W: Write + Seek>(
let channels = waves.len();
let end = frames_to_skip + frames_to_write;
for frame in frames_to_skip..end {
for chan in 0..channels {
let value64 = waves[chan][frame];
for wave in waves.iter().take(channels) {
let value64 = wave[frame];
let bytes = value64.to_le_bytes();
output.write(&bytes).unwrap();
output.write_all(&bytes).unwrap();
}
}
}
Expand Down
98 changes: 82 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@
//!
//! # Changelog
//!
//! - v0.14.0
//! - Add argument to let `input/output_buffer_allocate()` optionally pre-fill buffers with zeros.
//! - Add convenience methods for managing buffers.
//! - Bugfixes for buffer allocation and max output length calculation.
//! - v0.13.0
//! - Switch to slices of references for input and output data.
//! - Add faster (lower quality) asynchronous resamplers.
Expand Down Expand Up @@ -242,7 +246,7 @@ where
/// The input and output buffers are noninterleaved.
/// The input is a slice, where each element of the slice is itself referenceable
/// as a slice ([AsRef<\[T\]>](AsRef)) which contains the samples for a single channel.
/// Because [Vec<T>] implements [AsRef<\[T\]>](AsRef), the input may be [`Vec<Vec<T>>`](Vec).
/// Because `[Vec<T>]` implements [`AsRef<\[T\]>`](AsRef), the input may be [`Vec<Vec<T>>`](Vec).
///
/// The output data is a slice, where each element of the slice is a `[T]` which contains
/// the samples for a single channel. If the output channel slices do not have sufficient
Expand Down Expand Up @@ -337,14 +341,13 @@ where
/// is big enough to prevent allocating additional heap memory before any call to
/// [process_into_buffer](Resampler::process_into_buffer) regardless of the current
/// resampling ratio.
fn input_buffer_allocate(&self) -> Vec<Vec<T>> {
///
/// The `filled` argument determines if the vectors should be pre-filled with zeros or not.
/// When false, the vectors are only allocated but returned empty.
fn input_buffer_allocate(&self, filled: bool) -> Vec<Vec<T>> {
let frames = self.input_frames_max();
let channels = self.nbr_channels();
let mut buffer = Vec::with_capacity(channels);
for _ in 0..channels {
buffer.push(Vec::with_capacity(frames));
}
buffer
make_buffer(channels, frames, filled)
}

/// Get the maximum number of input frames per channel the resampler could require
Expand All @@ -362,10 +365,13 @@ where
/// is big enough to prevent allocating additional heap memory during any call to
/// [process_into_buffer](Resampler::process_into_buffer) regardless of the current
/// resampling ratio.
fn output_buffer_allocate(&self) -> Vec<Vec<T>> {
///
/// The `filled` argument determines if the vectors should be pre-filled with zeros or not.
/// When false, the vectors are only allocated but returned empty.
fn output_buffer_allocate(&self, filled: bool) -> Vec<Vec<T>> {
let frames = self.output_frames_max();
let channels = self.nbr_channels();
vec![Vec::with_capacity(frames); channels]
make_buffer(channels, frames, filled)
}

/// Get the max number of output frames per channel
Expand Down Expand Up @@ -462,7 +468,7 @@ macro_rules! implement_resampler {
) -> rubato::ResampleResult<Vec<Vec<T>>>;

/// Refer to [Resampler::input_buffer_allocate]
fn input_buffer_allocate(&self) -> Vec<Vec<T>>;
fn input_buffer_allocate(&self, filled: bool) -> Vec<Vec<T>>;

/// Refer to [Resampler::input_frames_max]
fn input_frames_max(&self) -> usize;
Expand All @@ -474,7 +480,7 @@ macro_rules! implement_resampler {
fn nbr_channels(&self) -> usize;

/// Refer to [Resampler::output_buffer_allocate]
fn output_buffer_allocate(&self) -> Vec<Vec<T>>;
fn output_buffer_allocate(&self, filled: bool) -> Vec<Vec<T>>;

/// Refer to [Resampler::output_frames_max]
fn output_frames_max(&self) -> usize;
Expand Down Expand Up @@ -536,8 +542,8 @@ macro_rules! implement_resampler {
rubato::Resampler::process_partial(self, wave_in, active_channels_mask)
}

fn output_buffer_allocate(&self) -> Vec<Vec<T>> {
rubato::Resampler::output_buffer_allocate(self)
fn output_buffer_allocate(&self, filled: bool) -> Vec<Vec<T>> {
rubato::Resampler::output_buffer_allocate(self, filled)
}

fn output_frames_next(&self) -> usize {
Expand All @@ -564,8 +570,8 @@ macro_rules! implement_resampler {
rubato::Resampler::input_frames_max(self)
}

fn input_buffer_allocate(&self) -> Vec<Vec<T>> {
rubato::Resampler::input_buffer_allocate(self)
fn input_buffer_allocate(&self, filled: bool) -> Vec<Vec<T>> {
rubato::Resampler::input_buffer_allocate(self, filled)
}

fn set_resample_ratio(&mut self, new_ratio: f64, ramp: bool) -> rubato::ResampleResult<()> {
Expand Down Expand Up @@ -639,9 +645,48 @@ pub(crate) fn validate_buffers<T, Vin: AsRef<[T]>, Vout: AsMut<[T]>>(
Ok(())
}

/// Convenience method for allocating a buffer to hold a given number of channels and frames.
/// The `filled` argument determines if the vectors should be pre-filled with zeros or not.
/// When false, the vectors are only allocated but returned empty.
pub fn make_buffer<T: Sample>(channels: usize, frames: usize, filled: bool) -> Vec<Vec<T>> {
let mut buffer = Vec::with_capacity(channels);
for _ in 0..channels {
buffer.push(Vec::with_capacity(frames));
}
if filled {
resize_buffer(&mut buffer, frames)
}
buffer
}

/// Convenience method for resizing a buffer to new number of frames.
/// If the new number is no larger than the buffer capacity,
/// then no reallocation will occur.
/// If the new length is smaller than the current, the excess elements are dropped.
/// If it is larger, zeros are inserted for the missing elements.
pub fn resize_buffer<T: Sample>(buffer: &mut [Vec<T>], frames: usize) {
buffer.iter_mut().for_each(|v| v.resize(frames, T::zero()));
}

/// Convenience method for getting the current length of a buffer in frames.
/// Checks the [length](Vec::len) of the vector for each channel and returns the smallest.
pub fn buffer_length<T: Sample>(buffer: &[Vec<T>]) -> usize {
return buffer.iter().map(|v| v.len()).min().unwrap_or_default();
}

/// Convenience method for getting the current allocated capacity of a buffer in frames.
/// Checks the [capacity](Vec::capacity) of the vector for each channel and returns the smallest.
pub fn buffer_capacity<T: Sample>(buffer: &[Vec<T>]) -> usize {
return buffer
.iter()
.map(|v| v.capacity())
.min()
.unwrap_or_default();
}

#[cfg(test)]
pub mod tests {
use crate::VecResampler;
use crate::{buffer_capacity, buffer_length, make_buffer, resize_buffer, VecResampler};
use crate::{FftFixedIn, FftFixedInOut, FftFixedOut};
use crate::{SincFixedIn, SincFixedOut};

Expand Down Expand Up @@ -729,4 +774,25 @@ pub mod tests {
}
};
}

#[test]
fn test_buffer_helpers() {
let buf1 = vec![vec![0.0f64; 7], vec![0.0f64; 5], vec![0.0f64; 10]];
assert_eq!(buffer_length(&buf1), 5);
let mut buf2 = vec![Vec::<f32>::with_capacity(5), Vec::<f32>::with_capacity(15)];
assert_eq!(buffer_length(&buf2), 0);
assert_eq!(buffer_capacity(&buf2), 5);

resize_buffer(&mut buf2, 3);
assert_eq!(buffer_length(&buf2), 3);
assert_eq!(buffer_capacity(&buf2), 5);

let buf3 = make_buffer::<f32>(4, 10, false);
assert_eq!(buffer_length(&buf3), 0);
assert_eq!(buffer_capacity(&buf3), 10);

let buf4 = make_buffer::<f32>(4, 10, true);
assert_eq!(buffer_length(&buf4), 10);
assert_eq!(buffer_capacity(&buf4), 10);
}
}
3 changes: 2 additions & 1 deletion src/synchro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,8 @@ where
}

fn output_frames_max(&self) -> usize {
self.chunk_size_in * (self.fft_size_out / self.fft_size_in + 1)
(2.0 * self.chunk_size_in as f32 / self.fft_size_in as f32).floor() as usize
* self.fft_size_out
}

fn output_frames_next(&self) -> usize {
Expand Down

0 comments on commit cdf67cc

Please sign in to comment.