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 = InterleavedView::from_slice(&data, 2, 3);
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 InterleavedView<'a, S: Sample> {
27    data: &'a [S],
28    num_channels: u16,
29    num_frames: usize,
30    num_channels_allocated: u16,
31    num_frames_allocated: usize,
32}
33
34impl<'a, S: Sample> InterleavedView<'a, S> {
35    /// Creates a new [`InterleavedView`] from a slice of interleaved audio data.
36    ///
37    /// # Parameters
38    /// * `data` - The slice containing interleaved audio samples
39    /// * `num_channels` - Number of audio channels in the data
40    /// * `num_frames` - Number of audio frames in the data
41    ///
42    /// # Panics
43    /// Panics if the length of `data` doesn't equal `num_channels * num_frames`.
44    #[nonblocking]
45    pub fn from_slice(data: &'a [S], num_channels: u16, num_frames: usize) -> Self {
46        assert_eq!(data.len(), num_channels as usize * num_frames);
47        Self {
48            data,
49            num_channels,
50            num_frames,
51            num_channels_allocated: num_channels,
52            num_frames_allocated: num_frames,
53        }
54    }
55
56    /// Creates a new [`InterleavedView`] from a slice with limited visibility.
57    ///
58    /// This function allows creating a view that exposes only a subset of the allocated channels
59    /// and frames, which is useful for working with a logical section of a larger buffer.
60    ///
61    /// # Parameters
62    /// * `data` - The slice containing interleaved audio samples
63    /// * `num_channels_visible` - Number of audio channels to expose in the view
64    /// * `num_frames_visible` - Number of audio frames to expose in the view
65    /// * `num_channels_allocated` - Total number of channels allocated in the data buffer
66    /// * `num_frames_allocated` - Total number of frames allocated in the data buffer
67    ///
68    /// # Panics
69    /// * Panics if the length of `data` doesn't equal `num_channels_allocated * num_frames_allocated`
70    /// * Panics if `num_channels_visible` exceeds `num_channels_allocated`
71    /// * Panics if `num_frames_visible` exceeds `num_frames_allocated`
72    #[nonblocking]
73    pub fn from_slice_limited(
74        data: &'a [S],
75        num_channels_visible: u16,
76        num_frames_visible: usize,
77        num_channels_allocated: u16,
78        num_frames_allocated: usize,
79    ) -> Self {
80        assert_eq!(
81            data.len(),
82            num_channels_allocated as usize * num_frames_allocated
83        );
84        assert!(num_channels_visible <= num_channels_allocated);
85        assert!(num_frames_visible <= num_frames_allocated);
86        Self {
87            data,
88            num_channels: num_channels_visible,
89            num_frames: num_frames_visible,
90            num_channels_allocated,
91            num_frames_allocated,
92        }
93    }
94
95    /// Creates a new [`InterleavedView`] from a pointer.
96    ///
97    /// # Safety
98    ///
99    /// The caller must ensure that:
100    /// - `ptr` points to valid memory containing at least `num_channels_available * num_frames_available` elements
101    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
102    /// - The memory must not be mutated through other pointers while this view exists
103    #[nonblocking]
104    pub unsafe fn from_ptr(ptr: *const S, num_channels: u16, num_frames: usize) -> Self {
105        Self {
106            data: unsafe { std::slice::from_raw_parts(ptr, num_channels as usize * num_frames) },
107            num_channels,
108            num_frames,
109            num_channels_allocated: num_channels,
110            num_frames_allocated: num_frames,
111        }
112    }
113
114    /// Creates a new [`InterleavedView`] from a pointer with a limited amount of channels and/or frames.
115    ///
116    /// # Safety
117    ///
118    /// The caller must ensure that:
119    /// - `ptr` points to valid memory containing at least `num_channels_available * num_frames_available` elements
120    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
121    /// - The memory must not be mutated through other pointers while this view exists
122    #[nonblocking]
123    pub unsafe fn from_ptr_limited(
124        ptr: *const S,
125        num_channels_visible: u16,
126        num_frames_visible: usize,
127        num_channels_allocated: u16,
128        num_frames_allocated: usize,
129    ) -> Self {
130        assert!(num_channels_visible <= num_channels_allocated);
131        assert!(num_frames_visible <= num_frames_allocated);
132        Self {
133            data: unsafe {
134                std::slice::from_raw_parts(
135                    ptr,
136                    num_channels_allocated as usize * num_frames_allocated,
137                )
138            },
139            num_channels: num_channels_visible,
140            num_frames: num_frames_visible,
141            num_channels_allocated,
142            num_frames_allocated,
143        }
144    }
145}
146
147impl<S: Sample> AudioBlock<S> for InterleavedView<'_, S> {
148    #[nonblocking]
149    fn num_channels(&self) -> u16 {
150        self.num_channels
151    }
152
153    #[nonblocking]
154    fn num_frames(&self) -> usize {
155        self.num_frames
156    }
157
158    #[nonblocking]
159    fn num_channels_allocated(&self) -> u16 {
160        self.num_channels_allocated
161    }
162
163    #[nonblocking]
164    fn num_frames_allocated(&self) -> usize {
165        self.num_frames_allocated
166    }
167
168    #[nonblocking]
169    fn sample(&self, channel: u16, frame: usize) -> S {
170        assert!(channel < self.num_channels);
171        assert!(frame < self.num_frames);
172        unsafe {
173            *self
174                .data
175                .get_unchecked(frame * self.num_channels_allocated as usize + channel as usize)
176        }
177    }
178
179    #[nonblocking]
180    fn channel(&self, channel: u16) -> impl Iterator<Item = &S> {
181        assert!(channel < self.num_channels);
182        self.data
183            .iter()
184            .skip(channel as usize)
185            .step_by(self.num_channels_allocated as usize)
186            .take(self.num_frames)
187    }
188
189    #[nonblocking]
190    fn channels(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
191        let num_channels = self.num_channels as usize;
192        let num_frames = self.num_frames;
193        let stride = self.num_channels_allocated as usize;
194        let data_ptr = self.data.as_ptr();
195
196        (0..num_channels).map(move |channel_idx| {
197            // Safety check: Ensure data isn't empty if we calculate a start_ptr.
198            // If num_frames or num_channels is 0, remaining will be 0, iterator is safe.
199            // If data is empty, ptr is dangling, but add(0) is okay. add(>0) is UB.
200            // But if data is empty, num_channels or num_frames must be 0.
201            let start_ptr = if self.data.is_empty() {
202                NonNull::dangling().as_ptr() // Use dangling pointer if slice is empty
203            } else {
204                // Safety: channel_idx is < num_channels <= num_channels_allocated.
205                // Adding it to a valid data_ptr is safe within slice bounds.
206                unsafe { data_ptr.add(channel_idx) }
207            };
208
209            StridedSampleIter::<'_, S> {
210                // Note: '_ lifetime from &self borrow
211                // Safety: Pointer is either dangling (if empty) or valid start pointer.
212                // NonNull::new is safe if start_ptr is non-null (i.e., data not empty).
213                ptr: NonNull::new(start_ptr as *mut S).unwrap_or(NonNull::dangling()), // Use dangling on null/empty
214                stride,
215                remaining: num_frames, // If 0, iterator yields None immediately
216                _marker: PhantomData,
217            }
218        })
219    }
220
221    #[nonblocking]
222    fn frame(&self, frame: usize) -> impl Iterator<Item = &S> {
223        assert!(frame < self.num_frames);
224        self.data
225            .iter()
226            .skip(frame * self.num_channels_allocated as usize)
227            .take(self.num_channels as usize)
228    }
229
230    #[nonblocking]
231    fn frames(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
232        let num_channels = self.num_channels as usize;
233        let num_channels_allocated = self.num_channels_allocated as usize;
234        self.data
235            .chunks(num_channels_allocated)
236            .take(self.num_frames)
237            .map(move |channel_chunk| channel_chunk.iter().take(num_channels))
238    }
239
240    #[nonblocking]
241    fn frame_slice(&self, frame: usize) -> Option<&[S]> {
242        assert!(frame < self.num_frames);
243        let start = frame * self.num_channels_allocated as usize;
244        let end = start + self.num_channels as usize;
245        Some(&self.data[start..end])
246    }
247
248    #[nonblocking]
249    fn view(&self) -> impl AudioBlock<S> {
250        InterleavedView::from_slice_limited(
251            self.data,
252            self.num_channels,
253            self.num_frames,
254            self.num_channels_allocated,
255            self.num_frames_allocated,
256        )
257    }
258
259    #[nonblocking]
260    fn layout(&self) -> crate::BlockLayout {
261        crate::BlockLayout::Interleaved
262    }
263
264    #[nonblocking]
265    fn raw_data(&self, _: Option<u16>) -> &[S] {
266        self.data
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use rtsan_standalone::no_sanitize_realtime;
273
274    use super::*;
275
276    #[test]
277    fn test_samples() {
278        let data = vec![0.0, 5.0, 1.0, 6.0, 2.0, 7.0, 3.0, 8.0, 4.0, 9.0];
279        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
280
281        for ch in 0..block.num_channels() {
282            for f in 0..block.num_frames() {
283                assert_eq!(
284                    block.sample(ch, f),
285                    (ch as usize * block.num_frames() + f) as f32
286                );
287            }
288        }
289    }
290
291    #[test]
292    fn test_channel() {
293        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
294        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
295
296        let channel = block.channel(0).copied().collect::<Vec<_>>();
297        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
298        let channel = block.channel(1).copied().collect::<Vec<_>>();
299        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
300    }
301
302    #[test]
303    fn test_channels() {
304        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
305        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
306
307        let mut channels_iter = block.channels();
308        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
309        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
310
311        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
312        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
313        assert!(channels_iter.next().is_none());
314    }
315
316    #[test]
317    fn test_frame() {
318        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
319        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
320
321        let channel = block.frame(0).copied().collect::<Vec<_>>();
322        assert_eq!(channel, vec![0.0, 1.0]);
323        let channel = block.frame(1).copied().collect::<Vec<_>>();
324        assert_eq!(channel, vec![2.0, 3.0]);
325        let channel = block.frame(2).copied().collect::<Vec<_>>();
326        assert_eq!(channel, vec![4.0, 5.0]);
327        let channel = block.frame(3).copied().collect::<Vec<_>>();
328        assert_eq!(channel, vec![6.0, 7.0]);
329        let channel = block.frame(4).copied().collect::<Vec<_>>();
330        assert_eq!(channel, vec![8.0, 9.0]);
331    }
332
333    #[test]
334    fn test_frames() {
335        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
336        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
337
338        let mut frames_iter = block.frames();
339        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
340        assert_eq!(channel, vec![0.0, 1.0]);
341        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
342        assert_eq!(channel, vec![2.0, 3.0]);
343        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
344        assert_eq!(channel, vec![4.0, 5.0]);
345        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
346        assert_eq!(channel, vec![6.0, 7.0]);
347        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
348        assert_eq!(channel, vec![8.0, 9.0]);
349        assert!(frames_iter.next().is_none());
350    }
351
352    #[test]
353    fn test_from_slice() {
354        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
355        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
356        assert_eq!(block.num_channels(), 2);
357        assert_eq!(block.num_channels_allocated, 2);
358        assert_eq!(block.num_frames(), 5);
359        assert_eq!(block.num_frames_allocated, 5);
360        assert_eq!(
361            block.channel(0).copied().collect::<Vec<_>>(),
362            vec![0.0, 2.0, 4.0, 6.0, 8.0]
363        );
364        assert_eq!(
365            block.channel(1).copied().collect::<Vec<_>>(),
366            vec![1.0, 3.0, 5.0, 7.0, 9.0]
367        );
368        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 1.0]);
369        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![2.0, 3.0]);
370        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![4.0, 5.0]);
371        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![6.0, 7.0]);
372        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![8.0, 9.0]);
373    }
374
375    #[test]
376    fn test_view() {
377        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
378        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
379        let view = block.view();
380        assert_eq!(
381            view.channel(0).copied().collect::<Vec<_>>(),
382            vec![0.0, 2.0, 4.0, 6.0, 8.0]
383        );
384        assert_eq!(
385            view.channel(1).copied().collect::<Vec<_>>(),
386            vec![1.0, 3.0, 5.0, 7.0, 9.0]
387        );
388    }
389
390    #[test]
391    fn test_limited() {
392        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];
393
394        let block = InterleavedView::from_slice_limited(&data, 2, 3, 3, 4);
395
396        assert_eq!(block.num_channels(), 2);
397        assert_eq!(block.num_frames(), 3);
398        assert_eq!(block.num_channels_allocated, 3);
399        assert_eq!(block.num_frames_allocated, 4);
400
401        for i in 0..block.num_channels() {
402            assert_eq!(block.channel(i).count(), 3);
403        }
404        for i in 0..block.num_frames() {
405            assert_eq!(block.frame(i).count(), 2);
406        }
407    }
408
409    #[test]
410    fn test_from_raw() {
411        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
412        let block = unsafe { InterleavedView::<f32>::from_ptr(data.as_mut_ptr(), 2, 5) };
413        assert_eq!(block.num_channels(), 2);
414        assert_eq!(block.num_channels_allocated, 2);
415        assert_eq!(block.num_frames(), 5);
416        assert_eq!(block.num_frames_allocated, 5);
417        assert_eq!(
418            block.channel(0).copied().collect::<Vec<_>>(),
419            vec![0.0, 2.0, 4.0, 6.0, 8.0]
420        );
421        assert_eq!(
422            block.channel(1).copied().collect::<Vec<_>>(),
423            vec![1.0, 3.0, 5.0, 7.0, 9.0]
424        );
425        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 1.0]);
426        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![2.0, 3.0]);
427        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![4.0, 5.0]);
428        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![6.0, 7.0]);
429        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![8.0, 9.0]);
430    }
431
432    #[test]
433    fn test_from_raw_limited() {
434        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];
435
436        let block = unsafe { InterleavedView::from_ptr_limited(data.as_ptr(), 2, 3, 3, 4) };
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_slice() {
453        let data = [1.0, 1.0, 0.0, 2.0, 2.0, 0.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0];
454        let block = InterleavedView::<f32>::from_slice_limited(&data, 2, 3, 3, 4);
455        assert!(block.channel_slice(0).is_none());
456
457        assert_eq!(block.frame_slice(0).unwrap(), &[1.0; 2]);
458        assert_eq!(block.frame_slice(1).unwrap(), &[2.0; 2]);
459        assert_eq!(block.frame_slice(2).unwrap(), &[3.0; 2]);
460    }
461
462    #[test]
463    #[should_panic]
464    #[no_sanitize_realtime]
465    fn test_slice_out_of_bounds() {
466        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];
467        let block = InterleavedView::<f32>::from_slice_limited(&data, 2, 3, 3, 4);
468        block.frame_slice(5);
469    }
470
471    #[test]
472    fn test_raw_data() {
473        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
474        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
475
476        assert_eq!(block.layout(), crate::BlockLayout::Interleaved);
477
478        assert_eq!(
479            block.raw_data(None),
480            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
481        );
482    }
483}