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>
impl<'a, S: Sample> AudioBuffer<'a, S>
Sourcepub fn from_slices_checked(
inputs: &'a [&'a [S]],
outputs: &'a mut [&'a mut [S]],
num_samples: usize,
) -> Self
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.
Sourcepub unsafe fn from_slices(
inputs: &'a [&'a [S]],
outputs: &'a mut [&'a mut [S]],
num_samples: usize,
) -> Self
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).
Sourcepub fn set_in_place_mask(&mut self, mask: u64)
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.
Sourcepub fn is_in_place(&self, ch: usize) -> bool
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.
Sourcepub fn in_out_mut(&mut self, ch: usize) -> &mut [S]
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.
pub fn num_samples(&self) -> usize
pub fn num_input_channels(&self) -> usize
pub fn num_output_channels(&self) -> usize
pub fn input(&self, channel: usize) -> &[S]
pub fn output(&mut self, channel: usize) -> &mut [S]
Sourcepub fn io_pair(&mut self, in_ch: usize, out_ch: usize) -> (&[S], &mut [S])
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.
Sourcepub fn io(&mut self, ch: usize) -> (&[S], &mut [S])
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).
Sourcepub fn chunks_mut<const N: usize>(&mut self) -> ChunksMut<'_, 'a, S, N>
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).
Sourcepub fn for_each_frame<const N: usize, F>(&mut self, tick: F)
pub fn for_each_frame<const N: usize, F>(&mut self, tick: F)
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.
Sourcepub fn output_peak(&self, ch: usize) -> f32
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.