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::MonoView, AudioBlock};
17///
18/// let data = vec![0.0, 1.0, 2.0, 3.0, 4.0];
19/// let block = MonoView::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 MonoView<'a, S: Sample> {
26    data: &'a [S],
27    num_frames: usize,
28    num_frames_allocated: usize,
29}
30
31impl<'a, S: Sample> MonoView<'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::MonoView, AudioBlock};
41    ///
42    /// let samples = [1.0, 2.0, 3.0, 4.0, 5.0];
43    /// let block = MonoView::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::MonoView, AudioBlock};
74    ///
75    /// let samples = [1.0, 2.0, 3.0, 4.0, 5.0];
76    /// let block = MonoView::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::MonoView, AudioBlock};
108    ///
109    /// let samples = [1.0, 2.0, 3.0, 4.0, 5.0];
110    /// let block = unsafe { MonoView::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) -> MonoView<'_, S> {
171        MonoView::from_slice_limited(self.data, self.num_frames, self.num_frames_allocated)
172    }
173}
174
175impl<S: Sample> AudioBlock<S> for MonoView<'_, S> {
176    type PlanarView = [S; 0];
177
178    #[nonblocking]
179    fn num_channels(&self) -> u16 {
180        1
181    }
182
183    #[nonblocking]
184    fn num_frames(&self) -> usize {
185        self.num_frames
186    }
187
188    #[nonblocking]
189    fn num_channels_allocated(&self) -> u16 {
190        1
191    }
192
193    #[nonblocking]
194    fn num_frames_allocated(&self) -> usize {
195        self.num_frames_allocated
196    }
197
198    #[nonblocking]
199    fn layout(&self) -> crate::BlockLayout {
200        crate::BlockLayout::Sequential
201    }
202
203    #[nonblocking]
204    fn sample(&self, channel: u16, frame: usize) -> S {
205        assert_eq!(channel, 0, "audio_blocks::MonoView only has channel 0");
206        self.sample(frame)
207    }
208
209    #[nonblocking]
210    fn channel_iter(&self, channel: u16) -> impl ExactSizeIterator<Item = &S> {
211        assert_eq!(channel, 0, "audio_blocks::MonoView only has channel 0");
212        self.samples().iter()
213    }
214
215    #[nonblocking]
216    fn channels_iter(&self) -> impl ExactSizeIterator<Item = impl ExactSizeIterator<Item = &S>> {
217        core::iter::once(self.samples().iter())
218    }
219
220    #[nonblocking]
221    fn frame_iter(&self, frame: usize) -> impl ExactSizeIterator<Item = &S> {
222        assert!(frame < self.num_frames);
223        core::iter::once(&self.data[frame])
224    }
225
226    #[nonblocking]
227    fn frames_iter(&self) -> impl ExactSizeIterator<Item = impl ExactSizeIterator<Item = &S>> {
228        self.data.iter().take(self.num_frames).map(core::iter::once)
229    }
230
231    #[nonblocking]
232    fn as_view(&self) -> impl AudioBlock<S> {
233        self.view()
234    }
235}
236
237impl<S: Sample + core::fmt::Debug> core::fmt::Debug for MonoView<'_, S> {
238    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
239        writeln!(f, "audio_blocks::MonoView {{")?;
240        writeln!(f, "  num_frames: {}", self.num_frames)?;
241        writeln!(f, "  num_frames_allocated: {}", self.num_frames_allocated)?;
242        writeln!(f, "  samples: {:?}", self.samples())?;
243        writeln!(f, "}}")?;
244        Ok(())
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251    use rtsan_standalone::no_sanitize_realtime;
252
253    #[test]
254    fn test_from_slice() {
255        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
256        let block = MonoView::from_slice(&data);
257        assert_eq!(block.num_frames(), 5);
258        assert_eq!(block.num_frames_allocated(), 5);
259        assert_eq!(block.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
260    }
261
262    #[test]
263    fn test_from_slice_limited() {
264        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
265        let block = MonoView::from_slice_limited(&data, 3, 5);
266        assert_eq!(block.num_frames(), 3);
267        assert_eq!(block.num_frames_allocated(), 5);
268        assert_eq!(block.samples(), &[1.0, 2.0, 3.0]);
269        assert_eq!(block.raw_data(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
270    }
271
272    #[test]
273    fn test_from_ptr() {
274        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
275        let block = unsafe { MonoView::from_ptr(data.as_ptr(), 5) };
276        assert_eq!(block.num_frames(), 5);
277        assert_eq!(block.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
278    }
279
280    #[test]
281    fn test_from_ptr_limited() {
282        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
283        let block = unsafe { MonoView::from_ptr_limited(data.as_ptr(), 3, 5) };
284        assert_eq!(block.num_frames(), 3);
285        assert_eq!(block.num_frames_allocated(), 5);
286        assert_eq!(block.samples(), &[1.0, 2.0, 3.0]);
287    }
288
289    #[test]
290    fn test_sample_access() {
291        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
292        let block = MonoView::from_slice(&data);
293        assert_eq!(block.sample(0), 1.0);
294        assert_eq!(block.sample(2), 3.0);
295        assert_eq!(block.sample(4), 5.0);
296    }
297
298    #[test]
299    fn test_samples_iter() {
300        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
301        let block = MonoView::from_slice(&data);
302        assert_eq!(block.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
303    }
304
305    #[test]
306    fn test_audio_blocks_trait() {
307        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
308        let block = MonoView::from_slice(&data);
309
310        assert_eq!(block.num_channels(), 1);
311        assert_eq!(block.num_frames(), 5);
312
313        // Test channel_iter
314        let channel: Vec<f32> = block.channel_iter(0).copied().collect();
315        assert_eq!(channel, vec![1.0, 2.0, 3.0, 4.0, 5.0]);
316
317        // Test frame_iter
318        let frame: Vec<f32> = block.frame_iter(2).copied().collect();
319        assert_eq!(frame, vec![3.0]);
320    }
321
322    #[test]
323    fn test_view() {
324        let data = [1.0, 2.0, 3.0, 4.0, 5.0];
325        let block = MonoView::from_slice(&data);
326        let view = block.view();
327        assert_eq!(view.num_frames(), 5);
328        assert_eq!(view.samples(), &[1.0, 2.0, 3.0, 4.0, 5.0]);
329    }
330
331    #[test]
332    #[should_panic]
333    #[no_sanitize_realtime]
334    fn test_sample_out_of_bounds() {
335        let data = [1.0, 2.0, 3.0];
336        let block = MonoView::from_slice(&data);
337        let _ = block.sample(10);
338    }
339
340    #[test]
341    #[should_panic]
342    #[no_sanitize_realtime]
343    fn test_wrong_channel() {
344        let data = [1.0, 2.0, 3.0];
345        let block = MonoView::from_slice(&data);
346        let _ = block.channel_iter(1);
347    }
348}