Skip to main content

audiograph/
buffer.rs

1use crate::{AudioGraphError, channel::ChannelSelection, sample::Sample};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct FrameSize(pub usize);
6
7/// Trait representing a buffer of audio samples organized by channels
8///
9/// Audiograph expects audio to be organized in a channel-based format where each channel stores its samples
10/// in contiguous memory. Implementations for the most common use cases are provided (see [`MultiChannelBuffer`]
11/// for owned buffers and [`MultiChannelBufferView`] for non-owning views), but you can implement this trait for
12/// your own custom buffer types as needed.
13pub trait AudioBuffer<T: Sample> {
14    /// Returns the number of channels in the buffer
15    fn num_channels(&self) -> usize;
16
17    /// Returns the number of frames (samples per channel) in the buffer
18    fn num_frames(&self) -> FrameSize;
19
20    /// Returns a slice of samples for the specified channel index, or `None` if the index is out of bounds
21    fn channel(&self, index: usize) -> Option<&[T]>;
22
23    /// Returns a mutable slice of samples for the specified channel index, or `None` if the index is out of bounds
24    fn channel_mut(&mut self, index: usize) -> Option<&mut [T]>;
25
26    /// Clears the buffer, setting all samples to zero
27    fn clear(&mut self);
28
29    /// Interleaves the audio buffer into the provided output slice
30    ///
31    /// Returns the number of samples written to the output buffer or an error if the output buffer is too small.
32    fn copy_to_interleaved(&self, output: &mut [T]) -> Result<usize, AudioGraphError> {
33        let num_samples = self.num_channels() * self.num_frames().0;
34        if output.len() < num_samples {
35            return Err("Output buffer is too small");
36        }
37
38        for channel in 0..self.num_channels() {
39            let src_channel = self.channel(channel).unwrap();
40            for (frame, &sample) in src_channel.iter().enumerate() {
41                output[frame * self.num_channels() + channel] = sample;
42            }
43        }
44
45        Ok(num_samples)
46    }
47
48    /// Copies interleaved audio data from the input slice into the deinterleaved buffer format
49    ///
50    /// Returns the number of frames processed, or an error if the number of channels or the size
51    /// of the input buffer exceeds the capacity of this buffer.
52    /// Clears any remaining channels if num_channels is less than the number of channels in this buffer.
53    /// Errors out if the provided input buffer size is not a multiple of the provided number of channels.
54    fn copy_from_interleaved(
55        &mut self,
56        input: &[T],
57        num_channels: usize,
58    ) -> Result<FrameSize, AudioGraphError> {
59        if num_channels > self.num_channels() {
60            return Err("Input channel count exceeds buffer channel count");
61        }
62
63        if num_channels == 0 {
64            self.clear();
65            return Ok(FrameSize(0));
66        }
67
68        if !input.len().is_multiple_of(num_channels) {
69            return Err("Input buffer size must be a multiple of the number of channels");
70        }
71
72        let max_num_samples = num_channels * self.num_frames().0;
73        if input.len() > max_num_samples {
74            return Err("Input buffer is too large");
75        }
76
77        let num_frames_processed = input.len() / num_channels;
78
79        for channel in 0..num_channels {
80            let dst_channel = self.channel_mut(channel).unwrap();
81            for (frame, &sample) in input.iter().skip(channel).step_by(num_channels).enumerate() {
82                dst_channel[frame] = sample;
83            }
84        }
85
86        for channel in num_channels..self.num_channels() {
87            let dst_channel = self.channel_mut(channel).unwrap();
88            for sample in dst_channel.iter_mut() {
89                *sample = T::zero();
90            }
91        }
92
93        Ok(FrameSize(num_frames_processed))
94    }
95
96    /// Sums channels from another buffer into this buffer, optionally using a channel selection to specify which channels to sum.
97    fn add(&mut self, other: &dyn AudioBuffer<T>, channel_selection: &Option<ChannelSelection>) {
98        if let Some(selection) = channel_selection {
99            let mut filtered_selection = selection.clone();
100            filtered_selection.clamp(self.num_channels().min(other.num_channels()));
101            for channel in filtered_selection.iter() {
102                let src = other.channel(channel).unwrap();
103                let dst = self.channel_mut(channel).unwrap();
104                dst.iter_mut().zip(src.iter()).for_each(|(a, b)| {
105                    *a += *b;
106                });
107            }
108        } else {
109            let num_channels = self.num_channels().min(other.num_channels());
110            for channel in 0..num_channels {
111                let src = other.channel(channel).unwrap();
112                let dst = self.channel_mut(channel).unwrap();
113                dst.iter_mut().zip(src.iter()).for_each(|(a, b)| {
114                    *a += *b;
115                });
116            }
117        }
118    }
119}
120
121/// Implementation of [`AudioBuffer`] that owns the samples
122pub struct MultiChannelBuffer<T: Sample> {
123    channels: Vec<Box<[T]>>,
124    num_frames: FrameSize,
125}
126
127impl<T: Sample> MultiChannelBuffer<T> {
128    /// Construct a new multi channel buffer by pre-allocating internal buffers with zeros
129    pub fn new(num_channels: usize, num_frames: FrameSize) -> Self {
130        let mut channels = Vec::with_capacity(num_channels);
131        for _ in 0..num_channels {
132            channels.push(vec![T::zero(); num_frames.0].into_boxed_slice());
133        }
134        Self {
135            channels,
136            num_frames,
137        }
138    }
139}
140
141impl<T: Sample> AudioBuffer<T> for MultiChannelBuffer<T> {
142    fn num_channels(&self) -> usize {
143        self.channels.len()
144    }
145
146    fn num_frames(&self) -> FrameSize {
147        self.num_frames
148    }
149
150    fn channel(&self, index: usize) -> Option<&[T]> {
151        self.channels.get(index).map(|b| &**b)
152    }
153
154    fn channel_mut(&mut self, index: usize) -> Option<&mut [T]> {
155        self.channels.get_mut(index).map(|b| &mut **b)
156    }
157
158    fn clear(&mut self) {
159        for channel in self.channels.iter_mut() {
160            for sample in channel.iter_mut() {
161                *sample = T::zero();
162            }
163        }
164    }
165}
166
167/// Non-owning view into a channel-based collection of audio samples
168///
169/// Useful for zero-copy processing of immutable (input) audio data. Example:
170/// fn channel_based_callback<'a>(data: &[&[f32]]) {
171///     let buffer_view = MultiChannelBufferView::new(data, FrameSize(data\[0\].len()));
172/// }
173pub struct MultiChannelBufferView<'a, T: Sample> {
174    channels: &'a [&'a [T]],
175    num_frames: FrameSize,
176}
177
178impl<'a, T: Sample> MultiChannelBufferView<'a, T> {
179    /// Construct the view from a slice of sample data slices. Does not allocate.
180    pub fn new(channels: &'a [&'a [T]], num_frames: FrameSize) -> Self {
181        Self {
182            channels,
183            num_frames,
184        }
185    }
186}
187
188impl<T: Sample> AudioBuffer<T> for MultiChannelBufferView<'_, T> {
189    fn num_channels(&self) -> usize {
190        self.channels.len()
191    }
192
193    fn num_frames(&self) -> FrameSize {
194        self.num_frames
195    }
196
197    fn channel(&self, index: usize) -> Option<&[T]> {
198        self.channels.get(index).map(|b| &**b)
199    }
200
201    /// Always returns None as this is an immutable view type
202    fn channel_mut(&mut self, _index: usize) -> Option<&mut [T]> {
203        None
204    }
205
206    fn clear(&mut self) {}
207}
208
209/// Non-owning mutable view into a channel-based collection of audio samples.
210///
211/// Useful for zero-copy processing of mutable (output) audio data. Example:
212/// fn channel_based_callback<'a>(data: &'a mut [&'a mut [f32]]) {
213///     let mut mutable_buffer_view = MultiChannelBufferViewMut::new(data, FrameSize(data\[0\].len()));
214/// }
215pub struct MultiChannelBufferViewMut<'a, T: Sample> {
216    channels: &'a mut [&'a mut [T]],
217    num_frames: FrameSize,
218}
219
220impl<'a, T: Sample> MultiChannelBufferViewMut<'a, T> {
221    /// Construct a mutable buffer view from a slice of mutable sample data slices. Does not allocate.
222    pub fn new(channels: &'a mut [&'a mut [T]], num_frames: FrameSize) -> Self {
223        assert!(!channels.is_empty());
224        Self {
225            channels,
226            num_frames,
227        }
228    }
229}
230
231impl<T: Sample> AudioBuffer<T> for MultiChannelBufferViewMut<'_, T> {
232    fn num_channels(&self) -> usize {
233        self.channels.len()
234    }
235
236    fn num_frames(&self) -> FrameSize {
237        self.num_frames
238    }
239
240    fn channel(&self, index: usize) -> Option<&[T]> {
241        self.channels.get(index).map(|b| &**b)
242    }
243
244    fn channel_mut(&mut self, index: usize) -> Option<&mut [T]> {
245        self.channels.get_mut(index).map(|b| &mut **b)
246    }
247
248    fn clear(&mut self) {
249        for channel in &mut *self.channels {
250            for sample in channel.iter_mut() {
251                *sample = T::zero();
252            }
253        }
254    }
255}
256
257/// Immutable AudioBuffer view that remaps channel indices
258///
259/// TODO: Define and use a Rewire type here and in the rewire graph
260pub struct RewiredBufferView<'a, T: Sample> {
261    pub buffer: &'a dyn AudioBuffer<T>,
262    pub rewire: &'a HashMap<usize, usize>,
263}
264
265impl<T: Sample> AudioBuffer<T> for RewiredBufferView<'_, T> {
266    fn num_channels(&self) -> usize {
267        self.rewire.keys().max().map_or(0, |&max| max + 1)
268    }
269
270    fn num_frames(&self) -> FrameSize {
271        self.buffer.num_frames()
272    }
273
274    fn channel(&self, index: usize) -> Option<&[T]> {
275        if let Some(&source_channel) = self.rewire.get(&index) {
276            self.buffer.channel(source_channel)
277        } else {
278            None
279        }
280    }
281
282    fn channel_mut(&mut self, _index: usize) -> Option<&mut [T]> {
283        None
284    }
285
286    fn clear(&mut self) {
287        panic!("Cannot clear an immutable buffer view");
288    }
289}