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 view(&self) -> impl AudioBlock<S> {
242        InterleavedView::from_slice_limited(
243            self.data,
244            self.num_channels,
245            self.num_frames,
246            self.num_channels_allocated,
247            self.num_frames_allocated,
248        )
249    }
250
251    #[nonblocking]
252    fn layout(&self) -> crate::BlockLayout {
253        crate::BlockLayout::Interleaved
254    }
255
256    #[nonblocking]
257    fn raw_data(&self, _: Option<u16>) -> &[S] {
258        self.data
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265
266    #[test]
267    fn test_samples() {
268        let data = vec![0.0, 5.0, 1.0, 6.0, 2.0, 7.0, 3.0, 8.0, 4.0, 9.0];
269        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
270
271        for ch in 0..block.num_channels() {
272            for f in 0..block.num_frames() {
273                assert_eq!(
274                    block.sample(ch, f),
275                    (ch as usize * block.num_frames() + f) as f32
276                );
277            }
278        }
279    }
280
281    #[test]
282    fn test_channel() {
283        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
284        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
285
286        let channel = block.channel(0).copied().collect::<Vec<_>>();
287        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
288        let channel = block.channel(1).copied().collect::<Vec<_>>();
289        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
290    }
291
292    #[test]
293    fn test_channels() {
294        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
295        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
296
297        let mut channels_iter = block.channels();
298        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
299        assert_eq!(channel, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
300
301        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
302        assert_eq!(channel, vec![1.0, 3.0, 5.0, 7.0, 9.0]);
303        assert!(channels_iter.next().is_none());
304    }
305
306    #[test]
307    fn test_frame() {
308        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
309        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
310
311        let channel = block.frame(0).copied().collect::<Vec<_>>();
312        assert_eq!(channel, vec![0.0, 1.0]);
313        let channel = block.frame(1).copied().collect::<Vec<_>>();
314        assert_eq!(channel, vec![2.0, 3.0]);
315        let channel = block.frame(2).copied().collect::<Vec<_>>();
316        assert_eq!(channel, vec![4.0, 5.0]);
317        let channel = block.frame(3).copied().collect::<Vec<_>>();
318        assert_eq!(channel, vec![6.0, 7.0]);
319        let channel = block.frame(4).copied().collect::<Vec<_>>();
320        assert_eq!(channel, vec![8.0, 9.0]);
321    }
322
323    #[test]
324    fn test_frames() {
325        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
326        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
327
328        let mut frames_iter = block.frames();
329        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
330        assert_eq!(channel, vec![0.0, 1.0]);
331        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
332        assert_eq!(channel, vec![2.0, 3.0]);
333        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
334        assert_eq!(channel, vec![4.0, 5.0]);
335        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
336        assert_eq!(channel, vec![6.0, 7.0]);
337        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
338        assert_eq!(channel, vec![8.0, 9.0]);
339        assert!(frames_iter.next().is_none());
340    }
341
342    #[test]
343    fn test_from_slice() {
344        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
345        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
346        assert_eq!(block.num_channels(), 2);
347        assert_eq!(block.num_channels_allocated, 2);
348        assert_eq!(block.num_frames(), 5);
349        assert_eq!(block.num_frames_allocated, 5);
350        assert_eq!(
351            block.channel(0).copied().collect::<Vec<_>>(),
352            vec![0.0, 2.0, 4.0, 6.0, 8.0]
353        );
354        assert_eq!(
355            block.channel(1).copied().collect::<Vec<_>>(),
356            vec![1.0, 3.0, 5.0, 7.0, 9.0]
357        );
358        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 1.0]);
359        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![2.0, 3.0]);
360        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![4.0, 5.0]);
361        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![6.0, 7.0]);
362        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![8.0, 9.0]);
363    }
364
365    #[test]
366    fn test_view() {
367        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
368        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
369        let view = block.view();
370        assert_eq!(
371            view.channel(0).copied().collect::<Vec<_>>(),
372            vec![0.0, 2.0, 4.0, 6.0, 8.0]
373        );
374        assert_eq!(
375            view.channel(1).copied().collect::<Vec<_>>(),
376            vec![1.0, 3.0, 5.0, 7.0, 9.0]
377        );
378    }
379
380    #[test]
381    fn test_limited() {
382        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];
383
384        let block = InterleavedView::from_slice_limited(&data, 2, 3, 3, 4);
385
386        assert_eq!(block.num_channels(), 2);
387        assert_eq!(block.num_frames(), 3);
388        assert_eq!(block.num_channels_allocated, 3);
389        assert_eq!(block.num_frames_allocated, 4);
390
391        for i in 0..block.num_channels() {
392            assert_eq!(block.channel(i).count(), 3);
393        }
394        for i in 0..block.num_frames() {
395            assert_eq!(block.frame(i).count(), 2);
396        }
397    }
398
399    #[test]
400    fn test_from_raw() {
401        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
402        let block = unsafe { InterleavedView::<f32>::from_ptr(data.as_mut_ptr(), 2, 5) };
403        assert_eq!(block.num_channels(), 2);
404        assert_eq!(block.num_channels_allocated, 2);
405        assert_eq!(block.num_frames(), 5);
406        assert_eq!(block.num_frames_allocated, 5);
407        assert_eq!(
408            block.channel(0).copied().collect::<Vec<_>>(),
409            vec![0.0, 2.0, 4.0, 6.0, 8.0]
410        );
411        assert_eq!(
412            block.channel(1).copied().collect::<Vec<_>>(),
413            vec![1.0, 3.0, 5.0, 7.0, 9.0]
414        );
415        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 1.0]);
416        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![2.0, 3.0]);
417        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![4.0, 5.0]);
418        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![6.0, 7.0]);
419        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![8.0, 9.0]);
420    }
421
422    #[test]
423    fn test_from_raw_limited() {
424        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];
425
426        let block = unsafe { InterleavedView::from_ptr_limited(data.as_ptr(), 2, 3, 3, 4) };
427
428        assert_eq!(block.num_channels(), 2);
429        assert_eq!(block.num_frames(), 3);
430        assert_eq!(block.num_channels_allocated, 3);
431        assert_eq!(block.num_frames_allocated, 4);
432
433        for i in 0..block.num_channels() {
434            assert_eq!(block.channel(i).count(), 3);
435        }
436        for i in 0..block.num_frames() {
437            assert_eq!(block.frame(i).count(), 2);
438        }
439    }
440
441    #[test]
442    fn test_raw_data() {
443        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
444        let block = InterleavedView::<f32>::from_slice(&data, 2, 5);
445
446        assert_eq!(block.layout(), crate::BlockLayout::Interleaved);
447
448        assert_eq!(
449            block.raw_data(None),
450            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
451        );
452    }
453}