audio_blocks/interleaved/
view.rs

1use rtsan_standalone::nonblocking;
2
3use core::{marker::PhantomData, ptr::NonNull};
4
5use crate::{AudioBlock, Sample, iter::StridedSampleIter};
6
7/// A read-only view of interleaved audio data.
8///
9/// * **Layout:** `[ch0, ch1, ch0, ch1, ch0, ch1]`
10/// * **Interpretation:** Each group of channel samples represents a frame. So, this layout stores frames one after another.
11/// * **Terminology:** Described as “packed” or “frames first” because each time step is grouped and processed as a unit (a frame).
12/// * **Usage:** Often used in APIs or hardware-level interfaces, where synchronized playback across channels is crucial.
13///
14/// # Example
15///
16/// ```
17/// use audio_blocks::*;
18///
19/// let data = vec![0.0, 1.0, 0.0, 1.0, 0.0, 1.0];
20///
21/// let block = AudioBlockInterleavedView::from_slice(&data, 2, 3);
22///
23/// assert_eq!(block.frame(0), &[0.0, 1.0]);
24/// assert_eq!(block.frame(1), &[0.0, 1.0]);
25/// assert_eq!(block.frame(2), &[0.0, 1.0]);
26/// ```
27pub struct AudioBlockInterleavedView<'a, S: Sample> {
28    data: &'a [S],
29    num_channels: u16,
30    num_frames: usize,
31    num_channels_allocated: u16,
32    num_frames_allocated: usize,
33}
34
35impl<'a, S: Sample> AudioBlockInterleavedView<'a, S> {
36    /// Creates a new interleaved audio block from a slice of interleaved audio data.
37    ///
38    /// # Parameters
39    /// * `data` - The slice containing interleaved audio samples
40    /// * `num_channels` - Number of audio channels in the data
41    /// * `num_frames` - Number of audio frames in the data
42    ///
43    /// # Panics
44    /// Panics if the length of `data` doesn't equal `num_channels * num_frames`.
45    #[nonblocking]
46    pub fn from_slice(data: &'a [S], num_channels: u16, num_frames: usize) -> Self {
47        assert_eq!(data.len(), num_channels as usize * num_frames);
48        Self {
49            data,
50            num_channels,
51            num_frames,
52            num_channels_allocated: num_channels,
53            num_frames_allocated: num_frames,
54        }
55    }
56
57    /// Creates a new interleaved audio block from a slice with limited visibility.
58    ///
59    /// This function allows creating a view that exposes only a subset of the allocated channels
60    /// and frames, which is useful for working with a logical section of a larger buffer.
61    ///
62    /// # Parameters
63    /// * `data` - The slice containing interleaved audio samples
64    /// * `num_channels_visible` - Number of audio channels to expose in the view
65    /// * `num_frames_visible` - Number of audio frames to expose in the view
66    /// * `num_channels_allocated` - Total number of channels allocated in the data buffer
67    /// * `num_frames_allocated` - Total number of frames allocated in the data buffer
68    ///
69    /// # Panics
70    /// * Panics if the length of `data` doesn't equal `num_channels_allocated * num_frames_allocated`
71    /// * Panics if `num_channels_visible` exceeds `num_channels_allocated`
72    /// * Panics if `num_frames_visible` exceeds `num_frames_allocated`
73    #[nonblocking]
74    pub fn from_slice_limited(
75        data: &'a [S],
76        num_channels_visible: u16,
77        num_frames_visible: usize,
78        num_channels_allocated: u16,
79        num_frames_allocated: usize,
80    ) -> Self {
81        assert_eq!(
82            data.len(),
83            num_channels_allocated as usize * num_frames_allocated
84        );
85        assert!(num_channels_visible <= num_channels_allocated);
86        assert!(num_frames_visible <= num_frames_allocated);
87        Self {
88            data,
89            num_channels: num_channels_visible,
90            num_frames: num_frames_visible,
91            num_channels_allocated,
92            num_frames_allocated,
93        }
94    }
95
96    /// Creates a new interleaved audio block from a pointer.
97    ///
98    /// # Safety
99    ///
100    /// The caller must ensure that:
101    /// - `ptr` points to valid memory containing at least `num_channels_available * num_frames_available` elements
102    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
103    /// - The memory must not be mutated through other pointers while this view exists
104    #[nonblocking]
105    pub unsafe fn from_ptr(ptr: *const S, num_channels: u16, num_frames: usize) -> Self {
106        Self {
107            data: unsafe { std::slice::from_raw_parts(ptr, num_channels as usize * num_frames) },
108            num_channels,
109            num_frames,
110            num_channels_allocated: num_channels,
111            num_frames_allocated: num_frames,
112        }
113    }
114
115    /// Creates a new audio block from a pointer with a limited amount of channels and/or frames.
116    ///
117    /// # Safety
118    ///
119    /// The caller must ensure that:
120    /// - `ptr` points to valid memory containing at least `num_channels_available * num_frames_available` elements
121    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
122    /// - The memory must not be mutated through other pointers while this view exists
123    #[nonblocking]
124    pub unsafe fn from_ptr_limited(
125        ptr: *const S,
126        num_channels_visible: u16,
127        num_frames_visible: usize,
128        num_channels_allocated: u16,
129        num_frames_allocated: usize,
130    ) -> Self {
131        assert!(num_channels_visible <= num_channels_allocated);
132        assert!(num_frames_visible <= num_frames_allocated);
133        Self {
134            data: unsafe {
135                std::slice::from_raw_parts(
136                    ptr,
137                    num_channels_allocated as usize * num_frames_allocated,
138                )
139            },
140            num_channels: num_channels_visible,
141            num_frames: num_frames_visible,
142            num_channels_allocated,
143            num_frames_allocated,
144        }
145    }
146
147    /// Returns a slice for a single frame.
148    ///
149    /// # Panics
150    ///
151    /// Panics if frame index is out of bounds.
152    #[nonblocking]
153    pub fn frame(&self, frame: usize) -> &[S] {
154        assert!(frame < self.num_frames);
155        let start = frame * self.num_channels_allocated as usize;
156        let end = start + self.num_channels as usize;
157        &self.data[start..end]
158    }
159
160    /// Returns an iterator over all frames in the block.
161    ///
162    /// Each frame is represented as a slice of samples.
163    #[nonblocking]
164    pub fn frames(&self) -> impl Iterator<Item = &[S]> {
165        self.data
166            .chunks(self.num_channels_allocated as usize)
167            .take(self.num_frames)
168            .map(move |frame| &frame[..self.num_channels as usize])
169    }
170
171    /// Provides direct access to the underlying memory as an interleaved slice.
172    ///
173    /// This function gives access to all allocated data, including any reserved capacity
174    /// beyond the active range.
175    #[nonblocking]
176    pub fn raw_data(&self) -> &[S] {
177        &self.data
178    }
179
180    #[nonblocking]
181    pub fn view(&self) -> AudioBlockInterleavedView<'_, S> {
182        AudioBlockInterleavedView::from_slice_limited(
183            self.data,
184            self.num_channels,
185            self.num_frames,
186            self.num_channels_allocated,
187            self.num_frames_allocated,
188        )
189    }
190}
191
192impl<S: Sample> AudioBlock<S> for AudioBlockInterleavedView<'_, S> {
193    type PlanarView = [S; 0];
194
195    #[nonblocking]
196    fn num_channels(&self) -> u16 {
197        self.num_channels
198    }
199
200    #[nonblocking]
201    fn num_frames(&self) -> usize {
202        self.num_frames
203    }
204
205    #[nonblocking]
206    fn num_channels_allocated(&self) -> u16 {
207        self.num_channels_allocated
208    }
209
210    #[nonblocking]
211    fn num_frames_allocated(&self) -> usize {
212        self.num_frames_allocated
213    }
214
215    #[nonblocking]
216    fn layout(&self) -> crate::BlockLayout {
217        crate::BlockLayout::Interleaved
218    }
219
220    #[nonblocking]
221    fn sample(&self, channel: u16, frame: usize) -> S {
222        assert!(channel < self.num_channels);
223        assert!(frame < self.num_frames);
224        unsafe {
225            *self
226                .data
227                .get_unchecked(frame * self.num_channels_allocated as usize + channel as usize)
228        }
229    }
230
231    #[nonblocking]
232    fn channel_iter(&self, channel: u16) -> impl Iterator<Item = &S> {
233        assert!(channel < self.num_channels);
234        self.data
235            .iter()
236            .skip(channel as usize)
237            .step_by(self.num_channels_allocated as usize)
238            .take(self.num_frames)
239    }
240
241    #[nonblocking]
242    fn channels_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
243        let num_channels = self.num_channels as usize;
244        let num_frames = self.num_frames;
245        let stride = self.num_channels_allocated as usize;
246        let data_ptr = self.data.as_ptr();
247
248        (0..num_channels).map(move |channel_idx| {
249            // Safety check: Ensure data isn't empty if we calculate a start_ptr.
250            // If num_frames or num_channels is 0, remaining will be 0, iterator is safe.
251            // If data is empty, ptr is dangling, but add(0) is okay. add(>0) is UB.
252            // But if data is empty, num_channels or num_frames must be 0.
253            let start_ptr = if self.data.is_empty() {
254                NonNull::dangling().as_ptr() // Use dangling pointer if slice is empty
255            } else {
256                // Safety: channel_idx is < num_channels <= num_channels_allocated.
257                // Adding it to a valid data_ptr is safe within slice bounds.
258                unsafe { data_ptr.add(channel_idx) }
259            };
260
261            StridedSampleIter::<'_, S> {
262                // Note: '_ lifetime from &self borrow
263                // Safety: Pointer is either dangling (if empty) or valid start pointer.
264                // NonNull::new is safe if start_ptr is non-null (i.e., data not empty).
265                ptr: NonNull::new(start_ptr as *mut S).unwrap_or(NonNull::dangling()), // Use dangling on null/empty
266                stride,
267                remaining: num_frames, // If 0, iterator yields None immediately
268                _marker: PhantomData,
269            }
270        })
271    }
272
273    #[nonblocking]
274    fn frame_iter(&self, frame: usize) -> impl Iterator<Item = &S> {
275        assert!(frame < self.num_frames);
276        self.data
277            .iter()
278            .skip(frame * self.num_channels_allocated as usize)
279            .take(self.num_channels as usize)
280    }
281
282    #[nonblocking]
283    fn frame_iters(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
284        let num_channels = self.num_channels as usize;
285        let num_channels_allocated = self.num_channels_allocated as usize;
286        self.data
287            .chunks(num_channels_allocated)
288            .take(self.num_frames)
289            .map(move |channel_chunk| channel_chunk.iter().take(num_channels))
290    }
291
292    #[nonblocking]
293    fn as_view(&self) -> impl AudioBlock<S> {
294        self.view()
295    }
296
297    #[nonblocking]
298    fn as_interleaved_view(&self) -> Option<AudioBlockInterleavedView<'_, S>> {
299        Some(self.view())
300    }
301}
302
303impl<S: Sample + core::fmt::Debug> core::fmt::Debug for AudioBlockInterleavedView<'_, S> {
304    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
305        writeln!(f, "AudioBlockInterleavedView {{")?;
306        writeln!(f, "  num_channels: {}", self.num_channels)?;
307        writeln!(f, "  num_frames: {}", self.num_frames)?;
308        writeln!(
309            f,
310            "  num_channels_allocated: {}",
311            self.num_channels_allocated
312        )?;
313        writeln!(f, "  num_frames_allocated: {}", self.num_frames_allocated)?;
314        writeln!(f, "  frames:")?;
315
316        for (i, frame) in self.frames().enumerate() {
317            writeln!(f, "    {}: {:?}", i, frame)?;
318        }
319
320        writeln!(f, "  raw_data: {:?}", self.raw_data())?;
321        writeln!(f, "}}")?;
322
323        Ok(())
324    }
325}
326
327#[cfg(test)]
328mod tests {
329    use super::*;
330    use rtsan_standalone::no_sanitize_realtime;
331
332    #[test]
333    fn test_member_functions() {
334        let block = AudioBlockInterleavedView::from_slice_limited(
335            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, 0.0, 0.0, 0.0],
336            3,
337            2,
338            4,
339            3,
340        );
341
342        // single frame
343        assert_eq!(block.frame(0), &[0.0, 1.0, 2.0]);
344        assert_eq!(block.frame(1), &[4.0, 5.0, 6.0]);
345
346        // all frames
347        let mut frames = block.frames();
348        assert_eq!(frames.next().unwrap(), &[0.0, 1.0, 2.0]);
349        assert_eq!(frames.next().unwrap(), &[4.0, 5.0, 6.0]);
350        assert_eq!(frames.next(), None);
351        drop(frames);
352
353        // raw data
354        assert_eq!(
355            block.raw_data(),
356            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, 0.0, 0.0, 0.0]
357        );
358
359        // views
360        let view = block.view();
361        assert_eq!(view.num_channels(), block.num_channels());
362        assert_eq!(view.num_frames(), block.num_frames());
363        assert_eq!(
364            view.num_channels_allocated(),
365            block.num_channels_allocated()
366        );
367        assert_eq!(view.num_frames_allocated(), block.num_frames_allocated());
368        assert_eq!(view.raw_data(), block.raw_data());
369    }
370
371    #[test]
372    fn test_channel_iter() {
373        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
374        let block = AudioBlockInterleavedView::<f32>::from_slice(&data, 2, 5);
375
376        let channel = block.channel_iter(0).copied().collect::<Vec<_>>();
377        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
378        let channel = block.channel_iter(1).copied().collect::<Vec<_>>();
379        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
380    }
381
382    #[test]
383    fn test_channel_iters() {
384        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
385        let block = AudioBlockInterleavedView::<f32>::from_slice(&data, 2, 5);
386
387        let mut channels_iter = block.channels_iter();
388        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
389        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
390
391        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
392        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
393        assert!(channels_iter.next().is_none());
394    }
395
396    #[test]
397    fn test_frame_iter() {
398        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
399        let block = AudioBlockInterleavedView::<f32>::from_slice(&data, 2, 5);
400
401        let channel = block.frame_iter(0).copied().collect::<Vec<_>>();
402        assert_eq!(channel, vec![0.0, 1.0]);
403        let channel = block.frame_iter(1).copied().collect::<Vec<_>>();
404        assert_eq!(channel, vec![2.0, 3.0]);
405        let channel = block.frame_iter(2).copied().collect::<Vec<_>>();
406        assert_eq!(channel, vec![4.0, 5.0]);
407        let channel = block.frame_iter(3).copied().collect::<Vec<_>>();
408        assert_eq!(channel, vec![6.0, 7.0]);
409        let channel = block.frame_iter(4).copied().collect::<Vec<_>>();
410        assert_eq!(channel, vec![8.0, 9.0]);
411    }
412
413    #[test]
414    fn test_frame_iters() {
415        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
416        let block = AudioBlockInterleavedView::<f32>::from_slice(&data, 2, 5);
417
418        let mut frames_iter = block.frame_iters();
419        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
420        assert_eq!(channel, vec![0.0, 1.0]);
421        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
422        assert_eq!(channel, vec![2.0, 3.0]);
423        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
424        assert_eq!(channel, vec![4.0, 5.0]);
425        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
426        assert_eq!(channel, vec![6.0, 7.0]);
427        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
428        assert_eq!(channel, vec![8.0, 9.0]);
429        assert!(frames_iter.next().is_none());
430    }
431
432    #[test]
433    fn test_from_slice() {
434        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
435        let block = AudioBlockInterleavedView::<f32>::from_slice(&data, 2, 5);
436        assert_eq!(block.num_channels(), 2);
437        assert_eq!(block.num_channels_allocated, 2);
438        assert_eq!(block.num_frames(), 5);
439        assert_eq!(block.num_frames_allocated, 5);
440        assert_eq!(
441            block.channel_iter(0).copied().collect::<Vec<_>>(),
442            vec![0.0, 2.0, 4.0, 6.0, 8.0]
443        );
444        assert_eq!(
445            block.channel_iter(1).copied().collect::<Vec<_>>(),
446            vec![1.0, 3.0, 5.0, 7.0, 9.0]
447        );
448        assert_eq!(
449            block.frame_iter(0).copied().collect::<Vec<_>>(),
450            vec![0.0, 1.0]
451        );
452        assert_eq!(
453            block.frame_iter(1).copied().collect::<Vec<_>>(),
454            vec![2.0, 3.0]
455        );
456        assert_eq!(
457            block.frame_iter(2).copied().collect::<Vec<_>>(),
458            vec![4.0, 5.0]
459        );
460        assert_eq!(
461            block.frame_iter(3).copied().collect::<Vec<_>>(),
462            vec![6.0, 7.0]
463        );
464        assert_eq!(
465            block.frame_iter(4).copied().collect::<Vec<_>>(),
466            vec![8.0, 9.0]
467        );
468    }
469
470    #[test]
471    fn test_view() {
472        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
473        let block = AudioBlockInterleavedView::<f32>::from_slice(&data, 2, 5);
474        assert!(block.as_interleaved_view().is_some());
475        assert!(block.as_planar_view().is_none());
476        assert!(block.as_sequential_view().is_none());
477
478        let view = block.view();
479        assert_eq!(
480            view.channel_iter(0).copied().collect::<Vec<_>>(),
481            vec![0.0, 2.0, 4.0, 6.0, 8.0]
482        );
483        assert_eq!(
484            view.channel_iter(1).copied().collect::<Vec<_>>(),
485            vec![1.0, 3.0, 5.0, 7.0, 9.0]
486        );
487    }
488
489    #[test]
490    fn test_limited() {
491        let data = [1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 5.0, 6.0, 0.0, 0.0, 0.0, 0.0];
492
493        let block = AudioBlockInterleavedView::from_slice_limited(&data, 2, 3, 3, 4);
494
495        assert_eq!(block.num_channels(), 2);
496        assert_eq!(block.num_frames(), 3);
497        assert_eq!(block.num_channels_allocated, 3);
498        assert_eq!(block.num_frames_allocated, 4);
499
500        for i in 0..block.num_channels() {
501            assert_eq!(block.channel_iter(i).count(), 3);
502        }
503        for i in 0..block.num_frames() {
504            assert_eq!(block.frame_iter(i).count(), 2);
505        }
506    }
507
508    #[test]
509    fn test_from_raw() {
510        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
511        let block = unsafe { AudioBlockInterleavedView::<f32>::from_ptr(data.as_mut_ptr(), 2, 5) };
512        assert_eq!(block.num_channels(), 2);
513        assert_eq!(block.num_channels_allocated, 2);
514        assert_eq!(block.num_frames(), 5);
515        assert_eq!(block.num_frames_allocated, 5);
516        assert_eq!(
517            block.channel_iter(0).copied().collect::<Vec<_>>(),
518            vec![0.0, 2.0, 4.0, 6.0, 8.0]
519        );
520        assert_eq!(
521            block.channel_iter(1).copied().collect::<Vec<_>>(),
522            vec![1.0, 3.0, 5.0, 7.0, 9.0]
523        );
524        assert_eq!(
525            block.frame_iter(0).copied().collect::<Vec<_>>(),
526            vec![0.0, 1.0]
527        );
528        assert_eq!(
529            block.frame_iter(1).copied().collect::<Vec<_>>(),
530            vec![2.0, 3.0]
531        );
532        assert_eq!(
533            block.frame_iter(2).copied().collect::<Vec<_>>(),
534            vec![4.0, 5.0]
535        );
536        assert_eq!(
537            block.frame_iter(3).copied().collect::<Vec<_>>(),
538            vec![6.0, 7.0]
539        );
540        assert_eq!(
541            block.frame_iter(4).copied().collect::<Vec<_>>(),
542            vec![8.0, 9.0]
543        );
544    }
545
546    #[test]
547    fn test_from_raw_limited() {
548        let data = [1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 5.0, 6.0, 0.0, 0.0, 0.0, 0.0];
549
550        let block =
551            unsafe { AudioBlockInterleavedView::from_ptr_limited(data.as_ptr(), 2, 3, 3, 4) };
552
553        assert_eq!(block.num_channels(), 2);
554        assert_eq!(block.num_frames(), 3);
555        assert_eq!(block.num_channels_allocated, 3);
556        assert_eq!(block.num_frames_allocated, 4);
557
558        for i in 0..block.num_channels() {
559            assert_eq!(block.channel_iter(i).count(), 3);
560        }
561        for i in 0..block.num_frames() {
562            assert_eq!(block.frame_iter(i).count(), 2);
563        }
564    }
565
566    #[test]
567    #[should_panic]
568    #[no_sanitize_realtime]
569    fn test_slice_out_of_bounds() {
570        let data = [1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0];
571        let block = AudioBlockInterleavedView::<f32>::from_slice_limited(&data, 2, 3, 3, 4);
572        block.frame(5);
573    }
574}