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 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 “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 = AudioBlockSequentialView::from_slice(&data, 2, 3);
22///
23/// assert_eq!(block.channel(0), &[0.0, 0.0, 0.0]);
24/// assert_eq!(block.channel(1), &[1.0, 1.0, 1.0]);
25/// ```
26pub struct AudioBlockSequentialView<'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> AudioBlockSequentialView<'a, S> {
35    /// Creates a new audio block 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 audio block 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 audio block 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 audio block 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    /// Returns a slice for a single channel.
145    ///
146    /// # Panics
147    ///
148    /// Panics if channel index is out of bounds.
149    #[nonblocking]
150    pub fn channel(&self, channel: u16) -> &[S] {
151        assert!(channel < self.num_channels);
152        let start = channel as usize * self.num_frames_allocated;
153        let end = start + self.num_frames;
154        &self.data[start..end]
155    }
156
157    /// Returns an iterator over all channels in the block.
158    ///
159    /// Each channel is represented as a slice of samples.
160    #[nonblocking]
161    pub fn channels(&self) -> impl Iterator<Item = &[S]> {
162        self.data
163            .chunks(self.num_frames_allocated)
164            .take(self.num_channels as usize)
165            .map(|frame| &frame[..self.num_frames])
166    }
167
168    /// Provides direct access to the underlying memory as an interleaved slice.
169    ///
170    /// This function gives access to all allocated data, including any reserved capacity
171    /// beyond the active range.
172    #[nonblocking]
173    pub fn raw_data(&self) -> &[S] {
174        &self.data
175    }
176
177    #[nonblocking]
178    pub fn view(&self) -> AudioBlockSequentialView<'_, S> {
179        AudioBlockSequentialView::from_slice_limited(
180            self.data,
181            self.num_channels,
182            self.num_frames,
183            self.num_channels_allocated,
184            self.num_frames_allocated,
185        )
186    }
187}
188
189impl<S: Sample> AudioBlock<S> for AudioBlockSequentialView<'_, S> {
190    type PlanarView = [S; 0];
191
192    #[nonblocking]
193    fn num_channels(&self) -> u16 {
194        self.num_channels
195    }
196
197    #[nonblocking]
198    fn num_frames(&self) -> usize {
199        self.num_frames
200    }
201
202    #[nonblocking]
203    fn num_channels_allocated(&self) -> u16 {
204        self.num_channels_allocated
205    }
206
207    #[nonblocking]
208    fn num_frames_allocated(&self) -> usize {
209        self.num_frames_allocated
210    }
211
212    #[nonblocking]
213    fn layout(&self) -> crate::BlockLayout {
214        crate::BlockLayout::Sequential
215    }
216
217    #[nonblocking]
218    fn sample(&self, channel: u16, frame: usize) -> S {
219        assert!(channel < self.num_channels);
220        assert!(frame < self.num_frames);
221        unsafe {
222            *self
223                .data
224                .get_unchecked(channel as usize * self.num_frames_allocated + frame)
225        }
226    }
227
228    #[nonblocking]
229    fn channel_iter(&self, channel: u16) -> impl Iterator<Item = &S> {
230        assert!(channel < self.num_channels);
231        self.data
232            .iter()
233            .skip(channel as usize * self.num_frames_allocated)
234            .take(self.num_frames)
235    }
236
237    #[nonblocking]
238    fn channels_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
239        let num_frames = self.num_frames; // Active frames per channel
240        let num_frames_allocated = self.num_frames_allocated; // Allocated frames per channel (chunk size)
241
242        self.data
243            .chunks(num_frames_allocated)
244            .take(self.num_channels as usize)
245            .map(move |channel_chunk| channel_chunk.iter().take(num_frames))
246    }
247
248    #[nonblocking]
249    fn frame_iter(&self, frame: usize) -> impl Iterator<Item = &S> {
250        assert!(frame < self.num_frames);
251        self.data
252            .iter()
253            .skip(frame)
254            .step_by(self.num_frames_allocated)
255            .take(self.num_channels as usize)
256    }
257
258    #[nonblocking]
259    fn frame_iters(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
260        let num_channels = self.num_channels as usize;
261        let num_frames = self.num_frames;
262        let stride = self.num_frames_allocated;
263        let data_ptr = self.data.as_ptr();
264
265        (0..num_frames).map(move |frame_idx| {
266            // Safety check: Ensure data isn't empty if we calculate a start_ptr.
267            // If num_frames or num_channels is 0, remaining will be 0, iterator is safe.
268            // If data is empty, ptr is dangling, but add(0) is okay. add(>0) is UB.
269            // But if data is empty, num_channels or num_frames must be 0.
270            let start_ptr = if self.data.is_empty() {
271                NonNull::dangling().as_ptr() // Use dangling pointer if slice is empty
272            } else {
273                // Safety: channel_idx is < num_channels <= num_channels_allocated.
274                // Adding it to a valid data_ptr is safe within slice bounds.
275                unsafe { data_ptr.add(frame_idx) }
276            };
277
278            StridedSampleIter::<'_, S> {
279                // Note: '_ lifetime from &self borrow
280                // Safety: Pointer is either dangling (if empty) or valid start pointer.
281                // NonNull::new is safe if start_ptr is non-null (i.e., data not empty).
282                ptr: NonNull::new(start_ptr as *mut S).unwrap_or(NonNull::dangling()), // Use dangling on null/empty
283                stride,
284                remaining: num_channels, // If 0, iterator yields None immediately
285                _marker: PhantomData,
286            }
287        })
288    }
289
290    #[nonblocking]
291    fn as_view(&self) -> impl AudioBlock<S> {
292        self.view()
293    }
294
295    #[nonblocking]
296    fn as_sequential_view(&self) -> Option<AudioBlockSequentialView<'_, S>> {
297        Some(self.view())
298    }
299}
300
301impl<S: Sample + core::fmt::Debug> core::fmt::Debug for AudioBlockSequentialView<'_, S> {
302    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
303        writeln!(f, "AudioBlockSequentialView {{")?;
304        writeln!(f, "  num_channels: {}", self.num_channels)?;
305        writeln!(f, "  num_frames: {}", self.num_frames)?;
306        writeln!(
307            f,
308            "  num_channels_allocated: {}",
309            self.num_channels_allocated
310        )?;
311        writeln!(f, "  num_frames_allocated: {}", self.num_frames_allocated)?;
312        writeln!(f, "  channels:")?;
313
314        for (i, channel) in self.channels().enumerate() {
315            writeln!(f, "    {}: {:?}", i, channel)?;
316        }
317
318        writeln!(f, "  raw_data: {:?}", self.raw_data())?;
319        writeln!(f, "}}")?;
320
321        Ok(())
322    }
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328    use rtsan_standalone::no_sanitize_realtime;
329
330    #[test]
331    fn test_member_functions() {
332        let block = AudioBlockSequentialView::from_slice_limited(
333            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, 0.0, 0.0, 0.0],
334            2,
335            3,
336            3,
337            4,
338        );
339
340        // single frame
341        assert_eq!(block.channel(0), &[0.0, 1.0, 2.0]);
342        assert_eq!(block.channel(1), &[4.0, 5.0, 6.0]);
343
344        // all frames
345        let mut channels = block.channels();
346        assert_eq!(channels.next().unwrap(), &[0.0, 1.0, 2.0]);
347        assert_eq!(channels.next().unwrap(), &[4.0, 5.0, 6.0]);
348        assert_eq!(channels.next(), None);
349        drop(channels);
350
351        // raw data
352        assert_eq!(
353            block.raw_data(),
354            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, 0.0, 0.0, 0.0]
355        );
356
357        // views
358        let view = block.view();
359        assert_eq!(view.num_channels(), block.num_channels());
360        assert_eq!(view.num_frames(), block.num_frames());
361        assert_eq!(
362            view.num_channels_allocated(),
363            block.num_channels_allocated()
364        );
365        assert_eq!(view.num_frames_allocated(), block.num_frames_allocated());
366        assert_eq!(view.raw_data(), block.raw_data());
367    }
368
369    #[test]
370    fn test_samples() {
371        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
372        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2, 5);
373
374        for ch in 0..block.num_channels() {
375            for f in 0..block.num_frames() {
376                assert_eq!(
377                    block.sample(ch, f),
378                    (ch as usize * block.num_frames() + f) as f32
379                );
380            }
381        }
382    }
383
384    #[test]
385    fn test_channel_iter() {
386        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
387        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2, 5);
388
389        let channel = block.channel_iter(0).copied().collect::<Vec<_>>();
390        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
391        let channel = block.channel_iter(1).copied().collect::<Vec<_>>();
392        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
393    }
394
395    #[test]
396    fn test_channel_iters() {
397        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
398        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2, 5);
399
400        let mut channels_iter = block.channels_iter();
401        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
402        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
403
404        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
405        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
406        assert!(channels_iter.next().is_none());
407    }
408
409    #[test]
410    fn test_frame_iter() {
411        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
412        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2, 5);
413
414        let channel = block.frame_iter(0).copied().collect::<Vec<_>>();
415        assert_eq!(channel, vec![0.0, 5.0]);
416        let channel = block.frame_iter(1).copied().collect::<Vec<_>>();
417        assert_eq!(channel, vec![1.0, 6.0]);
418        let channel = block.frame_iter(2).copied().collect::<Vec<_>>();
419        assert_eq!(channel, vec![2.0, 7.0]);
420        let channel = block.frame_iter(3).copied().collect::<Vec<_>>();
421        assert_eq!(channel, vec![3.0, 8.0]);
422        let channel = block.frame_iter(4).copied().collect::<Vec<_>>();
423        assert_eq!(channel, vec![4.0, 9.0]);
424    }
425
426    #[test]
427    fn test_frame_iters() {
428        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
429        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2, 5);
430
431        let mut frames_iter = block.frame_iters();
432        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
433        assert_eq!(channel, vec![0.0, 5.0]);
434        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
435        assert_eq!(channel, vec![1.0, 6.0]);
436        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
437        assert_eq!(channel, vec![2.0, 7.0]);
438        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
439        assert_eq!(channel, vec![3.0, 8.0]);
440        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
441        assert_eq!(channel, vec![4.0, 9.0]);
442        assert!(frames_iter.next().is_none());
443    }
444
445    #[test]
446    fn test_from_slice() {
447        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
448        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2, 5);
449        assert_eq!(block.num_channels(), 2);
450        assert_eq!(block.num_channels_allocated, 2);
451        assert_eq!(block.num_frames(), 5);
452        assert_eq!(block.num_frames_allocated, 5);
453        assert_eq!(
454            block.channel_iter(0).copied().collect::<Vec<_>>(),
455            vec![0.0, 1.0, 2.0, 3.0, 4.0]
456        );
457        assert_eq!(
458            block.channel_iter(1).copied().collect::<Vec<_>>(),
459            vec![5.0, 6.0, 7.0, 8.0, 9.0]
460        );
461        assert_eq!(
462            block.frame_iter(0).copied().collect::<Vec<_>>(),
463            vec![0.0, 5.0]
464        );
465        assert_eq!(
466            block.frame_iter(1).copied().collect::<Vec<_>>(),
467            vec![1.0, 6.0]
468        );
469        assert_eq!(
470            block.frame_iter(2).copied().collect::<Vec<_>>(),
471            vec![2.0, 7.0]
472        );
473        assert_eq!(
474            block.frame_iter(3).copied().collect::<Vec<_>>(),
475            vec![3.0, 8.0]
476        );
477        assert_eq!(
478            block.frame_iter(4).copied().collect::<Vec<_>>(),
479            vec![4.0, 9.0]
480        );
481    }
482
483    #[test]
484    fn test_view() {
485        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
486        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2, 5);
487        assert!(block.as_interleaved_view().is_none());
488        assert!(block.as_planar_view().is_none());
489        assert!(block.as_sequential_view().is_some());
490        let view = block.as_view();
491        assert_eq!(
492            view.channel_iter(0).copied().collect::<Vec<_>>(),
493            vec![0.0, 1.0, 2.0, 3.0, 4.0]
494        );
495        assert_eq!(
496            view.channel_iter(1).copied().collect::<Vec<_>>(),
497            vec![5.0, 6.0, 7.0, 8.0, 9.0]
498        );
499    }
500
501    #[test]
502    fn test_limited() {
503        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];
504
505        let block = AudioBlockSequentialView::from_slice_limited(&data, 2, 3, 3, 4);
506
507        assert_eq!(block.num_channels(), 2);
508        assert_eq!(block.num_frames(), 3);
509        assert_eq!(block.num_channels_allocated, 3);
510        assert_eq!(block.num_frames_allocated, 4);
511
512        for i in 0..block.num_channels() {
513            assert_eq!(block.channel_iter(i).count(), 3);
514        }
515        for i in 0..block.num_frames() {
516            assert_eq!(block.frame_iter(i).count(), 2);
517        }
518    }
519
520    #[test]
521    fn test_from_raw() {
522        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
523        let block = unsafe { AudioBlockSequentialView::<f32>::from_ptr(data.as_mut_ptr(), 2, 5) };
524        assert_eq!(block.num_channels(), 2);
525        assert_eq!(block.num_channels_allocated, 2);
526        assert_eq!(block.num_frames(), 5);
527        assert_eq!(block.num_frames_allocated, 5);
528        assert_eq!(
529            block.channel_iter(0).copied().collect::<Vec<_>>(),
530            vec![0.0, 1.0, 2.0, 3.0, 4.0]
531        );
532        assert_eq!(
533            block.channel_iter(1).copied().collect::<Vec<_>>(),
534            vec![5.0, 6.0, 7.0, 8.0, 9.0]
535        );
536        assert_eq!(
537            block.frame_iter(0).copied().collect::<Vec<_>>(),
538            vec![0.0, 5.0]
539        );
540        assert_eq!(
541            block.frame_iter(1).copied().collect::<Vec<_>>(),
542            vec![1.0, 6.0]
543        );
544        assert_eq!(
545            block.frame_iter(2).copied().collect::<Vec<_>>(),
546            vec![2.0, 7.0]
547        );
548        assert_eq!(
549            block.frame_iter(3).copied().collect::<Vec<_>>(),
550            vec![3.0, 8.0]
551        );
552        assert_eq!(
553            block.frame_iter(4).copied().collect::<Vec<_>>(),
554            vec![4.0, 9.0]
555        );
556    }
557
558    #[test]
559    fn test_from_raw_limited() {
560        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];
561
562        let block =
563            unsafe { AudioBlockSequentialView::from_ptr_limited(data.as_ptr(), 2, 3, 3, 4) };
564
565        assert_eq!(block.num_channels(), 2);
566        assert_eq!(block.num_frames(), 3);
567        assert_eq!(block.num_channels_allocated, 3);
568        assert_eq!(block.num_frames_allocated, 4);
569
570        for i in 0..block.num_channels() {
571            assert_eq!(block.channel_iter(i).count(), 3);
572        }
573        for i in 0..block.num_frames() {
574            assert_eq!(block.frame_iter(i).count(), 2);
575        }
576    }
577
578    #[test]
579    #[should_panic]
580    #[no_sanitize_realtime]
581    fn test_slice_out_of_bounds() {
582        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];
583        let block = AudioBlockSequentialView::from_slice_limited(&data, 2, 3, 3, 4);
584
585        block.channel(2);
586    }
587}