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 frame(&self, frame: usize) -> impl Iterator<Item = &S> {
199        assert!(frame < self.num_frames);
200        self.data
201            .iter()
202            .skip(frame)
203            .step_by(self.num_frames_allocated)
204            .take(self.num_channels as usize)
205    }
206
207    #[nonblocking]
208    fn frames(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
209        let num_channels = self.num_channels as usize;
210        let num_frames = self.num_frames;
211        let stride = self.num_frames_allocated;
212        let data_ptr = self.data.as_ptr();
213
214        (0..num_frames).map(move |frame_idx| {
215            // Safety check: Ensure data isn't empty if we calculate a start_ptr.
216            // If num_frames or num_channels is 0, remaining will be 0, iterator is safe.
217            // If data is empty, ptr is dangling, but add(0) is okay. add(>0) is UB.
218            // But if data is empty, num_channels or num_frames must be 0.
219            let start_ptr = if self.data.is_empty() {
220                NonNull::dangling().as_ptr() // Use dangling pointer if slice is empty
221            } else {
222                // Safety: channel_idx is < num_channels <= num_channels_allocated.
223                // Adding it to a valid data_ptr is safe within slice bounds.
224                unsafe { data_ptr.add(frame_idx) }
225            };
226
227            StridedSampleIter::<'_, S> {
228                // Note: '_ lifetime from &self borrow
229                // Safety: Pointer is either dangling (if empty) or valid start pointer.
230                // NonNull::new is safe if start_ptr is non-null (i.e., data not empty).
231                ptr: NonNull::new(start_ptr as *mut S).unwrap_or(NonNull::dangling()), // Use dangling on null/empty
232                stride,
233                remaining: num_channels, // If 0, iterator yields None immediately
234                _marker: PhantomData,
235            }
236        })
237    }
238
239    #[nonblocking]
240    fn view(&self) -> impl AudioBlock<S> {
241        SequentialView::from_slice_limited(
242            self.data,
243            self.num_channels,
244            self.num_frames,
245            self.num_channels_allocated,
246            self.num_frames_allocated,
247        )
248    }
249
250    #[nonblocking]
251    fn layout(&self) -> crate::BlockLayout {
252        crate::BlockLayout::Sequential
253    }
254
255    #[nonblocking]
256    fn raw_data(&self, _: Option<u16>) -> &[S] {
257        self.data
258    }
259}
260
261#[cfg(test)]
262mod tests {
263    use super::*;
264
265    #[test]
266    fn test_samples() {
267        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
268        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
269
270        for ch in 0..block.num_channels() {
271            for f in 0..block.num_frames() {
272                assert_eq!(
273                    block.sample(ch, f),
274                    (ch as usize * block.num_frames() + f) as f32
275                );
276            }
277        }
278    }
279
280    #[test]
281    fn test_channel() {
282        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
283        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
284
285        let channel = block.channel(0).copied().collect::<Vec<_>>();
286        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
287        let channel = block.channel(1).copied().collect::<Vec<_>>();
288        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
289    }
290
291    #[test]
292    fn test_channels() {
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 = SequentialView::<f32>::from_slice(&data, 2, 5);
295
296        let mut channels_iter = block.channels();
297        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
298        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
299
300        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
301        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
302        assert!(channels_iter.next().is_none());
303    }
304
305    #[test]
306    fn test_frame() {
307        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
308        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
309
310        let channel = block.frame(0).copied().collect::<Vec<_>>();
311        assert_eq!(channel, vec![0.0, 5.0]);
312        let channel = block.frame(1).copied().collect::<Vec<_>>();
313        assert_eq!(channel, vec![1.0, 6.0]);
314        let channel = block.frame(2).copied().collect::<Vec<_>>();
315        assert_eq!(channel, vec![2.0, 7.0]);
316        let channel = block.frame(3).copied().collect::<Vec<_>>();
317        assert_eq!(channel, vec![3.0, 8.0]);
318        let channel = block.frame(4).copied().collect::<Vec<_>>();
319        assert_eq!(channel, vec![4.0, 9.0]);
320    }
321
322    #[test]
323    fn test_frames() {
324        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
325        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
326
327        let mut frames_iter = block.frames();
328        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
329        assert_eq!(channel, vec![0.0, 5.0]);
330        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
331        assert_eq!(channel, vec![1.0, 6.0]);
332        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
333        assert_eq!(channel, vec![2.0, 7.0]);
334        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
335        assert_eq!(channel, vec![3.0, 8.0]);
336        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
337        assert_eq!(channel, vec![4.0, 9.0]);
338        assert!(frames_iter.next().is_none());
339    }
340
341    #[test]
342    fn test_from_slice() {
343        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
344        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
345        assert_eq!(block.num_channels(), 2);
346        assert_eq!(block.num_channels_allocated, 2);
347        assert_eq!(block.num_frames(), 5);
348        assert_eq!(block.num_frames_allocated, 5);
349        assert_eq!(
350            block.channel(0).copied().collect::<Vec<_>>(),
351            vec![0.0, 1.0, 2.0, 3.0, 4.0]
352        );
353        assert_eq!(
354            block.channel(1).copied().collect::<Vec<_>>(),
355            vec![5.0, 6.0, 7.0, 8.0, 9.0]
356        );
357        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 5.0]);
358        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![1.0, 6.0]);
359        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![2.0, 7.0]);
360        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![3.0, 8.0]);
361        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![4.0, 9.0]);
362    }
363
364    #[test]
365    fn test_view() {
366        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
367        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
368        let view = block.view();
369        assert_eq!(
370            view.channel(0).copied().collect::<Vec<_>>(),
371            vec![0.0, 1.0, 2.0, 3.0, 4.0]
372        );
373        assert_eq!(
374            view.channel(1).copied().collect::<Vec<_>>(),
375            vec![5.0, 6.0, 7.0, 8.0, 9.0]
376        );
377    }
378
379    #[test]
380    fn test_limited() {
381        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];
382
383        let block = SequentialView::from_slice_limited(&data, 2, 3, 3, 4);
384
385        assert_eq!(block.num_channels(), 2);
386        assert_eq!(block.num_frames(), 3);
387        assert_eq!(block.num_channels_allocated, 3);
388        assert_eq!(block.num_frames_allocated, 4);
389
390        for i in 0..block.num_channels() {
391            assert_eq!(block.channel(i).count(), 3);
392        }
393        for i in 0..block.num_frames() {
394            assert_eq!(block.frame(i).count(), 2);
395        }
396    }
397
398    #[test]
399    fn test_from_raw() {
400        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
401        let block = unsafe { SequentialView::<f32>::from_ptr(data.as_mut_ptr(), 2, 5) };
402        assert_eq!(block.num_channels(), 2);
403        assert_eq!(block.num_channels_allocated, 2);
404        assert_eq!(block.num_frames(), 5);
405        assert_eq!(block.num_frames_allocated, 5);
406        assert_eq!(
407            block.channel(0).copied().collect::<Vec<_>>(),
408            vec![0.0, 1.0, 2.0, 3.0, 4.0]
409        );
410        assert_eq!(
411            block.channel(1).copied().collect::<Vec<_>>(),
412            vec![5.0, 6.0, 7.0, 8.0, 9.0]
413        );
414        assert_eq!(block.frame(0).copied().collect::<Vec<_>>(), vec![0.0, 5.0]);
415        assert_eq!(block.frame(1).copied().collect::<Vec<_>>(), vec![1.0, 6.0]);
416        assert_eq!(block.frame(2).copied().collect::<Vec<_>>(), vec![2.0, 7.0]);
417        assert_eq!(block.frame(3).copied().collect::<Vec<_>>(), vec![3.0, 8.0]);
418        assert_eq!(block.frame(4).copied().collect::<Vec<_>>(), vec![4.0, 9.0]);
419    }
420
421    #[test]
422    fn test_from_raw_limited() {
423        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];
424
425        let block = unsafe { SequentialView::from_ptr_limited(data.as_ptr(), 2, 3, 3, 4) };
426
427        assert_eq!(block.num_channels(), 2);
428        assert_eq!(block.num_frames(), 3);
429        assert_eq!(block.num_channels_allocated, 3);
430        assert_eq!(block.num_frames_allocated, 4);
431
432        for i in 0..block.num_channels() {
433            assert_eq!(block.channel(i).count(), 3);
434        }
435        for i in 0..block.num_frames() {
436            assert_eq!(block.frame(i).count(), 2);
437        }
438    }
439
440    #[test]
441    fn test_raw_data() {
442        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
443        let block = SequentialView::<f32>::from_slice(&data, 2, 5);
444
445        assert_eq!(block.layout(), crate::BlockLayout::Sequential);
446
447        assert_eq!(
448            block.raw_data(None),
449            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
450        );
451    }
452}