Skip to main content

beamer_core/
conversion_buffers.rs

1//! Pre-allocated buffers for f64↔f32 sample format conversion.
2//!
3//! When a plugin's processor only supports f32 but the host provides f64 buffers,
4//! format wrappers need to convert samples before/after processing. This module
5//! provides `ConversionBuffers` which pre-allocates the necessary buffers during
6//! setup to avoid heap allocations during audio processing.
7//!
8//! # Real-Time Safety
9//!
10//! - All buffers are allocated once during `allocate()` or `allocate_from_buses()`
11//! - No heap allocations occur during audio processing
12//! - Buffer access is O(1)
13//!
14//! # Usage
15//!
16//! ```ignore
17//! // During setup (non-real-time):
18//! let buffers = ConversionBuffers::allocate_from_buses(&input_buses, &output_buses, max_frames);
19//!
20//! // During process (real-time):
21//! // 1. Convert f64 inputs to f32
22//! for (ch, f64_slice) in input_f64.iter().enumerate() {
23//!     if let Some(f32_buf) = buffers.main_input_mut(ch, num_samples) {
24//!         for (i, &sample) in f64_slice.iter().enumerate() {
25//!             f32_buf[i] = sample as f32;
26//!         }
27//!     }
28//! }
29//!
30//! // 2. Process with f32 buffers
31//! // 3. Convert f32 outputs back to f64
32//! ```
33
34use crate::BusInfo;
35
36/// Pre-allocated buffers for f64↔f32 conversion.
37///
38/// Avoids heap allocation during audio processing when the processor
39/// only supports f32 but the host provides f64 buffers.
40///
41/// Fields are public for direct iteration access in format wrappers.
42pub struct ConversionBuffers {
43    /// Main input bus conversion buffers: [channel][samples]
44    pub main_input_f32: Vec<Vec<f32>>,
45    /// Main output bus conversion buffers: [channel][samples]
46    pub main_output_f32: Vec<Vec<f32>>,
47    /// Auxiliary input buses: [bus_index][channel_index][samples]
48    pub aux_input_f32: Vec<Vec<Vec<f32>>>,
49    /// Auxiliary output buses: [bus_index][channel_index][samples]
50    pub aux_output_f32: Vec<Vec<Vec<f32>>>,
51}
52
53impl ConversionBuffers {
54    /// Create empty conversion buffers (no capacity reserved).
55    ///
56    /// Use this when you don't know the configuration yet.
57    /// Call `allocate()` or `allocate_from_buses()` later.
58    pub fn new() -> Self {
59        Self {
60            main_input_f32: Vec::new(),
61            main_output_f32: Vec::new(),
62            aux_input_f32: Vec::new(),
63            aux_output_f32: Vec::new(),
64        }
65    }
66
67    /// Pre-allocate buffers from bus information.
68    ///
69    /// This is the preferred allocation method when you have access to `BusInfo`.
70    /// Extracts channel counts from main bus (index 0) and auxiliary buses (index 1+).
71    ///
72    /// # Arguments
73    ///
74    /// * `input_buses` - Slice of input bus information
75    /// * `output_buses` - Slice of output bus information
76    /// * `max_frames` - Maximum number of samples per buffer
77    pub fn allocate_from_buses(
78        input_buses: &[BusInfo],
79        output_buses: &[BusInfo],
80        max_frames: usize,
81    ) -> Self {
82        // Main bus (bus 0) channels
83        let main_in_channels = input_buses
84            .first()
85            .map(|b| b.channel_count as usize)
86            .unwrap_or(0);
87        let main_out_channels = output_buses
88            .first()
89            .map(|b| b.channel_count as usize)
90            .unwrap_or(0);
91
92        let main_input_f32: Vec<Vec<f32>> = (0..main_in_channels)
93            .map(|_| vec![0.0f32; max_frames])
94            .collect();
95
96        let main_output_f32: Vec<Vec<f32>> = (0..main_out_channels)
97            .map(|_| vec![0.0f32; max_frames])
98            .collect();
99
100        // Auxiliary buses (bus 1+)
101        let aux_input_f32: Vec<Vec<Vec<f32>>> = input_buses
102            .iter()
103            .skip(1)
104            .map(|info| {
105                (0..info.channel_count)
106                    .map(|_| vec![0.0f32; max_frames])
107                    .collect()
108            })
109            .collect();
110
111        let aux_output_f32: Vec<Vec<Vec<f32>>> = output_buses
112            .iter()
113            .skip(1)
114            .map(|info| {
115                (0..info.channel_count)
116                    .map(|_| vec![0.0f32; max_frames])
117                    .collect()
118            })
119            .collect();
120
121        Self {
122            main_input_f32,
123            main_output_f32,
124            aux_input_f32,
125            aux_output_f32,
126        }
127    }
128
129    /// Pre-allocate buffers with explicit channel counts.
130    ///
131    /// Use this when you have channel counts directly rather than `BusInfo`.
132    ///
133    /// # Arguments
134    ///
135    /// * `main_input_channels` - Number of main input channels
136    /// * `main_output_channels` - Number of main output channels
137    /// * `aux_input_channels` - Channel count for each auxiliary input bus
138    /// * `aux_output_channels` - Channel count for each auxiliary output bus
139    /// * `max_frames` - Maximum number of samples per buffer
140    pub fn allocate(
141        main_input_channels: usize,
142        main_output_channels: usize,
143        aux_input_channels: &[usize],
144        aux_output_channels: &[usize],
145        max_frames: usize,
146    ) -> Self {
147        let main_input_f32 = (0..main_input_channels)
148            .map(|_| vec![0.0f32; max_frames])
149            .collect();
150
151        let main_output_f32 = (0..main_output_channels)
152            .map(|_| vec![0.0f32; max_frames])
153            .collect();
154
155        let aux_input_f32 = aux_input_channels
156            .iter()
157            .map(|&channels| (0..channels).map(|_| vec![0.0f32; max_frames]).collect())
158            .collect();
159
160        let aux_output_f32 = aux_output_channels
161            .iter()
162            .map(|&channels| (0..channels).map(|_| vec![0.0f32; max_frames]).collect())
163            .collect();
164
165        Self {
166            main_input_f32,
167            main_output_f32,
168            aux_input_f32,
169            aux_output_f32,
170        }
171    }
172
173    // =========================================================================
174    // Main bus accessors
175    // =========================================================================
176
177    /// Get mutable reference to a main input channel buffer.
178    ///
179    /// Returns `None` if the channel index is out of bounds.
180    #[inline]
181    pub fn main_input_mut(&mut self, channel: usize) -> Option<&mut [f32]> {
182        self.main_input_f32.get_mut(channel).map(|v| v.as_mut_slice())
183    }
184
185    /// Get reference to a main input channel buffer.
186    #[inline]
187    pub fn main_input(&self, channel: usize) -> Option<&[f32]> {
188        self.main_input_f32.get(channel).map(|v| v.as_slice())
189    }
190
191    /// Get mutable reference to a main output channel buffer.
192    #[inline]
193    pub fn main_output_mut(&mut self, channel: usize) -> Option<&mut [f32]> {
194        self.main_output_f32.get_mut(channel).map(|v| v.as_mut_slice())
195    }
196
197    /// Get reference to a main output channel buffer.
198    #[inline]
199    pub fn main_output(&self, channel: usize) -> Option<&[f32]> {
200        self.main_output_f32.get(channel).map(|v| v.as_slice())
201    }
202
203    /// Get number of main input channels.
204    #[inline]
205    pub fn main_input_channel_count(&self) -> usize {
206        self.main_input_f32.len()
207    }
208
209    /// Get number of main output channels.
210    #[inline]
211    pub fn main_output_channel_count(&self) -> usize {
212        self.main_output_f32.len()
213    }
214
215    // =========================================================================
216    // Auxiliary bus accessors
217    // =========================================================================
218
219    /// Get mutable slice of an auxiliary input channel buffer.
220    ///
221    /// # Arguments
222    ///
223    /// * `bus` - Auxiliary bus index (0 = first aux bus, not main bus)
224    /// * `channel` - Channel index within the bus
225    /// * `len` - Number of samples to access
226    #[inline]
227    pub fn aux_input_mut(&mut self, bus: usize, channel: usize, len: usize) -> Option<&mut [f32]> {
228        self.aux_input_f32
229            .get_mut(bus)
230            .and_then(|b| b.get_mut(channel))
231            .map(|v| {
232                let actual_len = len.min(v.len());
233                &mut v[..actual_len]
234            })
235    }
236
237    /// Get slice of an auxiliary input channel buffer.
238    #[inline]
239    pub fn aux_input(&self, bus: usize, channel: usize, len: usize) -> Option<&[f32]> {
240        self.aux_input_f32
241            .get(bus)
242            .and_then(|b| b.get(channel))
243            .map(|v| &v[..len.min(v.len())])
244    }
245
246    /// Get mutable slice of an auxiliary output channel buffer.
247    #[inline]
248    pub fn aux_output_mut(&mut self, bus: usize, channel: usize, len: usize) -> Option<&mut [f32]> {
249        self.aux_output_f32
250            .get_mut(bus)
251            .and_then(|b| b.get_mut(channel))
252            .map(|v| {
253                let actual_len = len.min(v.len());
254                &mut v[..actual_len]
255            })
256    }
257
258    /// Get slice of an auxiliary output channel buffer.
259    #[inline]
260    pub fn aux_output(&self, bus: usize, channel: usize, len: usize) -> Option<&[f32]> {
261        self.aux_output_f32
262            .get(bus)
263            .and_then(|b| b.get(channel))
264            .map(|v| &v[..len.min(v.len())])
265    }
266
267    /// Get number of auxiliary input buses.
268    #[inline]
269    pub fn aux_input_bus_count(&self) -> usize {
270        self.aux_input_f32.len()
271    }
272
273    /// Get number of auxiliary output buses.
274    #[inline]
275    pub fn aux_output_bus_count(&self) -> usize {
276        self.aux_output_f32.len()
277    }
278
279    /// Get number of channels in an auxiliary input bus.
280    #[inline]
281    pub fn aux_input_channel_count(&self, bus: usize) -> usize {
282        self.aux_input_f32.get(bus).map(|b| b.len()).unwrap_or(0)
283    }
284
285    /// Get number of channels in an auxiliary output bus.
286    #[inline]
287    pub fn aux_output_channel_count(&self, bus: usize) -> usize {
288        self.aux_output_f32.get(bus).map(|b| b.len()).unwrap_or(0)
289    }
290}
291
292impl Default for ConversionBuffers {
293    fn default() -> Self {
294        Self::new()
295    }
296}
297
298#[cfg(test)]
299mod tests {
300    use super::*;
301
302    #[test]
303    fn test_new_empty() {
304        let buffers = ConversionBuffers::new();
305        assert_eq!(buffers.main_input_channel_count(), 0);
306        assert_eq!(buffers.main_output_channel_count(), 0);
307        assert_eq!(buffers.aux_input_bus_count(), 0);
308        assert_eq!(buffers.aux_output_bus_count(), 0);
309    }
310
311    #[test]
312    fn test_allocate_stereo() {
313        let buffers = ConversionBuffers::allocate(2, 2, &[], &[], 512);
314
315        assert_eq!(buffers.main_input_channel_count(), 2);
316        assert_eq!(buffers.main_output_channel_count(), 2);
317        assert_eq!(buffers.aux_input_bus_count(), 0);
318        assert_eq!(buffers.aux_output_bus_count(), 0);
319
320        // Check buffer sizes
321        assert_eq!(buffers.main_input_f32[0].len(), 512);
322        assert_eq!(buffers.main_output_f32[1].len(), 512);
323    }
324
325    #[test]
326    fn test_allocate_with_aux() {
327        let buffers = ConversionBuffers::allocate(2, 2, &[2, 1], &[2], 256);
328
329        assert_eq!(buffers.main_input_channel_count(), 2);
330        assert_eq!(buffers.main_output_channel_count(), 2);
331        assert_eq!(buffers.aux_input_bus_count(), 2);
332        assert_eq!(buffers.aux_output_bus_count(), 1);
333
334        assert_eq!(buffers.aux_input_channel_count(0), 2);
335        assert_eq!(buffers.aux_input_channel_count(1), 1);
336        assert_eq!(buffers.aux_output_channel_count(0), 2);
337    }
338
339    #[test]
340    fn test_allocate_from_buses() {
341        let input_buses = vec![
342            BusInfo::stereo("Main In"),
343            BusInfo::aux("Sidechain", 2),
344        ];
345        let output_buses = vec![BusInfo::stereo("Main Out")];
346
347        let buffers = ConversionBuffers::allocate_from_buses(&input_buses, &output_buses, 1024);
348
349        assert_eq!(buffers.main_input_channel_count(), 2);
350        assert_eq!(buffers.main_output_channel_count(), 2);
351        assert_eq!(buffers.aux_input_bus_count(), 1);
352        assert_eq!(buffers.aux_input_channel_count(0), 2);
353        assert_eq!(buffers.aux_output_bus_count(), 0);
354    }
355
356    #[test]
357    fn test_accessors() {
358        let mut buffers = ConversionBuffers::allocate(2, 2, &[2], &[], 128);
359
360        // Main input
361        if let Some(buf) = buffers.main_input_mut(0) {
362            buf[0] = 0.5;
363        }
364        assert_eq!(buffers.main_input(0).unwrap()[0], 0.5);
365
366        // Main output
367        if let Some(buf) = buffers.main_output_mut(1) {
368            buf[10] = -0.5;
369        }
370        assert_eq!(buffers.main_output(1).unwrap()[10], -0.5);
371
372        // Aux input
373        if let Some(buf) = buffers.aux_input_mut(0, 0, 64) {
374            buf[0] = 0.25;
375        }
376        assert_eq!(buffers.aux_input(0, 0, 64).unwrap()[0], 0.25);
377
378        // Out of bounds returns None
379        assert!(buffers.main_input(5).is_none());
380        assert!(buffers.aux_input(5, 0, 64).is_none());
381    }
382}