audio_blocks/stacked/
view.rs

1use core::mem::MaybeUninit;
2use rtsan_standalone::nonblocking;
3use std::marker::PhantomData;
4
5use crate::{AudioBlock, Sample};
6
7/// A read-only view of stacked / separate-channel audio data.
8///
9/// * **Layout:** `[[ch0, ch0, ch0], [ch1, ch1, ch1]]`
10/// * **Interpretation:** Each channel has its own separate buffer or array.
11/// * **Terminology:** Also described as “planar” or “channels first” though more specifically it’s channel-isolated buffers.
12/// * **Usage:** Very common in real-time DSP, as it simplifies memory access and can improve SIMD/vectorization efficiency.
13///
14/// # Example
15///
16/// ```
17/// use audio_blocks::*;
18///
19/// let data = vec![[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]];
20///
21/// let block = StackedView::from_slice(&data);
22///
23/// block.channel(0).for_each(|&v| assert_eq!(v, 0.0));
24/// block.channel(1).for_each(|&v| assert_eq!(v, 1.0));
25/// ```
26pub struct StackedView<'a, S: Sample, V: AsRef<[S]>> {
27    data: &'a [V],
28    num_channels: u16,
29    num_frames: usize,
30    num_channels_allocated: u16,
31    num_frames_allocated: usize,
32    _phantom: PhantomData<S>,
33}
34
35impl<'a, S: Sample, V: AsRef<[S]>> StackedView<'a, S, V> {
36    /// Creates a new [`StackedView`] from a slice of stacked audio data.
37    ///
38    /// # Parameters
39    /// * `data` - The slice containing stacked audio samples (one slice per channel)
40    ///
41    /// # Panics
42    /// Panics if the channel slices have different lengths.
43    #[nonblocking]
44    pub fn from_slice(data: &'a [V]) -> Self {
45        let num_frames_available = if data.is_empty() {
46            0
47        } else {
48            data[0].as_ref().len()
49        };
50        Self::from_slice_limited(data, data.len() as u16, num_frames_available)
51    }
52
53    /// Creates a new [`StackedView`] from a slice with limited visibility.
54    ///
55    /// This function allows creating a view that exposes only a subset of the allocated channels
56    /// and frames, which is useful for working with a logical section of a larger buffer.
57    ///
58    /// # Parameters
59    /// * `data` - The slice containing stacked audio samples (one slice per channel)
60    /// * `num_channels_visible` - Number of audio channels to expose in the view
61    /// * `num_frames_visible` - Number of audio frames to expose in the view
62    ///
63    /// # Panics
64    /// * Panics if `num_channels_visible` exceeds the number of channels in `data`
65    /// * Panics if `num_frames_visible` exceeds the length of any channel buffer
66    /// * Panics if channel slices have different lengths
67    #[nonblocking]
68    pub fn from_slice_limited(
69        data: &'a [V],
70        num_channels_visible: u16,
71        num_frames_visible: usize,
72    ) -> Self {
73        let num_channels_allocated = data.len() as u16;
74        let num_frames_allocated = if num_channels_allocated == 0 {
75            0
76        } else {
77            data[0].as_ref().len()
78        };
79        assert!(num_channels_visible <= num_channels_allocated);
80        assert!(num_frames_visible <= num_frames_allocated);
81        data.iter()
82            .for_each(|v| assert_eq!(v.as_ref().len(), num_frames_allocated));
83
84        Self {
85            data,
86            num_channels: num_channels_visible,
87            num_frames: num_frames_visible,
88            num_channels_allocated,
89            num_frames_allocated,
90            _phantom: PhantomData,
91        }
92    }
93}
94
95impl<S: Sample, V: AsRef<[S]>> AudioBlock<S> for StackedView<'_, S, V> {
96    #[nonblocking]
97    fn num_frames(&self) -> usize {
98        self.num_frames
99    }
100
101    #[nonblocking]
102    fn num_channels(&self) -> u16 {
103        self.num_channels
104    }
105
106    #[nonblocking]
107    fn num_channels_allocated(&self) -> u16 {
108        self.num_channels_allocated
109    }
110
111    #[nonblocking]
112    fn num_frames_allocated(&self) -> usize {
113        self.num_frames_allocated
114    }
115
116    #[nonblocking]
117    fn sample(&self, channel: u16, frame: usize) -> S {
118        assert!(channel < self.num_channels);
119        assert!(frame < self.num_frames);
120        unsafe {
121            *self
122                .data
123                .get_unchecked(channel as usize)
124                .as_ref()
125                .get_unchecked(frame)
126        }
127    }
128
129    #[nonblocking]
130    fn channel(&self, channel: u16) -> impl Iterator<Item = &S> {
131        assert!(channel < self.num_channels);
132        unsafe {
133            self.data
134                .get_unchecked(channel as usize)
135                .as_ref()
136                .iter()
137                .take(self.num_frames)
138        }
139    }
140
141    #[nonblocking]
142    fn channels(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
143        let num_frames = self.num_frames; // Capture num_frames for the closure
144        self.data
145            .iter()
146            // Limit to the active number of channels
147            .take(self.num_channels as usize)
148            // For each channel slice, create an iterator over its samples
149            .map(move |channel_data| channel_data.as_ref().iter().take(num_frames))
150    }
151
152    #[nonblocking]
153    fn frame(&self, frame: usize) -> impl Iterator<Item = &S> {
154        assert!(frame < self.num_frames);
155        self.data
156            .iter()
157            .take(self.num_channels as usize)
158            .map(move |channel_data| unsafe { channel_data.as_ref().get_unchecked(frame) })
159    }
160
161    #[nonblocking]
162    fn frames(&self) -> impl Iterator<Item = impl Iterator<Item = &'_ S> + '_> + '_ {
163        let num_channels = self.num_channels as usize;
164        let num_frames = self.num_frames;
165        let data_slice: &[V] = self.data;
166
167        (0..num_frames).map(move |frame_idx| {
168            // For each frame index, create an iterator over the relevant channel views.
169            data_slice[..num_channels]
170                .iter() // Yields `&'a V`
171                .map(move |channel_view: &V| {
172                    // Get the immutable slice `&[S]` from the view using AsRef.
173                    let channel_slice: &[S] = channel_view.as_ref();
174                    // Access the sample immutably using safe indexing.
175                    // Assumes frame_idx is valid based on outer loop and struct invariants.
176                    &channel_slice[frame_idx]
177                    // For max performance (if bounds are absolutely guaranteed):
178                    // unsafe { channel_slice.get_unchecked(frame_idx) }
179                })
180        })
181    }
182
183    #[nonblocking]
184    fn view(&self) -> impl AudioBlock<S> {
185        StackedView::<S, V>::from_slice_limited(self.data, self.num_channels, self.num_frames)
186    }
187
188    #[nonblocking]
189    fn layout(&self) -> crate::BlockLayout {
190        crate::BlockLayout::Stacked
191    }
192
193    #[nonblocking]
194    fn raw_data(&self, stacked_ch: Option<u16>) -> &[S] {
195        let ch = stacked_ch.expect("For stacked layout channel needs to be provided!");
196        assert!(ch < self.num_channels_allocated);
197        unsafe { self.data.get_unchecked(ch as usize).as_ref() }
198    }
199}
200
201/// Adapter for creating stacked audio block views from raw pointers.
202///
203/// This adapter provides a safe interface to work with audio data stored in external buffers,
204/// which is common when interfacing with audio APIs or hardware.
205///
206/// # Example
207///
208/// ```
209/// use audio_blocks::*;
210///
211/// // Create sample data for two channels with five frames each
212/// let ch1 = vec![0.0f32, 1.0, 2.0, 3.0, 4.0];
213/// let ch2 = vec![5.0f32, 6.0, 7.0, 8.0, 9.0];
214///
215/// // Create pointers to the channel data
216/// let ptrs = [ch1.as_ptr(), ch2.as_ptr()];
217/// let data = ptrs.as_ptr();
218/// let num_channels = 2u16;
219/// let num_frames = 5;
220///
221/// // Create an adapter from raw pointers to audio channel data
222/// let adapter = unsafe { StackedPtrAdapter::<f32, 16>::from_ptr(data, num_channels, num_frames) };
223///
224/// // Get a safe view of the audio data
225/// let block = adapter.stacked_view();
226///
227/// // Verify the data access works
228/// assert_eq!(block.sample(0, 2), 2.0);
229/// assert_eq!(block.sample(1, 3), 8.0);
230/// ```
231///
232/// # Safety
233///
234/// When creating an adapter from raw pointers, you must ensure that:
235/// - The pointers are valid and properly aligned
236/// - The memory they point to remains valid for the lifetime of the adapter
237/// - The channel count doesn't exceed the adapter's `MAX_CHANNELS` capacity
238pub struct StackedPtrAdapter<'a, S: Sample, const MAX_CHANNELS: usize> {
239    data: [MaybeUninit<&'a [S]>; MAX_CHANNELS],
240    num_channels: u16,
241}
242
243impl<'a, S: Sample, const MAX_CHANNELS: usize> StackedPtrAdapter<'a, S, MAX_CHANNELS> {
244    /// Creates new StackedPtrAdapter from raw pointers.
245    ///
246    /// # Safety
247    ///
248    /// - `ptr` must be a valid pointer to an array of pointers
249    /// - The array must contain at least `num_channels` valid pointers
250    /// - Each pointer in the array must point to a valid array of samples with `num_frames` length
251    /// - The pointed memory must remain valid for the lifetime of the returned adapter
252    /// - The data must not be modified through other pointers for the lifetime of the returned adapter
253    #[nonblocking]
254    #[nonblocking]
255    pub unsafe fn from_ptr(ptr: *const *const S, num_channels: u16, num_frames: usize) -> Self {
256        assert!(
257            num_channels as usize <= MAX_CHANNELS,
258            "num_channels exceeds MAX_CHANNELS"
259        );
260
261        let mut data: [core::mem::MaybeUninit<&'a [S]>; MAX_CHANNELS] =
262            unsafe { core::mem::MaybeUninit::uninit().assume_init() }; // Or other safe initialization
263
264        // SAFETY: Caller guarantees `ptr` is valid for `num_channels` elements.
265        let ptr_slice: &[*const S] =
266            unsafe { core::slice::from_raw_parts(ptr, num_channels as usize) };
267
268        for ch in 0..num_channels as usize {
269            // SAFETY: See previous explanation
270            data[ch].write(unsafe { core::slice::from_raw_parts(ptr_slice[ch], num_frames) });
271        }
272
273        Self { data, num_channels }
274    }
275
276    /// Returns a slice of references to the initialized channel data buffers.
277    ///
278    /// This method provides access to the underlying audio data as a slice of slices,
279    /// with each inner slice representing one audio channel.
280    #[inline]
281    pub fn data_slice(&self) -> &[&'a [S]] {
282        let initialized_part: &[MaybeUninit<&'a [S]>] = &self.data[..self.num_channels as usize];
283        unsafe {
284            core::slice::from_raw_parts(
285                initialized_part.as_ptr() as *const &'a [S],
286                self.num_channels as usize,
287            )
288        }
289    }
290
291    /// Creates a safe [`StackedView`] for accessing the audio data.
292    ///
293    /// This provides a convenient way to interact with the audio data through
294    /// the full [`AudioBlock`] interface, enabling operations like iterating
295    /// through channels or frames.
296    ///
297    /// # Returns
298    ///
299    /// A [`StackedView`] that provides safe, immutable access to the audio data.
300    #[nonblocking]
301    pub fn stacked_view(&self) -> StackedView<'a, S, &[S]> {
302        StackedView::from_slice(self.data_slice())
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    #[test]
311    fn test_samples() {
312        let ch1 = &[0.0, 1.0, 2.0, 3.0, 4.0];
313        let ch2 = &[5.0, 6.0, 7.0, 8.0, 9.0];
314        let data = [ch1, ch2];
315        let block = StackedView::from_slice(&data);
316
317        for ch in 0..block.num_channels() {
318            for f in 0..block.num_frames() {
319                assert_eq!(
320                    block.sample(ch, f),
321                    (ch as usize * block.num_frames() + f) as f32
322                );
323            }
324        }
325    }
326
327    #[test]
328    fn test_channel() {
329        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
330        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
331        let data = vec![ch1, ch2];
332        let block = StackedView::from_slice(&data);
333
334        let channel = block.channel(0).copied().collect::<Vec<_>>();
335        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
336        let channel = block.channel(1).copied().collect::<Vec<_>>();
337        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
338    }
339
340    #[test]
341    fn test_channels() {
342        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
343        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
344        let data = vec![ch1, ch2];
345        let block = StackedView::from_slice(&data);
346
347        let mut channels_iter = block.channels();
348        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
349        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
350
351        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
352        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
353        assert!(channels_iter.next().is_none());
354    }
355
356    #[test]
357    fn test_frame() {
358        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
359        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
360        let data = vec![ch1.as_slice(), ch2.as_slice()];
361        let block = StackedView::from_slice(&data);
362
363        let channel = block.frame(0).copied().collect::<Vec<_>>();
364        assert_eq!(channel, vec![0.0, 1.0]);
365        let channel = block.frame(1).copied().collect::<Vec<_>>();
366        assert_eq!(channel, vec![2.0, 3.0]);
367        let channel = block.frame(2).copied().collect::<Vec<_>>();
368        assert_eq!(channel, vec![4.0, 5.0]);
369        let channel = block.frame(3).copied().collect::<Vec<_>>();
370        assert_eq!(channel, vec![6.0, 7.0]);
371        let channel = block.frame(4).copied().collect::<Vec<_>>();
372        assert_eq!(channel, vec![8.0, 9.0]);
373    }
374
375    #[test]
376    fn test_frames() {
377        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
378        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
379        let data = vec![ch1.as_slice(), ch2.as_slice()];
380        let block = StackedView::from_slice(&data);
381
382        let mut frames_iter = block.frames();
383        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
384        assert_eq!(channel, vec![0.0, 1.0]);
385        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
386        assert_eq!(channel, vec![2.0, 3.0]);
387        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
388        assert_eq!(channel, vec![4.0, 5.0]);
389        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
390        assert_eq!(channel, vec![6.0, 7.0]);
391        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
392        assert_eq!(channel, vec![8.0, 9.0]);
393        assert!(frames_iter.next().is_none());
394    }
395
396    #[test]
397    fn test_from_vec() {
398        let vec = vec![vec![0.0, 2.0, 4.0, 6.0, 8.0], vec![1.0, 3.0, 5.0, 7.0, 9.0]];
399        let block = StackedView::from_slice(&vec);
400        assert_eq!(block.num_channels(), 2);
401        assert_eq!(block.num_frames(), 5);
402        assert_eq!(
403            block.channel(0).copied().collect::<Vec<_>>(),
404            vec![0.0, 2.0, 4.0, 6.0, 8.0]
405        );
406        assert_eq!(
407            block.channel(1).copied().collect::<Vec<_>>(),
408            vec![1.0, 3.0, 5.0, 7.0, 9.0]
409        );
410        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 1.0]);
411        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![2.0, 3.0]);
412        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![4.0, 5.0]);
413        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![6.0, 7.0]);
414        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![8.0, 9.0]);
415    }
416
417    #[test]
418    fn test_view() {
419        let vec = vec![vec![0.0, 2.0, 4.0, 6.0, 8.0], vec![1.0, 3.0, 5.0, 7.0, 9.0]];
420        let block = StackedView::from_slice(&vec);
421        let view = block.view();
422        assert_eq!(
423            view.channel(0).copied().collect::<Vec<_>>(),
424            vec![0.0, 2.0, 4.0, 6.0, 8.0]
425        );
426        assert_eq!(
427            view.channel(1).copied().collect::<Vec<_>>(),
428            vec![1.0, 3.0, 5.0, 7.0, 9.0]
429        );
430    }
431
432    #[test]
433    fn test_limited() {
434        let data = vec![vec![0.0; 4]; 3];
435
436        let block = StackedView::from_slice_limited(&data, 2, 3);
437
438        assert_eq!(block.num_channels(), 2);
439        assert_eq!(block.num_frames(), 3);
440        assert_eq!(block.num_channels_allocated, 3);
441        assert_eq!(block.num_frames_allocated, 4);
442
443        for i in 0..block.num_channels() {
444            assert_eq!(block.channel(i).count(), 3);
445        }
446        for i in 0..block.num_frames() {
447            assert_eq!(block.frame(i).count(), 2);
448        }
449    }
450
451    #[test]
452    fn test_pointer() {
453        unsafe {
454            let num_channels = 2;
455            let num_frames = 5;
456            let mut vec = [vec![0.0, 2.0, 4.0, 6.0, 8.0], vec![1.0, 3.0, 5.0, 7.0, 9.0]];
457
458            let ptr_vec: Vec<*const f32> =
459                vec.iter_mut().map(|inner_vec| inner_vec.as_ptr()).collect();
460            let ptr = ptr_vec.as_ptr();
461
462            let adapter = StackedPtrAdapter::<_, 16>::from_ptr(ptr, num_channels, num_frames);
463            let stacked = adapter.stacked_view();
464
465            assert_eq!(
466                stacked.channel(0).copied().collect::<Vec<_>>(),
467                vec![0.0, 2.0, 4.0, 6.0, 8.0]
468            );
469
470            assert_eq!(
471                stacked.channel(1).copied().collect::<Vec<_>>(),
472                vec![1.0, 3.0, 5.0, 7.0, 9.0]
473            );
474        }
475    }
476
477    #[test]
478    fn test_raw_data() {
479        let vec = vec![vec![0.0, 2.0, 4.0, 6.0, 8.0], vec![1.0, 3.0, 5.0, 7.0, 9.0]];
480        let block = StackedView::from_slice(&vec);
481
482        assert_eq!(block.layout(), crate::BlockLayout::Stacked);
483
484        assert_eq!(block.raw_data(Some(0)), &[0.0, 2.0, 4.0, 6.0, 8.0]);
485        assert_eq!(block.raw_data(Some(1)), &[1.0, 3.0, 5.0, 7.0, 9.0]);
486    }
487}