audio_blocks/sequential/
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 sequential / planar audio data.
8///
9/// * **Layout:** `[ch0, ch0, ch0, ch1, ch1, ch1]`
10/// * **Interpretation:** All samples from `ch0` are stored first, followed by all from `ch1`, etc.
11/// * **Terminology:** Described as “planar” or “channels first” in the sense that all data for one channel appears before any data for the next.
12/// * **Usage:** Used in DSP pipelines where per-channel processing is easier and more efficient.
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 = SequentialView::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 SequentialView<'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> SequentialView<'a, S> {
35    /// Creates a new [`SequentialView`] from a slice of sequential audio data.
36    ///
37    /// # Parameters
38    /// * `data` - The slice containing sequential 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 [`SequentialView`] 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 sequential 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        Self {
85            data,
86            num_channels: num_channels_visible,
87            num_frames: num_frames_visible,
88            num_channels_allocated,
89            num_frames_allocated,
90        }
91    }
92
93    /// Creates a new [`SequentialView`] from a raw pointers.
94    ///
95    /// # Safety
96    ///
97    /// The caller must ensure that:
98    /// - `ptr` points to valid memory containing at least `num_channels_available * num_frames_available` elements
99    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
100    /// - The memory must not be mutated through other pointers while this view exists
101    #[nonblocking]
102    pub unsafe fn from_ptr(ptr: *const S, num_channels: u16, num_frames: usize) -> Self {
103        Self {
104            data: unsafe { std::slice::from_raw_parts(ptr, num_channels as usize * num_frames) },
105            num_channels,
106            num_frames,
107            num_channels_allocated: num_channels,
108            num_frames_allocated: num_frames,
109        }
110    }
111
112    /// Creates a new [`SequentialView`] from a pointer with a limited amount of channels and/or frames.
113    ///
114    /// # Safety
115    ///
116    /// The caller must ensure that:
117    /// - `ptr` points to valid memory containing at least `num_channels_available * num_frames_available` elements
118    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
119    /// - The memory must not be mutated through other pointers while this view exists
120    #[nonblocking]
121    pub unsafe fn from_ptr_limited(
122        ptr: *const S,
123        num_channels_visible: u16,
124        num_frames_visible: usize,
125        num_channels_allocated: u16,
126        num_frames_allocated: usize,
127    ) -> Self {
128        assert!(num_channels_visible <= num_channels_allocated);
129        assert!(num_frames_visible <= num_frames_allocated);
130        Self {
131            data: unsafe {
132                std::slice::from_raw_parts(
133                    ptr,
134                    num_channels_allocated as usize * num_frames_allocated,
135                )
136            },
137            num_channels: num_channels_visible,
138            num_frames: num_frames_visible,
139            num_channels_allocated,
140            num_frames_allocated,
141        }
142    }
143}
144
145impl<S: Sample> AudioBlock<S> for SequentialView<'_, S> {
146    #[nonblocking]
147    fn num_channels(&self) -> u16 {
148        self.num_channels
149    }
150
151    #[nonblocking]
152    fn num_frames(&self) -> usize {
153        self.num_frames
154    }
155
156    #[nonblocking]
157    fn num_channels_allocated(&self) -> u16 {
158        self.num_channels_allocated
159    }
160
161    #[nonblocking]
162    fn num_frames_allocated(&self) -> usize {
163        self.num_frames_allocated
164    }
165
166    #[nonblocking]
167    fn sample(&self, channel: u16, frame: usize) -> S {
168        assert!(channel < self.num_channels);
169        assert!(frame < self.num_frames);
170        unsafe {
171            *self
172                .data
173                .get_unchecked(channel as usize * self.num_frames_allocated + frame)
174        }
175    }
176
177    #[nonblocking]
178    fn channel(&self, channel: u16) -> impl Iterator<Item = &S> {
179        assert!(channel < self.num_channels);
180        self.data
181            .iter()
182            .skip(channel as usize * self.num_frames_allocated)
183            .take(self.num_frames)
184    }
185
186    #[nonblocking]
187    fn channels(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
188        let num_frames = self.num_frames; // Active frames per channel
189        let num_frames_allocated = self.num_frames_allocated; // Allocated frames per channel (chunk size)
190
191        self.data
192            .chunks(num_frames_allocated)
193            .take(self.num_channels as usize)
194            .map(move |channel_chunk| channel_chunk.iter().take(num_frames))
195    }
196
197    #[nonblocking]
198    fn channel_slice(&self, channel: u16) -> Option<&[S]> {
199        assert!(channel < self.num_channels);
200        let start = channel as usize * self.num_frames_allocated;
201        let end = start + self.num_frames;
202        Some(&self.data[start..end])
203    }
204
205    #[nonblocking]
206    fn frame(&self, frame: usize) -> impl Iterator<Item = &S> {
207        assert!(frame < self.num_frames);
208        self.data
209            .iter()
210            .skip(frame)
211            .step_by(self.num_frames_allocated)
212            .take(self.num_channels as usize)
213    }
214
215    #[nonblocking]
216    fn frames(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
217        let num_channels = self.num_channels as usize;
218        let num_frames = self.num_frames;
219        let stride = self.num_frames_allocated;
220        let data_ptr = self.data.as_ptr();
221
222        (0..num_frames).map(move |frame_idx| {
223            // Safety check: Ensure data isn't empty if we calculate a start_ptr.
224            // If num_frames or num_channels is 0, remaining will be 0, iterator is safe.
225            // If data is empty, ptr is dangling, but add(0) is okay. add(>0) is UB.
226            // But if data is empty, num_channels or num_frames must be 0.
227            let start_ptr = if self.data.is_empty() {
228                NonNull::dangling().as_ptr() // Use dangling pointer if slice is empty
229            } else {
230                // Safety: channel_idx is < num_channels <= num_channels_allocated.
231                // Adding it to a valid data_ptr is safe within slice bounds.
232                unsafe { data_ptr.add(frame_idx) }
233            };
234
235            StridedSampleIter::<'_, S> {
236                // Note: '_ lifetime from &self borrow
237                // Safety: Pointer is either dangling (if empty) or valid start pointer.
238                // NonNull::new is safe if start_ptr is non-null (i.e., data not empty).
239                ptr: NonNull::new(start_ptr as *mut S).unwrap_or(NonNull::dangling()), // Use dangling on null/empty
240                stride,
241                remaining: num_channels, // If 0, iterator yields None immediately
242                _marker: PhantomData,
243            }
244        })
245    }
246
247    #[nonblocking]
248    fn view(&self) -> impl AudioBlock<S> {
249        SequentialView::from_slice_limited(
250            self.data,
251            self.num_channels,
252            self.num_frames,
253            self.num_channels_allocated,
254            self.num_frames_allocated,
255        )
256    }
257
258    #[nonblocking]
259    fn layout(&self) -> crate::BlockLayout {
260        crate::BlockLayout::Sequential
261    }
262
263    #[nonblocking]
264    fn raw_data(&self, _: Option<u16>) -> &[S] {
265        self.data
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use rtsan_standalone::no_sanitize_realtime;
272
273    use super::*;
274
275    #[test]
276    fn test_samples() {
277        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
278        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
279
280        for ch in 0..block.num_channels() {
281            for f in 0..block.num_frames() {
282                assert_eq!(
283                    block.sample(ch, f),
284                    (ch as usize * block.num_frames() + f) as f32
285                );
286            }
287        }
288    }
289
290    #[test]
291    fn test_channel() {
292        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
293        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
294
295        let channel = block.channel(0).copied().collect::<Vec<_>>();
296        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
297        let channel = block.channel(1).copied().collect::<Vec<_>>();
298        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
299    }
300
301    #[test]
302    fn test_channels() {
303        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
304        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
305
306        let mut channels_iter = block.channels();
307        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
308        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
309
310        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
311        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
312        assert!(channels_iter.next().is_none());
313    }
314
315    #[test]
316    fn test_frame() {
317        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
318        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
319
320        let channel = block.frame(0).copied().collect::<Vec<_>>();
321        assert_eq!(channel, vec![0.0, 5.0]);
322        let channel = block.frame(1).copied().collect::<Vec<_>>();
323        assert_eq!(channel, vec![1.0, 6.0]);
324        let channel = block.frame(2).copied().collect::<Vec<_>>();
325        assert_eq!(channel, vec![2.0, 7.0]);
326        let channel = block.frame(3).copied().collect::<Vec<_>>();
327        assert_eq!(channel, vec![3.0, 8.0]);
328        let channel = block.frame(4).copied().collect::<Vec<_>>();
329        assert_eq!(channel, vec![4.0, 9.0]);
330    }
331
332    #[test]
333    fn test_frames() {
334        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
335        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
336
337        let mut frames_iter = block.frames();
338        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
339        assert_eq!(channel, vec![0.0, 5.0]);
340        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
341        assert_eq!(channel, vec![1.0, 6.0]);
342        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
343        assert_eq!(channel, vec![2.0, 7.0]);
344        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
345        assert_eq!(channel, vec![3.0, 8.0]);
346        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
347        assert_eq!(channel, vec![4.0, 9.0]);
348        assert!(frames_iter.next().is_none());
349    }
350
351    #[test]
352    fn test_from_slice() {
353        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
354        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
355        assert_eq!(block.num_channels(), 2);
356        assert_eq!(block.num_channels_allocated, 2);
357        assert_eq!(block.num_frames(), 5);
358        assert_eq!(block.num_frames_allocated, 5);
359        assert_eq!(
360            block.channel(0).copied().collect::<Vec<_>>(),
361            vec![0.0, 1.0, 2.0, 3.0, 4.0]
362        );
363        assert_eq!(
364            block.channel(1).copied().collect::<Vec<_>>(),
365            vec![5.0, 6.0, 7.0, 8.0, 9.0]
366        );
367        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 5.0]);
368        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![1.0, 6.0]);
369        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![2.0, 7.0]);
370        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![3.0, 8.0]);
371        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![4.0, 9.0]);
372    }
373
374    #[test]
375    fn test_view() {
376        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
377        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
378        let view = block.view();
379        assert_eq!(
380            view.channel(0).copied().collect::<Vec<_>>(),
381            vec![0.0, 1.0, 2.0, 3.0, 4.0]
382        );
383        assert_eq!(
384            view.channel(1).copied().collect::<Vec<_>>(),
385            vec![5.0, 6.0, 7.0, 8.0, 9.0]
386        );
387    }
388
389    #[test]
390    fn test_limited() {
391        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];
392
393        let block = SequentialView::from_slice_limited(&data, 2, 3, 3, 4);
394
395        assert_eq!(block.num_channels(), 2);
396        assert_eq!(block.num_frames(), 3);
397        assert_eq!(block.num_channels_allocated, 3);
398        assert_eq!(block.num_frames_allocated, 4);
399
400        for i in 0..block.num_channels() {
401            assert_eq!(block.channel(i).count(), 3);
402        }
403        for i in 0..block.num_frames() {
404            assert_eq!(block.frame(i).count(), 2);
405        }
406    }
407
408    #[test]
409    fn test_from_raw() {
410        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
411        let block = unsafe { SequentialView::<f32>::from_ptr(data.as_mut_ptr(), 2, 5) };
412        assert_eq!(block.num_channels(), 2);
413        assert_eq!(block.num_channels_allocated, 2);
414        assert_eq!(block.num_frames(), 5);
415        assert_eq!(block.num_frames_allocated, 5);
416        assert_eq!(
417            block.channel(0).copied().collect::<Vec<_>>(),
418            vec![0.0, 1.0, 2.0, 3.0, 4.0]
419        );
420        assert_eq!(
421            block.channel(1).copied().collect::<Vec<_>>(),
422            vec![5.0, 6.0, 7.0, 8.0, 9.0]
423        );
424        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 5.0]);
425        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![1.0, 6.0]);
426        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![2.0, 7.0]);
427        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![3.0, 8.0]);
428        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![4.0, 9.0]);
429    }
430
431    #[test]
432    fn test_from_raw_limited() {
433        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];
434
435        let block = unsafe { SequentialView::from_ptr_limited(data.as_ptr(), 2, 3, 3, 4) };
436
437        assert_eq!(block.num_channels(), 2);
438        assert_eq!(block.num_frames(), 3);
439        assert_eq!(block.num_channels_allocated, 3);
440        assert_eq!(block.num_frames_allocated, 4);
441
442        for i in 0..block.num_channels() {
443            assert_eq!(block.channel(i).count(), 3);
444        }
445        for i in 0..block.num_frames() {
446            assert_eq!(block.frame(i).count(), 2);
447        }
448    }
449
450    #[test]
451    fn test_slice() {
452        let data = [1.0, 1.0, 1.0, 0.0, 2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0];
453        let block = SequentialView::from_slice_limited(&data, 2, 3, 3, 4);
454
455        assert!(block.frame_slice(0).is_none());
456
457        assert_eq!(block.channel_slice(0).unwrap(), &[1.0; 3]);
458        assert_eq!(block.channel_slice(1).unwrap(), &[2.0; 3]);
459    }
460
461    #[test]
462    #[should_panic]
463    #[no_sanitize_realtime]
464    fn test_slice_out_of_bounds() {
465        let data = [1.0, 1.0, 1.0, 0.0, 2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0];
466        let block = SequentialView::from_slice_limited(&data, 2, 3, 3, 4);
467
468        block.channel_slice(2);
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 = SequentialView::<f32>::from_slice(&data, 2, 5);
475
476        assert_eq!(block.layout(), crate::BlockLayout::Sequential);
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}