audio_blocks/planar/
view.rs

1use core::{fmt::Debug, mem::MaybeUninit};
2use rtsan_standalone::nonblocking;
3use std::marker::PhantomData;
4
5use crate::{AudioBlock, Sample};
6
7/// A read-only view of planar / 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 = AudioBlockPlanarView::from_slice(&data);
22///
23/// assert_eq!(block.channel(0), &[0.0, 0.0, 0.0]);
24/// assert_eq!(block.channel(1), &[1.0, 1.0, 1.0]);
25/// ```
26pub struct AudioBlockPlanarView<'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]>> AudioBlockPlanarView<'a, S, V> {
36    /// Creates a new audio block from a slice of planar audio data.
37    ///
38    /// # Parameters
39    /// * `data` - The slice containing planar 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 audio block 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 planar 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    /// Returns a slice for a single channel.
95    ///
96    /// # Panics
97    ///
98    /// Panics if channel index is out of bounds.
99    #[nonblocking]
100    pub fn channel(&self, channel: u16) -> &[S] {
101        assert!(channel < self.num_channels);
102        &self.data[channel as usize].as_ref()[..self.num_frames]
103    }
104
105    /// Returns an iterator over all channels in the block.
106    ///
107    /// Each channel is represented as a slice of samples.
108    #[nonblocking]
109    pub fn channels(&self) -> impl Iterator<Item = &[S]> {
110        self.data
111            .iter()
112            .take(self.num_channels as usize)
113            .map(|channel_data| &channel_data.as_ref()[..self.num_frames])
114    }
115
116    /// Provides direct mutable access to the underlying memory.
117    ///
118    /// This function gives access to all allocated data, including any reserved capacity
119    /// beyond the active range.
120    #[nonblocking]
121    pub fn raw_data(&self) -> &[V] {
122        self.data
123    }
124
125    #[nonblocking]
126    pub fn view(&self) -> AudioBlockPlanarView<'_, S, V> {
127        AudioBlockPlanarView::from_slice_limited(self.data, self.num_channels, self.num_frames)
128    }
129}
130
131impl<S: Sample, V: AsRef<[S]>> AudioBlock<S> for AudioBlockPlanarView<'_, S, V> {
132    type PlanarView = V;
133
134    #[nonblocking]
135    fn num_channels(&self) -> u16 {
136        self.num_channels
137    }
138
139    #[nonblocking]
140    fn num_frames(&self) -> usize {
141        self.num_frames
142    }
143
144    #[nonblocking]
145    fn num_channels_allocated(&self) -> u16 {
146        self.num_channels_allocated
147    }
148
149    #[nonblocking]
150    fn num_frames_allocated(&self) -> usize {
151        self.num_frames_allocated
152    }
153
154    #[nonblocking]
155    fn layout(&self) -> crate::BlockLayout {
156        crate::BlockLayout::Planar
157    }
158
159    #[nonblocking]
160    fn sample(&self, channel: u16, frame: usize) -> S {
161        assert!(channel < self.num_channels);
162        assert!(frame < self.num_frames);
163        unsafe {
164            *self
165                .data
166                .get_unchecked(channel as usize)
167                .as_ref()
168                .get_unchecked(frame)
169        }
170    }
171
172    #[nonblocking]
173    fn channel_iter(&self, channel: u16) -> impl Iterator<Item = &S> {
174        assert!(channel < self.num_channels);
175        unsafe {
176            self.data
177                .get_unchecked(channel as usize)
178                .as_ref()
179                .iter()
180                .take(self.num_frames)
181        }
182    }
183
184    #[nonblocking]
185    fn channels_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
186        let num_frames = self.num_frames; // Capture num_frames for the closure
187        self.data
188            .iter()
189            // Limit to the active number of channels
190            .take(self.num_channels as usize)
191            // For each channel slice, create an iterator over its samples
192            .map(move |channel_data| channel_data.as_ref().iter().take(num_frames))
193    }
194
195    #[nonblocking]
196    fn frame_iter(&self, frame: usize) -> impl Iterator<Item = &S> {
197        assert!(frame < self.num_frames);
198        self.data
199            .iter()
200            .take(self.num_channels as usize)
201            .map(move |channel_data| unsafe { channel_data.as_ref().get_unchecked(frame) })
202    }
203
204    #[nonblocking]
205    fn frame_iters(&self) -> impl Iterator<Item = impl Iterator<Item = &'_ S> + '_> + '_ {
206        let num_channels = self.num_channels as usize;
207        let num_frames = self.num_frames;
208        let data_slice: &[V] = self.data;
209
210        (0..num_frames).map(move |frame_idx| {
211            // For each frame index, create an iterator over the relevant channel views.
212            data_slice[..num_channels]
213                .iter() // Yields `&'a V`
214                .map(move |channel_view: &V| {
215                    // Get the immutable slice `&[S]` from the view using AsRef.
216                    let channel_slice: &[S] = channel_view.as_ref();
217                    // Access the sample immutably using safe indexing.
218                    // Assumes frame_idx is valid based on outer loop and struct invariants.
219                    &channel_slice[frame_idx]
220                    // For max performance (if bounds are absolutely guaranteed):
221                    // unsafe { channel_slice.get_unchecked(frame_idx) }
222                })
223        })
224    }
225
226    #[nonblocking]
227    fn as_view(&self) -> impl AudioBlock<S> {
228        self.view()
229    }
230
231    #[nonblocking]
232    fn as_planar_view(&self) -> Option<AudioBlockPlanarView<'_, S, Self::PlanarView>> {
233        Some(self.view())
234    }
235}
236
237impl<S: Sample + core::fmt::Debug, V: AsRef<[S]> + Debug> core::fmt::Debug
238    for AudioBlockPlanarView<'_, S, V>
239{
240    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
241        writeln!(f, "AudioBlockPlanarView {{")?;
242        writeln!(f, "  num_channels: {}", self.num_channels)?;
243        writeln!(f, "  num_frames: {}", self.num_frames)?;
244        writeln!(
245            f,
246            "  num_channels_allocated: {}",
247            self.num_channels_allocated
248        )?;
249        writeln!(f, "  num_frames_allocated: {}", self.num_frames_allocated)?;
250        writeln!(f, "  channels:")?;
251
252        for (i, channel) in self.channels().enumerate() {
253            writeln!(f, "    {}: {:?}", i, channel)?;
254        }
255
256        writeln!(f, "  raw_data: {:?}", self.raw_data())?;
257        writeln!(f, "}}")?;
258
259        Ok(())
260    }
261}
262
263/// Adapter for creating planar audio block views from raw pointers.
264///
265/// This adapter provides a safe interface to work with audio data stored in external buffers,
266/// which is common when interfacing with audio APIs or hardware.
267///
268/// # Example
269///
270/// ```
271/// use audio_blocks::*;
272///
273/// // Create sample data for two channels with five frames each
274/// let ch1 = vec![0.0f32, 1.0, 2.0, 3.0, 4.0];
275/// let ch2 = vec![5.0f32, 6.0, 7.0, 8.0, 9.0];
276///
277/// // Create pointers to the channel data
278/// let ptrs = [ch1.as_ptr(), ch2.as_ptr()];
279/// let data = ptrs.as_ptr();
280/// let num_channels = 2u16;
281/// let num_frames = 5;
282///
283/// // Create an adapter from raw pointers to audio channel data
284/// let adapter = unsafe { PlanarPtrAdapter::<f32, 16>::from_ptr(data, num_channels, num_frames) };
285///
286/// // Get a safe view of the audio data
287/// let block = adapter.planar_view();
288///
289/// // Verify the data access works
290/// assert_eq!(block.sample(0, 2), 2.0);
291/// assert_eq!(block.sample(1, 3), 8.0);
292/// ```
293///
294/// # Safety
295///
296/// When creating an adapter from raw pointers, you must ensure that:
297/// - The pointers are valid and properly aligned
298/// - The memory they point to remains valid for the lifetime of the adapter
299/// - The channel count doesn't exceed the adapter's `MAX_CHANNELS` capacity
300pub struct PlanarPtrAdapter<'a, S: Sample, const MAX_CHANNELS: usize> {
301    data: [MaybeUninit<&'a [S]>; MAX_CHANNELS],
302    num_channels: u16,
303}
304
305impl<'a, S: Sample, const MAX_CHANNELS: usize> PlanarPtrAdapter<'a, S, MAX_CHANNELS> {
306    /// Creates new pointer adapter to create an audio block from raw pointers.
307    ///
308    /// # Safety
309    ///
310    /// - `ptr` must be a valid pointer to an array of pointers
311    /// - The array must contain at least `num_channels` valid pointers
312    /// - Each pointer in the array must point to a valid array of samples with `num_frames` length
313    /// - The pointed memory must remain valid for the lifetime of the returned adapter
314    /// - The data must not be modified through other pointers for the lifetime of the returned adapter
315    #[nonblocking]
316    pub unsafe fn from_ptr(ptr: *const *const S, num_channels: u16, num_frames: usize) -> Self {
317        assert!(
318            num_channels as usize <= MAX_CHANNELS,
319            "num_channels exceeds MAX_CHANNELS"
320        );
321
322        let mut data: [core::mem::MaybeUninit<&'a [S]>; MAX_CHANNELS] =
323            unsafe { core::mem::MaybeUninit::uninit().assume_init() }; // Or other safe initialization
324
325        // SAFETY: Caller guarantees `ptr` is valid for `num_channels` elements.
326        let ptr_slice: &[*const S] =
327            unsafe { core::slice::from_raw_parts(ptr, num_channels as usize) };
328
329        for ch in 0..num_channels as usize {
330            // SAFETY: See previous explanation
331            data[ch].write(unsafe { core::slice::from_raw_parts(ptr_slice[ch], num_frames) });
332        }
333
334        Self { data, num_channels }
335    }
336
337    /// Returns a slice of references to the initialized channel data buffers.
338    ///
339    /// This method provides access to the underlying audio data as a slice of slices,
340    /// with each inner slice representing one audio channel.
341    #[inline]
342    pub fn data_slice(&self) -> &[&'a [S]] {
343        let initialized_part: &[MaybeUninit<&'a [S]>] = &self.data[..self.num_channels as usize];
344        unsafe {
345            core::slice::from_raw_parts(
346                initialized_part.as_ptr() as *const &'a [S],
347                self.num_channels as usize,
348            )
349        }
350    }
351
352    /// Creates a safe [`AudioBlockPlanarView`] for accessing the audio data.
353    ///
354    /// This provides a convenient way to interact with the audio data through
355    /// the full [`AudioBlock`] interface, enabling operations like iterating
356    /// through channels or frames.
357    ///
358    /// # Returns
359    ///
360    /// A [`AudioBlockPlanarView`] that provides safe, immutable access to the audio data.
361    #[nonblocking]
362    pub fn planar_view(&self) -> AudioBlockPlanarView<'a, S, &[S]> {
363        AudioBlockPlanarView::from_slice(self.data_slice())
364    }
365}
366
367#[cfg(test)]
368mod tests {
369    use super::*;
370    use rtsan_standalone::no_sanitize_realtime;
371
372    #[test]
373    fn test_member_functions() {
374        let data = [
375            [0.0, 1.0, 2.0, 3.0],
376            [4.0, 5.0, 6.0, 7.0],
377            [0.0, 0.0, 0.0, 0.0],
378        ];
379        let block = AudioBlockPlanarView::from_slice_limited(&data, 2, 3);
380
381        // single frame
382        assert_eq!(block.channel(0), &[0.0, 1.0, 2.0]);
383        assert_eq!(block.channel(1), &[4.0, 5.0, 6.0]);
384
385        // all frames
386        let mut channels = block.channels();
387        assert_eq!(channels.next().unwrap(), &[0.0, 1.0, 2.0]);
388        assert_eq!(channels.next().unwrap(), &[4.0, 5.0, 6.0]);
389        assert_eq!(channels.next(), None);
390        drop(channels);
391
392        // raw data
393        assert_eq!(block.raw_data()[0].as_ref(), &[0.0, 1.0, 2.0, 3.0]);
394        assert_eq!(block.raw_data()[1].as_ref(), &[4.0, 5.0, 6.0, 7.0]);
395        assert_eq!(block.raw_data()[2].as_ref(), &[0.0, 0.0, 0.0, 0.0]);
396
397        // views
398        let view = block.view();
399        assert_eq!(view.num_channels(), block.num_channels());
400        assert_eq!(view.num_frames(), block.num_frames());
401        assert_eq!(
402            view.num_channels_allocated(),
403            block.num_channels_allocated()
404        );
405        assert_eq!(view.num_frames_allocated(), block.num_frames_allocated());
406        assert_eq!(view.raw_data(), block.raw_data());
407    }
408
409    #[test]
410    fn test_samples() {
411        let ch1 = &[0.0, 1.0, 2.0, 3.0, 4.0];
412        let ch2 = &[5.0, 6.0, 7.0, 8.0, 9.0];
413        let data = [ch1, ch2];
414        let block = AudioBlockPlanarView::from_slice(&data);
415
416        for ch in 0..block.num_channels() {
417            for f in 0..block.num_frames() {
418                assert_eq!(
419                    block.sample(ch, f),
420                    (ch as usize * block.num_frames() + f) as f32
421                );
422            }
423        }
424    }
425
426    #[test]
427    fn test_channel_iter() {
428        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
429        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
430        let data = vec![ch1, ch2];
431        let block = AudioBlockPlanarView::from_slice(&data);
432
433        let channel = block.channel_iter(0).copied().collect::<Vec<_>>();
434        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
435        let channel = block.channel_iter(1).copied().collect::<Vec<_>>();
436        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
437    }
438
439    #[test]
440    fn test_channel_iters() {
441        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
442        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
443        let data = vec![ch1, ch2];
444        let block = AudioBlockPlanarView::from_slice(&data);
445
446        let mut channels_iter = block.channels_iter();
447        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
448        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
449
450        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
451        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
452        assert!(channels_iter.next().is_none());
453    }
454
455    #[test]
456    fn test_frame_iter() {
457        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
458        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
459        let data = vec![ch1.as_slice(), ch2.as_slice()];
460        let block = AudioBlockPlanarView::from_slice(&data);
461
462        let channel = block.frame_iter(0).copied().collect::<Vec<_>>();
463        assert_eq!(channel, vec![0.0, 1.0]);
464        let channel = block.frame_iter(1).copied().collect::<Vec<_>>();
465        assert_eq!(channel, vec![2.0, 3.0]);
466        let channel = block.frame_iter(2).copied().collect::<Vec<_>>();
467        assert_eq!(channel, vec![4.0, 5.0]);
468        let channel = block.frame_iter(3).copied().collect::<Vec<_>>();
469        assert_eq!(channel, vec![6.0, 7.0]);
470        let channel = block.frame_iter(4).copied().collect::<Vec<_>>();
471        assert_eq!(channel, vec![8.0, 9.0]);
472    }
473
474    #[test]
475    fn test_frame_iters() {
476        let ch1 = vec![0.0, 2.0, 4.0, 6.0, 8.0];
477        let ch2 = vec![1.0, 3.0, 5.0, 7.0, 9.0];
478        let data = vec![ch1.as_slice(), ch2.as_slice()];
479        let block = AudioBlockPlanarView::from_slice(&data);
480
481        let mut frames_iter = block.frame_iters();
482        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
483        assert_eq!(channel, vec![0.0, 1.0]);
484        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
485        assert_eq!(channel, vec![2.0, 3.0]);
486        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
487        assert_eq!(channel, vec![4.0, 5.0]);
488        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
489        assert_eq!(channel, vec![6.0, 7.0]);
490        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
491        assert_eq!(channel, vec![8.0, 9.0]);
492        assert!(frames_iter.next().is_none());
493    }
494
495    #[test]
496    fn test_from_vec() {
497        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]];
498        let block = AudioBlockPlanarView::from_slice(&vec);
499        assert_eq!(block.num_channels(), 2);
500        assert_eq!(block.num_frames(), 5);
501        assert_eq!(
502            block.channel_iter(0).copied().collect::<Vec<_>>(),
503            vec![0.0, 2.0, 4.0, 6.0, 8.0]
504        );
505        assert_eq!(
506            block.channel_iter(1).copied().collect::<Vec<_>>(),
507            vec![1.0, 3.0, 5.0, 7.0, 9.0]
508        );
509        assert_eq!(
510            block.frame_iter(0).copied().collect::<Vec<_>>(),
511            vec![0.0, 1.0]
512        );
513        assert_eq!(
514            block.frame_iter(1).copied().collect::<Vec<_>>(),
515            vec![2.0, 3.0]
516        );
517        assert_eq!(
518            block.frame_iter(2).copied().collect::<Vec<_>>(),
519            vec![4.0, 5.0]
520        );
521        assert_eq!(
522            block.frame_iter(3).copied().collect::<Vec<_>>(),
523            vec![6.0, 7.0]
524        );
525        assert_eq!(
526            block.frame_iter(4).copied().collect::<Vec<_>>(),
527            vec![8.0, 9.0]
528        );
529    }
530
531    #[test]
532    fn test_view() {
533        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]];
534        let block = AudioBlockPlanarView::from_slice(&vec);
535
536        assert!(block.as_interleaved_view().is_none());
537        assert!(block.as_planar_view().is_some());
538        assert!(block.as_sequential_view().is_none());
539
540        let view = block.as_view();
541        assert_eq!(
542            view.channel_iter(0).copied().collect::<Vec<_>>(),
543            vec![0.0, 2.0, 4.0, 6.0, 8.0]
544        );
545        assert_eq!(
546            view.channel_iter(1).copied().collect::<Vec<_>>(),
547            vec![1.0, 3.0, 5.0, 7.0, 9.0]
548        );
549    }
550
551    #[test]
552    fn test_limited() {
553        let data = vec![vec![0.0; 4]; 3];
554
555        let block = AudioBlockPlanarView::from_slice_limited(&data, 2, 3);
556
557        assert_eq!(block.num_channels(), 2);
558        assert_eq!(block.num_frames(), 3);
559        assert_eq!(block.num_channels_allocated, 3);
560        assert_eq!(block.num_frames_allocated, 4);
561
562        for i in 0..block.num_channels() {
563            assert_eq!(block.channel_iter(i).count(), 3);
564        }
565        for i in 0..block.num_frames() {
566            assert_eq!(block.frame_iter(i).count(), 2);
567        }
568    }
569
570    #[test]
571    fn test_pointer() {
572        unsafe {
573            let num_channels = 2;
574            let num_frames = 5;
575            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]];
576
577            let ptr_vec: Vec<*const f32> =
578                vec.iter_mut().map(|inner_vec| inner_vec.as_ptr()).collect();
579            let ptr = ptr_vec.as_ptr();
580
581            let adapter = PlanarPtrAdapter::<_, 16>::from_ptr(ptr, num_channels, num_frames);
582            let planar = adapter.planar_view();
583
584            assert_eq!(
585                planar.channel_iter(0).copied().collect::<Vec<_>>(),
586                vec![0.0, 2.0, 4.0, 6.0, 8.0]
587            );
588
589            assert_eq!(
590                planar.channel_iter(1).copied().collect::<Vec<_>>(),
591                vec![1.0, 3.0, 5.0, 7.0, 9.0]
592            );
593        }
594    }
595
596    #[test]
597    #[should_panic]
598    #[no_sanitize_realtime]
599    fn test_slice_out_of_bounds() {
600        let data = [[1.0; 4], [2.0; 4], [0.0; 4]];
601        let block = AudioBlockPlanarView::from_slice_limited(&data, 2, 3);
602
603        block.channel(2);
604    }
605}