Skip to main content

truce_rack_core/
buffer.rs

1//! Planar audio buffer passed into [`crate::Plugin::process`].
2//!
3//! `AudioBuffer<S>` is the host-side view of the slices the
4//! plugin reads from and writes into. Channels are organised
5//! by bus to match how host SDKs natively expose audio — no
6//! interleave / deinterleave on the hot path.
7//!
8//! The lifetime `'a` is the block lifetime: every slice borrows
9//! from buffers the host owns. Plugins receive `&mut AudioBuffer`
10//! and may write through the output slices but cannot extend
11//! the borrows past the `process` call.
12
13use crate::sample::Sample;
14
15/// One channel of input audio.
16pub type InputChannel<'a, S> = &'a [S];
17
18/// One channel of output audio.
19pub type OutputChannel<'a, S> = &'a mut [S];
20
21/// Per-bus channel range into the buffer's flat channel arrays.
22///
23/// Format wrappers construct slices of these and hand them to
24/// [`AudioBuffer::new`]; plugin code does not need to look at
25/// them directly (use [`AudioBuffer::bus_inputs`] /
26/// [`AudioBuffer::bus_outputs`]).
27#[derive(Debug, Clone, Copy)]
28pub struct BusRange {
29    /// Inclusive start index into the flat channel array.
30    start: usize,
31    /// Number of channels in this bus.
32    len: usize,
33}
34
35/// Mutable, planar audio buffer for one `process` block.
36///
37/// Channels are flat across buses internally; `bus_inputs(0)` /
38/// `bus_outputs(0)` slice into the main bus, higher indices into
39/// sidechains and auxiliaries. The flat-then-sliced shape matches
40/// host SDK conventions (CLAP's `clap_audio_buffer`, VST3's
41/// `ProcessData`, AU's `AudioBufferList`).
42///
43/// `S` is the sample precision — `f32` for CLAP / VST2 / LV2 /
44/// AAX, `f32` or `f64` for VST3 / AU at the host's choice.
45pub struct AudioBuffer<'a, S: Sample> {
46    /// One slice per input channel, in bus-order.
47    inputs: &'a [&'a [S]],
48    /// One slice per output channel, in bus-order. The outer slice
49    /// is mutable so the plugin can write through it; the inner
50    /// `[S]` borrows are independent so two output buses don't
51    /// alias.
52    outputs: &'a mut [&'a mut [S]],
53    /// Number of frames in this block. All channel slices are
54    /// exactly this long.
55    num_frames: usize,
56    /// Per-input-bus channel ranges. `bus_inputs[k]` gives the
57    /// `start..start+len` range into `inputs`.
58    bus_inputs: &'a [BusRange],
59    /// Per-output-bus channel ranges into `outputs`.
60    bus_outputs: &'a [BusRange],
61}
62
63impl<'a, S: Sample> AudioBuffer<'a, S> {
64    /// Build a buffer from raw slices.
65    ///
66    /// Format wrappers call this once per block from their
67    /// `process` callback. Plugin code receives the buffer; only
68    /// wrappers construct one.
69    ///
70    /// # Panics
71    ///
72    /// Panics in debug builds if any channel slice is shorter
73    /// than `num_frames` or the bus ranges don't cover the
74    /// channel arrays exactly. Release builds elide the checks —
75    /// wrappers are expected to maintain the invariants.
76    #[must_use]
77    pub fn new(
78        inputs: &'a [&'a [S]],
79        outputs: &'a mut [&'a mut [S]],
80        num_frames: usize,
81        bus_inputs: &'a [BusRange],
82        bus_outputs: &'a [BusRange],
83    ) -> Self {
84        debug_assert!(
85            inputs.iter().all(|c| c.len() >= num_frames),
86            "all input channels must have at least num_frames samples"
87        );
88        debug_assert!(
89            outputs.iter().all(|c| c.len() >= num_frames),
90            "all output channels must have at least num_frames samples"
91        );
92        debug_assert_eq!(
93            bus_inputs.iter().map(|r| r.len).sum::<usize>(),
94            inputs.len(),
95            "input bus ranges must partition the channel array"
96        );
97        debug_assert_eq!(
98            bus_outputs.iter().map(|r| r.len).sum::<usize>(),
99            outputs.len(),
100            "output bus ranges must partition the channel array"
101        );
102        Self {
103            inputs,
104            outputs,
105            num_frames,
106            bus_inputs,
107            bus_outputs,
108        }
109    }
110
111    /// Block length in samples (frames). Every channel slice is
112    /// exactly this long.
113    #[must_use]
114    pub fn num_frames(&self) -> usize {
115        self.num_frames
116    }
117
118    /// Number of input buses (including main + sidechains).
119    #[must_use]
120    pub fn num_input_buses(&self) -> usize {
121        self.bus_inputs.len()
122    }
123
124    /// Number of output buses.
125    #[must_use]
126    pub fn num_output_buses(&self) -> usize {
127        self.bus_outputs.len()
128    }
129
130    /// Total input channels across every bus. Useful for the
131    /// "loop over flat channels" pattern when bus layout doesn't
132    /// matter for the operation.
133    #[must_use]
134    pub fn total_input_channels(&self) -> usize {
135        self.inputs.len()
136    }
137
138    /// Total output channels across every bus.
139    #[must_use]
140    pub fn total_output_channels(&self) -> usize {
141        self.outputs.len()
142    }
143
144    /// Input channels for one bus. `bus_index` is 0 for the main
145    /// input; higher indices for sidechains / auxiliaries.
146    ///
147    /// # Panics
148    ///
149    /// Panics if `bus_index >= num_input_buses()`.
150    #[must_use]
151    pub fn bus_inputs(&self, bus_index: usize) -> &[InputChannel<'a, S>] {
152        let range = self.bus_inputs[bus_index];
153        &self.inputs[range.start..range.start + range.len]
154    }
155
156    /// Mutable output channels for one bus.
157    ///
158    /// # Panics
159    ///
160    /// Panics if `bus_index >= num_output_buses()`.
161    pub fn bus_outputs(&mut self, bus_index: usize) -> &mut [&'a mut [S]] {
162        let range = self.bus_outputs[bus_index];
163        &mut self.outputs[range.start..range.start + range.len]
164    }
165
166    /// Shortcut: input channels of the main bus (index 0). Most
167    /// effects use this; reach for [`Self::bus_inputs`] when you
168    /// need sidechains too.
169    #[must_use]
170    pub fn main_inputs(&self) -> &[InputChannel<'a, S>] {
171        if self.bus_inputs.is_empty() {
172            &[]
173        } else {
174            self.bus_inputs(0)
175        }
176    }
177
178    /// Shortcut: mutable output channels of the main bus.
179    pub fn main_outputs(&mut self) -> &mut [&'a mut [S]] {
180        debug_assert!(
181            !self.bus_outputs.is_empty(),
182            "main_outputs called on a buffer with no output buses"
183        );
184        self.bus_outputs(0)
185    }
186}
187
188impl BusRange {
189    /// Construct a range from a `(start, len)` pair. Format
190    /// wrappers call this once per bus when building the slice
191    /// they pass to [`AudioBuffer::new`].
192    #[must_use]
193    pub const fn new(start: usize, len: usize) -> Self {
194        Self { start, len }
195    }
196}