Skip to main content

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);
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    ///
41    /// # Panics
42    /// Panics if the length of `data` is not evenly divisible by `num_channels`.
43    #[nonblocking]
44    pub fn from_slice(data: &'a [S], num_channels: u16) -> Self {
45        assert!(
46            num_channels > 0 && data.len() % num_channels as usize == 0,
47            "data length {} must be divisible by num_channels {}",
48            data.len(),
49            num_channels
50        );
51        let num_frames = data.len() / num_channels as usize;
52        Self {
53            data,
54            num_channels,
55            num_frames,
56            num_channels_allocated: num_channels,
57            num_frames_allocated: num_frames,
58        }
59    }
60
61    /// Creates a new audio block from a slice with limited visibility.
62    ///
63    /// This function allows creating a view that exposes only a subset of the allocated channels
64    /// and frames, which is useful for working with a logical section of a larger buffer.
65    ///
66    /// # Parameters
67    /// * `data` - The slice containing sequential audio samples
68    /// * `num_channels_visible` - Number of audio channels to expose in the view
69    /// * `num_frames_visible` - Number of audio frames to expose in the view
70    /// * `num_channels_allocated` - Total number of channels allocated in the data buffer
71    /// * `num_frames_allocated` - Total number of frames allocated in the data buffer
72    ///
73    /// # Panics
74    /// * Panics if the length of `data` doesn't equal `num_channels_allocated * num_frames_allocated`
75    /// * Panics if `num_channels_visible` exceeds `num_channels_allocated`
76    /// * Panics if `num_frames_visible` exceeds `num_frames_allocated`
77    #[nonblocking]
78    pub fn from_slice_limited(
79        data: &'a [S],
80        num_channels_visible: u16,
81        num_frames_visible: usize,
82        num_channels_allocated: u16,
83        num_frames_allocated: usize,
84    ) -> Self {
85        assert_eq!(
86            data.len(),
87            num_channels_allocated as usize * num_frames_allocated
88        );
89        assert!(num_channels_visible <= num_channels_allocated);
90        assert!(num_frames_visible <= num_frames_allocated);
91        Self {
92            data,
93            num_channels: num_channels_visible,
94            num_frames: num_frames_visible,
95            num_channels_allocated,
96            num_frames_allocated,
97        }
98    }
99
100    /// Creates a new audio block from a raw pointers.
101    ///
102    /// # Safety
103    ///
104    /// The caller must ensure that:
105    /// - `ptr` points to valid memory containing at least `num_channels_allocated * num_frames_allocated` elements
106    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
107    /// - The memory must not be mutated through other pointers while this view exists
108    #[nonblocking]
109    pub unsafe fn from_ptr(ptr: *const S, num_channels: u16, num_frames: usize) -> Self {
110        Self {
111            data: unsafe { std::slice::from_raw_parts(ptr, num_channels as usize * num_frames) },
112            num_channels,
113            num_frames,
114            num_channels_allocated: num_channels,
115            num_frames_allocated: num_frames,
116        }
117    }
118
119    /// Creates a new audio block from a pointer with a limited amount of channels and/or frames.
120    ///
121    /// # Safety
122    ///
123    /// The caller must ensure that:
124    /// - `ptr` points to valid memory containing at least `num_channels_allocated * num_frames_allocated` elements
125    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned `SequentialView`
126    /// - The memory must not be mutated through other pointers while this view exists
127    #[nonblocking]
128    pub unsafe fn from_ptr_limited(
129        ptr: *const S,
130        num_channels_visible: u16,
131        num_frames_visible: usize,
132        num_channels_allocated: u16,
133        num_frames_allocated: usize,
134    ) -> Self {
135        assert!(num_channels_visible <= num_channels_allocated);
136        assert!(num_frames_visible <= num_frames_allocated);
137        Self {
138            data: unsafe {
139                std::slice::from_raw_parts(
140                    ptr,
141                    num_channels_allocated as usize * num_frames_allocated,
142                )
143            },
144            num_channels: num_channels_visible,
145            num_frames: num_frames_visible,
146            num_channels_allocated,
147            num_frames_allocated,
148        }
149    }
150
151    /// Returns a slice for a single channel.
152    ///
153    /// # Panics
154    ///
155    /// Panics if channel index is out of bounds.
156    #[nonblocking]
157    pub fn channel(&self, channel: u16) -> &[S] {
158        assert!(channel < self.num_channels);
159        let start = channel as usize * self.num_frames_allocated;
160        let end = start + self.num_frames;
161        &self.data[start..end]
162    }
163
164    /// Returns an iterator over all channels in the block.
165    ///
166    /// Each channel is represented as a slice of samples.
167    #[nonblocking]
168    pub fn channels(&self) -> impl Iterator<Item = &[S]> {
169        self.data
170            .chunks(self.num_frames_allocated)
171            .take(self.num_channels as usize)
172            .map(|frame| &frame[..self.num_frames])
173    }
174
175    /// Provides direct access to the underlying memory as a sequential slice.
176    ///
177    /// This function gives access to all allocated data, including any reserved capacity
178    /// beyond the visible range.
179    #[nonblocking]
180    pub fn raw_data(&self) -> &[S] {
181        &self.data
182    }
183
184    #[nonblocking]
185    pub fn view(&self) -> AudioBlockSequentialView<'_, S> {
186        AudioBlockSequentialView::from_slice_limited(
187            self.data,
188            self.num_channels,
189            self.num_frames,
190            self.num_channels_allocated,
191            self.num_frames_allocated,
192        )
193    }
194}
195
196impl<S: Sample> AudioBlock<S> for AudioBlockSequentialView<'_, S> {
197    type PlanarView = [S; 0];
198
199    #[nonblocking]
200    fn num_channels(&self) -> u16 {
201        self.num_channels
202    }
203
204    #[nonblocking]
205    fn num_frames(&self) -> usize {
206        self.num_frames
207    }
208
209    #[nonblocking]
210    fn num_channels_allocated(&self) -> u16 {
211        self.num_channels_allocated
212    }
213
214    #[nonblocking]
215    fn num_frames_allocated(&self) -> usize {
216        self.num_frames_allocated
217    }
218
219    #[nonblocking]
220    fn layout(&self) -> crate::BlockLayout {
221        crate::BlockLayout::Sequential
222    }
223
224    #[nonblocking]
225    fn sample(&self, channel: u16, frame: usize) -> S {
226        assert!(channel < self.num_channels);
227        assert!(frame < self.num_frames);
228        unsafe {
229            *self
230                .data
231                .get_unchecked(channel as usize * self.num_frames_allocated + frame)
232        }
233    }
234
235    #[nonblocking]
236    fn channel_iter(&self, channel: u16) -> impl Iterator<Item = &S> {
237        assert!(channel < self.num_channels);
238        self.data
239            .iter()
240            .skip(channel as usize * self.num_frames_allocated)
241            .take(self.num_frames)
242    }
243
244    #[nonblocking]
245    fn channels_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
246        let num_frames = self.num_frames; // Visible frames per channel
247        let num_frames_allocated = self.num_frames_allocated; // Allocated frames per channel (chunk size)
248
249        self.data
250            .chunks(num_frames_allocated)
251            .take(self.num_channels as usize)
252            .map(move |channel_chunk| channel_chunk.iter().take(num_frames))
253    }
254
255    #[nonblocking]
256    fn frame_iter(&self, frame: usize) -> impl Iterator<Item = &S> {
257        assert!(frame < self.num_frames);
258        self.data
259            .iter()
260            .skip(frame)
261            .step_by(self.num_frames_allocated)
262            .take(self.num_channels as usize)
263    }
264
265    #[nonblocking]
266    fn frames_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
267        let num_channels = self.num_channels as usize;
268        let num_frames = self.num_frames;
269        let stride = self.num_frames_allocated;
270        let data_ptr = self.data.as_ptr();
271
272        (0..num_frames).map(move |frame_idx| {
273            // Safety check: Ensure data isn't empty if we calculate a start_ptr.
274            // If num_frames or num_channels is 0, remaining will be 0, iterator is safe.
275            // If data is empty, ptr is dangling, but add(0) is okay. add(>0) is UB.
276            // But if data is empty, num_channels or num_frames must be 0.
277            let start_ptr = if self.data.is_empty() {
278                NonNull::dangling().as_ptr() // Use dangling pointer if slice is empty
279            } else {
280                // Safety: channel_idx is < num_channels <= num_channels_allocated.
281                // Adding it to a valid data_ptr is safe within slice bounds.
282                unsafe { data_ptr.add(frame_idx) }
283            };
284
285            StridedSampleIter::<'_, S> {
286                // Note: '_ lifetime from &self borrow
287                // Safety: Pointer is either dangling (if empty) or valid start pointer.
288                // NonNull::new is safe if start_ptr is non-null (i.e., data not empty).
289                ptr: NonNull::new(start_ptr as *mut S).unwrap_or(NonNull::dangling()), // Use dangling on null/empty
290                stride,
291                remaining: num_channels, // If 0, iterator yields None immediately
292                _marker: PhantomData,
293            }
294        })
295    }
296
297    #[nonblocking]
298    fn as_view(&self) -> impl AudioBlock<S> {
299        self.view()
300    }
301
302    #[nonblocking]
303    fn as_sequential_view(&self) -> Option<AudioBlockSequentialView<'_, S>> {
304        Some(self.view())
305    }
306}
307
308impl<S: Sample + core::fmt::Debug> core::fmt::Debug for AudioBlockSequentialView<'_, S> {
309    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
310        writeln!(f, "AudioBlockSequentialView {{")?;
311        writeln!(f, "  num_channels: {}", self.num_channels)?;
312        writeln!(f, "  num_frames: {}", self.num_frames)?;
313        writeln!(
314            f,
315            "  num_channels_allocated: {}",
316            self.num_channels_allocated
317        )?;
318        writeln!(f, "  num_frames_allocated: {}", self.num_frames_allocated)?;
319        writeln!(f, "  channels:")?;
320
321        for (i, channel) in self.channels().enumerate() {
322            writeln!(f, "    {}: {:?}", i, channel)?;
323        }
324
325        writeln!(f, "  raw_data: {:?}", self.raw_data())?;
326        writeln!(f, "}}")?;
327
328        Ok(())
329    }
330}
331
332#[cfg(test)]
333mod tests {
334    use super::*;
335    use rtsan_standalone::no_sanitize_realtime;
336
337    #[test]
338    fn test_member_functions() {
339        let block = AudioBlockSequentialView::from_slice_limited(
340            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, 0.0, 0.0, 0.0],
341            2,
342            3,
343            3,
344            4,
345        );
346
347        // single frame
348        assert_eq!(block.channel(0), &[0.0, 1.0, 2.0]);
349        assert_eq!(block.channel(1), &[4.0, 5.0, 6.0]);
350
351        // all frames
352        let mut channels = block.channels();
353        assert_eq!(channels.next().unwrap(), &[0.0, 1.0, 2.0]);
354        assert_eq!(channels.next().unwrap(), &[4.0, 5.0, 6.0]);
355        assert_eq!(channels.next(), None);
356        drop(channels);
357
358        // raw data
359        assert_eq!(
360            block.raw_data(),
361            &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, 0.0, 0.0, 0.0]
362        );
363
364        // views
365        let view = block.view();
366        assert_eq!(view.num_channels(), block.num_channels());
367        assert_eq!(view.num_frames(), block.num_frames());
368        assert_eq!(
369            view.num_channels_allocated(),
370            block.num_channels_allocated()
371        );
372        assert_eq!(view.num_frames_allocated(), block.num_frames_allocated());
373        assert_eq!(view.raw_data(), block.raw_data());
374    }
375
376    #[test]
377    fn test_samples() {
378        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
379        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2);
380
381        for ch in 0..block.num_channels() {
382            for f in 0..block.num_frames() {
383                assert_eq!(
384                    block.sample(ch, f),
385                    (ch as usize * block.num_frames() + f) as f32
386                );
387            }
388        }
389    }
390
391    #[test]
392    fn test_channel_iter() {
393        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
394        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2);
395
396        let channel = block.channel_iter(0).copied().collect::<Vec<_>>();
397        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
398        let channel = block.channel_iter(1).copied().collect::<Vec<_>>();
399        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
400    }
401
402    #[test]
403    fn test_channel_iters() {
404        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
405        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2);
406
407        let mut channels_iter = block.channels_iter();
408        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
409        assert_eq!(channel, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
410
411        let channel = channels_iter.next().unwrap().copied().collect::<Vec<_>>();
412        assert_eq!(channel, vec![5.0, 6.0, 7.0, 8.0, 9.0]);
413        assert!(channels_iter.next().is_none());
414    }
415
416    #[test]
417    fn test_frame_iter() {
418        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
419        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2);
420
421        let channel = block.frame_iter(0).copied().collect::<Vec<_>>();
422        assert_eq!(channel, vec![0.0, 5.0]);
423        let channel = block.frame_iter(1).copied().collect::<Vec<_>>();
424        assert_eq!(channel, vec![1.0, 6.0]);
425        let channel = block.frame_iter(2).copied().collect::<Vec<_>>();
426        assert_eq!(channel, vec![2.0, 7.0]);
427        let channel = block.frame_iter(3).copied().collect::<Vec<_>>();
428        assert_eq!(channel, vec![3.0, 8.0]);
429        let channel = block.frame_iter(4).copied().collect::<Vec<_>>();
430        assert_eq!(channel, vec![4.0, 9.0]);
431    }
432
433    #[test]
434    fn test_frame_iters() {
435        let data = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
436        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2);
437
438        let mut frames_iter = block.frames_iter();
439        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
440        assert_eq!(channel, vec![0.0, 5.0]);
441        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
442        assert_eq!(channel, vec![1.0, 6.0]);
443        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
444        assert_eq!(channel, vec![2.0, 7.0]);
445        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
446        assert_eq!(channel, vec![3.0, 8.0]);
447        let channel = frames_iter.next().unwrap().copied().collect::<Vec<_>>();
448        assert_eq!(channel, vec![4.0, 9.0]);
449        assert!(frames_iter.next().is_none());
450    }
451
452    #[test]
453    fn test_from_slice() {
454        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
455        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2);
456        assert_eq!(block.num_channels(), 2);
457        assert_eq!(block.num_channels_allocated, 2);
458        assert_eq!(block.num_frames(), 5);
459        assert_eq!(block.num_frames_allocated, 5);
460        assert_eq!(
461            block.channel_iter(0).copied().collect::<Vec<_>>(),
462            vec![0.0, 1.0, 2.0, 3.0, 4.0]
463        );
464        assert_eq!(
465            block.channel_iter(1).copied().collect::<Vec<_>>(),
466            vec![5.0, 6.0, 7.0, 8.0, 9.0]
467        );
468        assert_eq!(
469            block.frame_iter(0).copied().collect::<Vec<_>>(),
470            vec![0.0, 5.0]
471        );
472        assert_eq!(
473            block.frame_iter(1).copied().collect::<Vec<_>>(),
474            vec![1.0, 6.0]
475        );
476        assert_eq!(
477            block.frame_iter(2).copied().collect::<Vec<_>>(),
478            vec![2.0, 7.0]
479        );
480        assert_eq!(
481            block.frame_iter(3).copied().collect::<Vec<_>>(),
482            vec![3.0, 8.0]
483        );
484        assert_eq!(
485            block.frame_iter(4).copied().collect::<Vec<_>>(),
486            vec![4.0, 9.0]
487        );
488    }
489
490    #[test]
491    fn test_view() {
492        let data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
493        let block = AudioBlockSequentialView::<f32>::from_slice(&data, 2);
494        assert!(block.as_interleaved_view().is_none());
495        assert!(block.as_planar_view().is_none());
496        assert!(block.as_sequential_view().is_some());
497        let view = block.as_view();
498        assert_eq!(
499            view.channel_iter(0).copied().collect::<Vec<_>>(),
500            vec![0.0, 1.0, 2.0, 3.0, 4.0]
501        );
502        assert_eq!(
503            view.channel_iter(1).copied().collect::<Vec<_>>(),
504            vec![5.0, 6.0, 7.0, 8.0, 9.0]
505        );
506    }
507
508    #[test]
509    fn test_limited() {
510        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];
511
512        let block = AudioBlockSequentialView::from_slice_limited(&data, 2, 3, 3, 4);
513
514        assert_eq!(block.num_channels(), 2);
515        assert_eq!(block.num_frames(), 3);
516        assert_eq!(block.num_channels_allocated, 3);
517        assert_eq!(block.num_frames_allocated, 4);
518
519        for i in 0..block.num_channels() {
520            assert_eq!(block.channel_iter(i).count(), 3);
521        }
522        for i in 0..block.num_frames() {
523            assert_eq!(block.frame_iter(i).count(), 2);
524        }
525    }
526
527    #[test]
528    fn test_from_ptr() {
529        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
530        let block = unsafe { AudioBlockSequentialView::<f32>::from_ptr(data.as_mut_ptr(), 2, 5) };
531        assert_eq!(block.num_channels(), 2);
532        assert_eq!(block.num_channels_allocated, 2);
533        assert_eq!(block.num_frames(), 5);
534        assert_eq!(block.num_frames_allocated, 5);
535        assert_eq!(
536            block.channel_iter(0).copied().collect::<Vec<_>>(),
537            vec![0.0, 1.0, 2.0, 3.0, 4.0]
538        );
539        assert_eq!(
540            block.channel_iter(1).copied().collect::<Vec<_>>(),
541            vec![5.0, 6.0, 7.0, 8.0, 9.0]
542        );
543        assert_eq!(
544            block.frame_iter(0).copied().collect::<Vec<_>>(),
545            vec![0.0, 5.0]
546        );
547        assert_eq!(
548            block.frame_iter(1).copied().collect::<Vec<_>>(),
549            vec![1.0, 6.0]
550        );
551        assert_eq!(
552            block.frame_iter(2).copied().collect::<Vec<_>>(),
553            vec![2.0, 7.0]
554        );
555        assert_eq!(
556            block.frame_iter(3).copied().collect::<Vec<_>>(),
557            vec![3.0, 8.0]
558        );
559        assert_eq!(
560            block.frame_iter(4).copied().collect::<Vec<_>>(),
561            vec![4.0, 9.0]
562        );
563    }
564
565    #[test]
566    fn test_from_ptr_limited() {
567        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];
568
569        let block =
570            unsafe { AudioBlockSequentialView::from_ptr_limited(data.as_ptr(), 2, 3, 3, 4) };
571
572        assert_eq!(block.num_channels(), 2);
573        assert_eq!(block.num_frames(), 3);
574        assert_eq!(block.num_channels_allocated, 3);
575        assert_eq!(block.num_frames_allocated, 4);
576
577        for i in 0..block.num_channels() {
578            assert_eq!(block.channel_iter(i).count(), 3);
579        }
580        for i in 0..block.num_frames() {
581            assert_eq!(block.frame_iter(i).count(), 2);
582        }
583    }
584
585    #[test]
586    #[should_panic]
587    #[no_sanitize_realtime]
588    fn test_slice_out_of_bounds() {
589        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];
590        let block = AudioBlockSequentialView::from_slice_limited(&data, 2, 3, 3, 4);
591
592        block.channel(2);
593    }
594}