Skip to main content

AudioBuffer

Struct AudioBuffer 

Source
pub struct AudioBuffer<'a, S: Sample = f32> { /* private fields */ }
Expand description

Non-interleaved audio buffer. Borrows host memory through the format wrapper.

Generic over the sample type S (the plugin’s chosen precision, f32 or f64). The format wrapper bridges between host-buffer precision and S at the block boundary - see RawBufferScratch::build. Plugin code under use truce::prelude::*; (f32) or use truce::prelude64::*; (f64) sees AudioBuffer<S> with S already picked.

In-place I/O. Some hosts (Reaper, pluginval) pass the same buffer for both input and output of a given channel. By default the wrapper copies the aliased inputs into per-channel scratch so input(ch) and output(ch) are disjoint &[S] / &mut [S] - no plugin code change required. Plugins that opt into Plugin::supports_in_place() = true skip the copy and must use Self::in_out_mut for channels where Self::is_in_place returns true.

Implementations§

Source§

impl<'a, S: Sample> AudioBuffer<'a, S>

Source

pub fn from_slices_checked( inputs: &'a [&'a [S]], outputs: &'a mut [&'a mut [S]], num_samples: usize, ) -> Self

Safe wrapper around Self::from_slices for callers that hold their own owned Vec<Vec<S>> (e.g. truce-driver’s test harness). Forwards to the unsafe constructor - the borrow checker proves the lifetime invariants the unsafe fn requires when both slice arrays and the buffer itself live in the same scope. num_samples > slice length still asserts in debug builds.

Source

pub unsafe fn from_slices( inputs: &'a [&'a [S]], outputs: &'a mut [&'a mut [S]], num_samples: usize, ) -> Self

Create a buffer from pre-split channel slices. Used by format wrappers after converting from host-specific buffer types.

§Safety

The caller must ensure the slices are valid for the lifetime 'a and that num_samples does not exceed any slice’s length.

§Panics

In debug builds only, panics if any input channel aliases an output channel or num_samples exceeds the length of any input/output slice. Release builds skip these checks (they’re safety preconditions, not runtime invariants).

Source

pub fn set_in_place_mask(&mut self, mask: u64)

Set the in-place mask. Called by format wrappers (or RawBufferScratch::build) after construction once they’ve determined which channels alias on the host side.

Source

pub fn is_in_place(&self, ch: usize) -> bool

true when the host passes a single buffer for both input and output of ch (in-place I/O). Use Self::in_out_mut to read and write that buffer directly when this returns true.

Source

pub fn in_out_mut(&mut self, ch: usize) -> &mut [S]

Read+write slice for an in-place channel - the same memory the host gave us for both input and output. Each sample reads as the input value before the plugin overwrites it.

Only meaningful when Self::is_in_place returns true. On a non-in-place channel this returns the output slice with no input data in it; reading is allowed but produces uninitialized host-buffer contents.

Source

pub fn num_samples(&self) -> usize

Source

pub fn num_input_channels(&self) -> usize

Source

pub fn num_output_channels(&self) -> usize

Source

pub fn input(&self, channel: usize) -> &[S]

Source

pub fn output(&mut self, channel: usize) -> &mut [S]

Source

pub fn channels(&self) -> usize

Number of channels (min of input and output).

Source

pub fn io_pair(&mut self, in_ch: usize, out_ch: usize) -> (&[S], &mut [S])

Get an input/output pair for a channel. Useful for in-place processing.

Source

pub fn io(&mut self, ch: usize) -> (&[S], &mut [S])

Get an input/output pair for the same channel index. Shorthand for io_pair(ch, ch).

Source

pub fn chunks_mut<const N: usize>(&mut self) -> ChunksMut<'_, 'a, S, N>

Iterate per-channel, in fixed-size N-sample chunks. The last chunk of each channel may be shorter than N; it’s yielded as a ChunkItem::Tail with the actual remaining length, and the caller falls back to scalar for it. Full N-sample chunks arrive as ChunkItem::Full carrying &[S; N] / &mut [S; N] stack arrays - exactly the shape the per-op SIMD primitives in truce-simd expect.

Iteration order is channel-major: all chunks of channel 0, then all chunks of channel 1, etc. Matches the natural orientation for per-channel state (biquad coefficients, per-channel meters) and lets the caller read its smoothed params once per chunk instead of once per sample.

The returned object is a “lending iterator” - it doesn’t implement Iterator because each yielded item borrows from the iterator itself. Use while let Some(chunk) = … .next():

let mut chunks = buffer.chunks_mut::<32>();
while let Some(chunk) = chunks.next() {
    match chunk {
        ChunkItem::Full { ch, inp, out } => {
            // SIMD-friendly path, inp / out are &[f32; 32]
        }
        ChunkItem::Tail { ch, inp, out } => {
            // scalar fallback for the trailing samples
        }
    }
}

Const-generic N is the chunk size; pick it to match the SIMD width × unroll factor for your inner op (32 / 64 are good defaults for current Apple Silicon + x86_64).

Source

pub fn for_each_frame<const N: usize, F>(&mut self, tick: F)
where F: FnMut(&[S; N], &mut [S; N]),

Iterate per-frame and hand a fixed-size (input, output) stack-array pair to tick. Sized at the type level by const generic N, which must equal Self::channels.

io() / io_pair() give a per-channel slice view, which is the right shape for “process channel ch in isolation” loops. But libraries that expect a per-frame (in: &[S], out: &mut [S]) callback - fundsp::AudioUnit::tick, nih_plug’s frame iterators, custom per-sample DSP nodes - can’t take that shape directly without either copying inputs into a scratch first (heap allocation on the audio thread) or fighting the borrow checker over two simultaneous &mut borrows of the buffer.

This helper does the per-frame transpose in-place against a stack-allocated [S; N] pair, calls tick num_samples() times, and writes back. No heap, no borrow gymnastics at the call site:

// Stereo plugin delegating per-frame DSP to fundsp:
buffer.for_each_frame::<2, _>(|frame_in, frame_out| {
    self.graph.tick(frame_in, frame_out);
});

&[S; N] deref-coerces to &[S] at the call site, so callers can pass the arrays straight to slice-taking APIs like fundsp’s tick.

§Panics

Debug builds panic if N != self.channels(). Release builds rely on the same precondition without checking; reading past the actual channel count would index out of bounds anyway.

Source

pub fn output_peak(&self, ch: usize) -> f32

Peak absolute value across an output channel, returned as f32 because meters / UI display always work in f32 regardless of the plugin’s internal precision.

Short-circuits and returns f32::NAN on the first NaN sample seen, so meters can flag runaway plugins instead of silently reporting “peaks within range” while NaN poison spreads downstream.

Source

pub fn slice(&mut self, start: usize, len: usize) -> AudioBuffer<'_, S>

Return a sub-block view covering samples start..start+len.

The returned buffer borrows self exclusively - you cannot use the original buffer while the slice is alive.

§Panics

Panics if start + len > self.num_samples().

Auto Trait Implementations§

§

impl<'a, S> Freeze for AudioBuffer<'a, S>

§

impl<'a, S> RefUnwindSafe for AudioBuffer<'a, S>
where S: RefUnwindSafe,

§

impl<'a, S> Send for AudioBuffer<'a, S>

§

impl<'a, S> Sync for AudioBuffer<'a, S>

§

impl<'a, S> Unpin for AudioBuffer<'a, S>

§

impl<'a, S> UnsafeUnpin for AudioBuffer<'a, S>

§

impl<'a, S = f32> !UnwindSafe for AudioBuffer<'a, S>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.