Skip to main content

audio_blocks/mono/
view.rs

1use rtsan_standalone::nonblocking;
2
3use crate::{AudioBlock, Sample};
4
5/// A read-only view of mono (single-channel) audio data.
6///
7/// This provides a lightweight, non-owning reference to a slice of mono audio samples.
8///
9/// * **Layout:** `[sample0, sample1, sample2, ...]`
10/// * **Interpretation:** A simple sequence of samples representing a single audio channel.
11/// * **Usage:** Ideal for mono audio processing, side-chain signals, or any single-channel audio data.
12///
13/// # Example
14///
15/// ```
16/// use audio_blocks::{mono::AudioBlockMonoView, AudioBlock};
17///
18/// let data = vec![0.0, 1.0, 2.0, 3.0, 4.0];
19/// let block = AudioBlockMonoView::from_slice(&data);
20///
21/// assert_eq!(block.sample(0), 0.0);
22/// assert_eq!(block.sample(4), 4.0);
23/// assert_eq!(block.num_frames(), 5);
24/// ```
25pub struct AudioBlockMonoView<'a, S: Sample> {
26    data: &'a [S],
27    num_frames: usize,
28    num_frames_allocated: usize,
29}
30
31impl<'a, S: Sample> AudioBlockMonoView<'a, S> {
32    /// Creates a new mono audio block view from a slice of audio samples.
33    ///
34    /// # Parameters
35    /// * `data` - The slice containing mono audio samples
36    ///
37    /// # Example
38    ///
39    /// ```
40    /// use audio_blocks::{mono::AudioBlockMonoView, AudioBlock};
41    ///
42    /// let samples = [1.0, 2.0, 3.0, 4.0, 5.0];
43    /// let block = AudioBlockMonoView::from_slice(&samples);
44    /// assert_eq!(block.num_frames(), 5);
45    /// ```
46    #[nonblocking]
47    pub fn from_slice(data: &'a [S]) -> Self {
48        let num_frames = data.len();
49        Self {
50            data,
51            num_frames,
52            num_frames_allocated: num_frames,
53        }
54    }
55
56    /// Creates a new mono audio block view from a slice with limited visibility.
57    ///
58    /// This function allows creating a view that exposes only a subset of the allocated frames,
59    /// which is useful for working with a logical section of a larger buffer.
60    ///
61    /// # Parameters
62    /// * `data` - The slice containing mono audio samples
63    /// * `num_frames_visible` - Number of audio frames to expose in the view
64    /// * `num_frames_allocated` - Total number of frames allocated in the data buffer
65    ///
66    /// # Panics
67    /// * Panics if the length of `data` doesn't equal `num_frames_allocated`
68    /// * Panics if `num_frames_visible` exceeds `num_frames_allocated`
69    ///
70    /// # Example
71    ///
72    /// ```
73    /// use audio_blocks::{mono::AudioBlockMonoView, AudioBlock};
74    ///
75    /// let samples = [1.0, 2.0, 3.0, 4.0, 5.0];
76    /// let block = AudioBlockMonoView::from_slice_limited(&samples, 3, 5);
77    /// assert_eq!(block.num_frames(), 3);
78    /// assert_eq!(block.num_frames_allocated(), 5);
79    /// ```
80    #[nonblocking]
81    pub fn from_slice_limited(
82        data: &'a [S],
83        num_frames_visible: usize,
84        num_frames_allocated: usize,
85    ) -> Self {
86        assert_eq!(data.len(), num_frames_allocated);
87        assert!(num_frames_visible <= num_frames_allocated);
88        Self {
89            data,
90            num_frames: num_frames_visible,
91            num_frames_allocated,
92        }
93    }
94
95    /// Creates a new mono audio block view from a pointer.
96    ///
97    /// # Safety
98    ///
99    /// The caller must ensure that:
100    /// - `ptr` points to valid memory containing at least `num_frames` elements
101    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned view
102    /// - The memory must not be mutated through other pointers while this view exists
103    ///
104    /// # Example
105    ///
106    /// ```
107    /// use audio_blocks::{mono::AudioBlockMonoView, AudioBlock};
108    ///
109    /// let samples = [1.0, 2.0, 3.0, 4.0, 5.0];
110    /// let block = unsafe { AudioBlockMonoView::from_ptr(samples.as_ptr(), 5) };
111    /// assert_eq!(block.num_frames(), 5);
112    /// ```
113    #[nonblocking]
114    pub unsafe fn from_ptr(ptr: *const S, num_frames: usize) -> Self {
115        Self {
116            data: unsafe { std::slice::from_raw_parts(ptr, num_frames) },
117            num_frames,
118            num_frames_allocated: num_frames,
119        }
120    }
121
122    /// Creates a new mono audio block view from a pointer with limited visibility.
123    ///
124    /// # Safety
125    ///
126    /// The caller must ensure that:
127    /// - `ptr` points to valid memory containing at least `num_frames_allocated` elements
128    /// - The memory referenced by `ptr` must be valid for the lifetime of the returned view
129    /// - The memory must not be mutated through other pointers while this view exists
130    #[nonblocking]
131    pub unsafe fn from_ptr_limited(
132        ptr: *const S,
133        num_frames_visible: usize,
134        num_frames_allocated: usize,
135    ) -> Self {
136        assert!(num_frames_visible <= num_frames_allocated);
137        Self {
138            data: unsafe { std::slice::from_raw_parts(ptr, num_frames_allocated) },
139            num_frames: num_frames_visible,
140            num_frames_allocated,
141        }
142    }
143
144    /// Returns the sample at the specified frame index.
145    ///
146    /// # Panics
147    ///
148    /// Panics if frame index is out of bounds.
149    #[nonblocking]
150    pub fn sample(&self, frame: usize) -> S {
151        assert!(frame < self.num_frames);
152        unsafe { *self.data.get_unchecked(frame) }
153    }
154
155    /// Provides direct access to the underlying samples as a slice.
156    ///
157    /// Returns only the visible samples (up to `num_frames`).
158    #[nonblocking]
159    pub fn samples(&self) -> &[S] {
160        &self.data[..self.num_frames]
161    }
162
163    /// Provides direct access to all allocated memory, including reserved capacity.
164    #[nonblocking]
165    pub fn raw_data(&self) -> &[S] {
166        self.data
167    }
168
169    #[nonblocking]
170    pub fn view(&self) -> AudioBlockMonoView<'_, S> {
171        AudioBlockMonoView::from_slice_limited(
172            self.data,
173            self.num_frames,
174            self.num_frames_allocated,
175        )
176    }
177}
178
179impl<S: Sample> AudioBlock<S> for AudioBlockMonoView<'_, S> {
180    type PlanarView = [S; 0];
181
182    #[nonblocking]
183    fn num_channels(&self) -> u16 {
184        1
185    }
186
187    #[nonblocking]
188    fn num_frames(&self) -> usize {
189        self.num_frames
190    }
191
192    #[nonblocking]
193    fn num_channels_allocated(&self) -> u16 {
194        1
195    }
196
197    #[nonblocking]
198    fn num_frames_allocated(&self) -> usize {
199        self.num_frames_allocated
200    }
201
202    #[nonblocking]
203    fn layout(&self) -> crate::BlockLayout {
204        crate::BlockLayout::Sequential
205    }
206
207    #[nonblocking]
208    fn sample(&self, channel: u16, frame: usize) -> S {
209        assert_eq!(channel, 0, "AudioBlockMonoView only has channel 0");
210        self.sample(frame)
211    }
212
213    #[nonblocking]
214    fn channel_iter(&self, channel: u16) -> impl Iterator<Item = &S> {
215        assert_eq!(channel, 0, "AudioBlockMonoView only has channel 0");
216        self.samples().iter()
217    }
218
219    #[nonblocking]
220    fn channels_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
221        core::iter::once(self.samples().iter())
222    }
223
224    #[nonblocking]
225    fn frame_iter(&self, frame: usize) -> impl Iterator<Item = &S> {
226        assert!(frame < self.num_frames);
227        core::iter::once(&self.data[frame])
228    }
229
230    #[nonblocking]
231    fn frames_iter(&self) -> impl Iterator<Item = impl Iterator<Item = &S> + '_> + '_ {
232        self.data.iter().take(self.num_frames).map(core::iter::once)
233    }
234
235    #[nonblocking]
236    fn as_view(&self) -> impl AudioBlock<S> {
237        self.view()
238    }
239}
240
241impl<S: Sample + core::fmt::Debug> core::fmt::Debug for AudioBlockMonoView<'_, S> {
242    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243        writeln!(f, "AudioBlockMonoView {{")?;
244        writeln!(f, "  num_frames: {}", self.num_frames)?;
245        writeln!(f, "  num_frames_allocated: {}", self.num_frames_allocated)?;
246        writeln!(f, "  samples: {:?}", self.samples())?;
247        writeln!(f, "}}")?;
248        Ok(())
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255    use rtsan_standalone::no_sanitize_realtime;
256
257    #[test]
258    fn test_from_slice() {
259        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
260        let block = AudioBlockMonoView::from_slice(&data);
261        assert_eq!(block.num_frames(), 5);
262        assert_eq!(block.num_frames_allocated(), 5);
263        assert_eq!(block.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
264    }
265
266    #[test]
267    fn test_from_slice_limited() {
268        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
269        let block = AudioBlockMonoView::from_slice_limited(&data, 3, 5);
270        assert_eq!(block.num_frames(), 3);
271        assert_eq!(block.num_frames_allocated(), 5);
272        assert_eq!(block.samples(), &[1.0, 2.0, 3.0]);
273        assert_eq!(block.raw_data(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
274    }
275
276    #[test]
277    fn test_from_ptr() {
278        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
279        let block = unsafe { AudioBlockMonoView::from_ptr(data.as_ptr(), 5) };
280        assert_eq!(block.num_frames(), 5);
281        assert_eq!(block.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
282    }
283
284    #[test]
285    fn test_from_ptr_limited() {
286        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
287        let block = unsafe { AudioBlockMonoView::from_ptr_limited(data.as_ptr(), 3, 5) };
288        assert_eq!(block.num_frames(), 3);
289        assert_eq!(block.num_frames_allocated(), 5);
290        assert_eq!(block.samples(), &[1.0, 2.0, 3.0]);
291    }
292
293    #[test]
294    fn test_sample_access() {
295        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
296        let block = AudioBlockMonoView::from_slice(&data);
297        assert_eq!(block.sample(0), 1.0);
298        assert_eq!(block.sample(2), 3.0);
299        assert_eq!(block.sample(4), 5.0);
300    }
301
302    #[test]
303    fn test_samples_iter() {
304        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
305        let block = AudioBlockMonoView::from_slice(&data);
306        assert_eq!(block.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
307    }
308
309    #[test]
310    fn test_audio_block_trait() {
311        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
312        let block = AudioBlockMonoView::from_slice(&data);
313
314        assert_eq!(block.num_channels(), 1);
315        assert_eq!(block.num_frames(), 5);
316
317        // Test channel_iter
318        let channel: Vec<f32> = block.channel_iter(0).copied().collect();
319        assert_eq!(channel, vec![1.0, 2.0, 3.0, 4.0, 5.0]);
320
321        // Test frame_iter
322        let frame: Vec<f32> = block.frame_iter(2).copied().collect();
323        assert_eq!(frame, vec![3.0]);
324    }
325
326    #[test]
327    fn test_view() {
328        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
329        let block = AudioBlockMonoView::from_slice(&data);
330        let view = block.view();
331        assert_eq!(view.num_frames(), 5);
332        assert_eq!(view.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
333    }
334
335    #[test]
336    #[should_panic]
337    #[no_sanitize_realtime]
338    fn test_sample_out_of_bounds() {
339        let data = [1.0, 2.0, 3.0];
340        let block = AudioBlockMonoView::from_slice(&data);
341        let _ = block.sample(10);
342    }
343
344    #[test]
345    #[should_panic]
346    #[no_sanitize_realtime]
347    fn test_wrong_channel() {
348        let data = [1.0, 2.0, 3.0];
349        let block = AudioBlockMonoView::from_slice(&data);
350        let _ = block.channel_iter(1);
351    }
352}