Skip to main content

audio_blocks/
ops.rs

1use rtsan_standalone::nonblocking;
2
3use crate::{
4    AudioBlock, AudioBlockMut, BlockLayout, Sample,
5    mono::{AudioBlockMonoView, AudioBlockMonoViewMut},
6};
7
8pub trait AudioBlockOps<S: Sample> {
9    /// Mix all channels to mono by averaging them.
10    /// Panics if source and destination don't have the same number of frames.
11    fn mix_to_mono(&self, dest: &mut AudioBlockMonoViewMut<S>)
12    where
13        S: std::ops::AddAssign + std::ops::Div<Output = S> + From<u16>;
14}
15
16pub trait AudioBlockOpsMut<S: Sample> {
17    /// Copy samples from source block, clamping to the smaller dimensions.
18    /// Resizes destination to `min(src, dst)` channels and frames.
19    /// Never panics - safely handles mismatched block sizes.
20    fn copy_from_block(&mut self, block: &impl AudioBlock<S>);
21    /// Copy samples from source block, requiring exact size match.
22    /// Panics if source and destination don't have identical channels and frames.
23    fn copy_from_block_exact(&mut self, block: &impl AudioBlock<S>);
24    /// Copy a mono block to all channels of this block.
25    /// Panics if blocks don't have the same number of frames.
26    fn copy_mono_to_all_channels(&mut self, mono: &AudioBlockMonoView<S>);
27    /// Gives access to all samples in the block.
28    fn for_each(&mut self, f: impl FnMut(&mut S));
29    /// Gives access to all samples in the block.
30    /// This is faster than `for_each` by not checking bounds of the block.
31    /// It can be used if your algorithm does not change if wrong samples are accessed.
32    /// For example this is the case for gain, clear, etc.
33    fn for_each_including_non_visible(&mut self, f: impl FnMut(&mut S));
34    /// Gives access to all samples in the block while supplying the information
35    /// about which channel and frame number the sample is stored in.
36    fn enumerate(&mut self, f: impl FnMut(u16, usize, &mut S));
37    /// Gives access to all samples in the block while supplying the information
38    /// about which channel and frame number the sample is stored in.
39    ///
40    /// This is faster than `enumerate` by not checking bounds of the block.
41    /// It can be used if your algorithm does not change if wrong samples are accessed.
42    /// For example this is the case for applying a gain, clear, etc.
43    fn enumerate_including_non_visible(&mut self, f: impl FnMut(u16, usize, &mut S));
44    /// Sets all samples in the block to the specified value
45    fn fill_with(&mut self, sample: S);
46    /// Sets all samples in the block to the default value (zero for numeric types)
47    fn clear(&mut self)
48    where
49        S: Default;
50}
51
52impl<S: Sample, B: AudioBlock<S>> AudioBlockOps<S> for B {
53    #[nonblocking]
54    fn mix_to_mono(&self, dest: &mut AudioBlockMonoViewMut<S>)
55    where
56        S: std::ops::AddAssign + std::ops::Div<Output = S> + From<u16>,
57    {
58        assert_eq!(self.num_frames(), dest.num_frames());
59
60        let num_channels = S::from(self.num_channels());
61
62        for frame in 0..self.num_frames() {
63            let mut sum = *self.frame_iter(frame).next().unwrap();
64            for sample in self.frame_iter(frame).skip(1) {
65                sum += *sample;
66            }
67            *dest.sample_mut(frame) = sum / num_channels;
68        }
69    }
70}
71
72impl<S: Sample, B: AudioBlockMut<S>> AudioBlockOpsMut<S> for B {
73    #[nonblocking]
74    fn copy_from_block(&mut self, block: &impl AudioBlock<S>) {
75        let channels = self.num_channels().min(block.num_channels());
76        let frames = self.num_frames().min(block.num_frames());
77
78        self.set_visible(channels, frames);
79        for (this_channel, other_channel) in self.channels_iter_mut().zip(block.channels_iter()) {
80            for (sample_mut, sample) in this_channel.zip(other_channel) {
81                *sample_mut = *sample;
82            }
83        }
84    }
85
86    #[nonblocking]
87    fn copy_from_block_exact(&mut self, block: &impl AudioBlock<S>) {
88        assert_eq!(block.num_channels(), self.num_channels());
89        assert_eq!(block.num_frames(), self.num_frames());
90        for ch in 0..self.num_channels() {
91            for (sample_mut, sample) in self.channel_iter_mut(ch).zip(block.channel_iter(ch)) {
92                *sample_mut = *sample;
93            }
94        }
95    }
96
97    #[nonblocking]
98    fn copy_mono_to_all_channels(&mut self, mono: &AudioBlockMonoView<S>) {
99        assert_eq!(mono.num_frames(), self.num_frames());
100        for channel in self.channels_iter_mut() {
101            for (sample_mut, sample) in channel.zip(mono.samples()) {
102                *sample_mut = *sample;
103            }
104        }
105    }
106
107    #[nonblocking]
108    fn for_each(&mut self, mut f: impl FnMut(&mut S)) {
109        // below 8 channels it is faster to always go per channel
110        if self.num_channels() < 8 {
111            for channel in self.channels_iter_mut() {
112                channel.for_each(&mut f);
113            }
114        } else {
115            match self.layout() {
116                BlockLayout::Sequential | BlockLayout::Planar => {
117                    for channel in self.channels_iter_mut() {
118                        channel.for_each(&mut f);
119                    }
120                }
121                BlockLayout::Interleaved => {
122                    for frame in 0..self.num_frames() {
123                        self.frame_iter_mut(frame).for_each(&mut f);
124                    }
125                }
126            }
127        }
128    }
129
130    #[nonblocking]
131    fn for_each_including_non_visible(&mut self, mut f: impl FnMut(&mut S)) {
132        match self.layout() {
133            BlockLayout::Interleaved => self
134                .as_interleaved_view_mut()
135                .expect("Layout is interleaved")
136                .raw_data_mut()
137                .iter_mut()
138                .for_each(&mut f),
139            BlockLayout::Planar => self
140                .as_planar_view_mut()
141                .expect("Layout is planar")
142                .raw_data_mut()
143                .iter_mut()
144                .for_each(|c| c.as_mut().iter_mut().for_each(&mut f)),
145            BlockLayout::Sequential => self
146                .as_sequential_view_mut()
147                .expect("Layout is sequential")
148                .raw_data_mut()
149                .iter_mut()
150                .for_each(&mut f),
151        }
152    }
153
154    #[nonblocking]
155    fn enumerate(&mut self, mut f: impl FnMut(u16, usize, &mut S)) {
156        // below 8 channels it is faster to always go per channel
157        if self.num_channels() < 8 {
158            for (ch, channel) in self.channels_iter_mut().enumerate() {
159                for (fr, sample) in channel.enumerate() {
160                    f(ch as u16, fr, sample)
161                }
162            }
163        } else {
164            match self.layout() {
165                BlockLayout::Interleaved => {
166                    for (fr, frame) in self.frames_iter_mut().enumerate() {
167                        for (ch, sample) in frame.enumerate() {
168                            f(ch as u16, fr, sample)
169                        }
170                    }
171                }
172                BlockLayout::Planar | BlockLayout::Sequential => {
173                    for (ch, channel) in self.channels_iter_mut().enumerate() {
174                        for (fr, sample) in channel.enumerate() {
175                            f(ch as u16, fr, sample)
176                        }
177                    }
178                }
179            }
180        }
181    }
182
183    #[nonblocking]
184    fn enumerate_including_non_visible(&mut self, mut f: impl FnMut(u16, usize, &mut S)) {
185        match self.layout() {
186            BlockLayout::Interleaved => {
187                let num_frames = self.num_frames_allocated();
188                self.as_interleaved_view_mut()
189                    .expect("Layout is interleaved")
190                    .raw_data_mut()
191                    .iter_mut()
192                    .enumerate()
193                    .for_each(|(i, sample)| {
194                        let channel = i % num_frames;
195                        let frame = i / num_frames;
196                        f(channel as u16, frame, sample)
197                    });
198            }
199            BlockLayout::Planar => self
200                .as_planar_view_mut()
201                .expect("Layout is planar")
202                .raw_data_mut()
203                .iter_mut()
204                .enumerate()
205                .for_each(|(ch, v)| {
206                    v.as_mut()
207                        .iter_mut()
208                        .enumerate()
209                        .for_each(|(frame, sample)| f(ch as u16, frame, sample))
210                }),
211            BlockLayout::Sequential => {
212                let num_frames = self.num_frames_allocated();
213                self.as_sequential_view_mut()
214                    .expect("Layout is sequential")
215                    .raw_data_mut()
216                    .iter_mut()
217                    .enumerate()
218                    .for_each(|(i, sample)| {
219                        let channel = i / num_frames;
220                        let frame = i % num_frames;
221                        f(channel as u16, frame, sample)
222                    });
223            }
224        }
225    }
226
227    #[nonblocking]
228    fn fill_with(&mut self, sample: S) {
229        self.for_each_including_non_visible(|v| *v = sample);
230    }
231
232    #[nonblocking]
233    fn clear(&mut self)
234    where
235        S: Default,
236    {
237        self.fill_with(S::default());
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use rtsan_standalone::no_sanitize_realtime;
244
245    use crate::{
246        interleaved::AudioBlockInterleavedViewMut,
247        planar::AudioBlockPlanarViewMut,
248        sequential::{AudioBlockSequentialView, AudioBlockSequentialViewMut},
249    };
250
251    use super::*;
252
253    #[test]
254    fn test_copy_from_block_resizes_to_smaller() {
255        // Destination is larger than source - should resize to source size
256        let mut data = [0.0; 15];
257        let mut block = AudioBlockInterleavedViewMut::from_slice(&mut data, 3);
258        let view =
259            AudioBlockSequentialView::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], 2);
260        block.copy_from_block(&view);
261
262        assert_eq!(block.num_channels(), 2);
263        assert_eq!(block.num_frames(), 4);
264        assert_eq!(block.num_channels_allocated(), 3);
265        assert_eq!(block.num_frames_allocated(), 5);
266
267        assert_eq!(
268            block.channel_iter(0).copied().collect::<Vec<_>>(),
269            vec![0.0, 1.0, 2.0, 3.0]
270        );
271        assert_eq!(
272            block.channel_iter(1).copied().collect::<Vec<_>>(),
273            vec![4.0, 5.0, 6.0, 7.0]
274        );
275    }
276
277    #[test]
278    fn test_copy_from_block_clamps_to_dest_size() {
279        // Source is larger than destination - should clamp to destination size
280        let mut data = [0.0; 4]; // 2 channels, 2 frames
281        let mut block = AudioBlockSequentialViewMut::from_slice(&mut data, 2);
282        let view =
283            AudioBlockSequentialView::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], 2);
284        block.copy_from_block(&view);
285
286        assert_eq!(block.num_channels(), 2);
287        assert_eq!(block.num_frames(), 2);
288        // Only first 2 frames copied from each channel
289        assert_eq!(
290            block.channel_iter(0).copied().collect::<Vec<_>>(),
291            vec![0.0, 1.0]
292        );
293        assert_eq!(
294            block.channel_iter(1).copied().collect::<Vec<_>>(),
295            vec![4.0, 5.0]
296        );
297    }
298
299    #[test]
300    fn test_copy_from_block_clamps_channels() {
301        // Source has more channels than destination
302        let mut data = [0.0; 4]; // 1 channel, 4 frames
303        let mut block = AudioBlockSequentialViewMut::from_slice(&mut data, 1);
304        let view =
305            AudioBlockSequentialView::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], 2);
306        block.copy_from_block(&view);
307
308        assert_eq!(block.num_channels(), 1);
309        assert_eq!(block.num_frames(), 4);
310        assert_eq!(
311            block.channel_iter(0).copied().collect::<Vec<_>>(),
312            vec![0.0, 1.0, 2.0, 3.0]
313        );
314    }
315
316    #[test]
317    fn test_copy_from_block_exact() {
318        let mut data = [0.0; 8];
319        let mut block = AudioBlockInterleavedViewMut::from_slice(&mut data, 2);
320        let view =
321            AudioBlockSequentialView::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], 2);
322        block.copy_from_block_exact(&view);
323
324        assert_eq!(block.num_channels(), 2);
325        assert_eq!(block.num_frames(), 4);
326
327        assert_eq!(
328            block.channel_iter(0).copied().collect::<Vec<_>>(),
329            vec![0.0, 1.0, 2.0, 3.0]
330        );
331        assert_eq!(
332            block.channel_iter(1).copied().collect::<Vec<_>>(),
333            vec![4.0, 5.0, 6.0, 7.0]
334        );
335    }
336
337    #[test]
338    #[should_panic]
339    #[no_sanitize_realtime]
340    fn test_copy_from_block_exact_wrong_channels() {
341        let mut data = [0.0; 12];
342        let mut block = AudioBlockInterleavedViewMut::from_slice(&mut data, 3);
343        let view =
344            AudioBlockSequentialView::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], 2);
345        block.copy_from_block_exact(&view);
346    }
347
348    #[test]
349    #[should_panic]
350    #[no_sanitize_realtime]
351    fn test_copy_from_block_exact_wrong_frames() {
352        let mut data = [0.0; 10];
353        let mut block = AudioBlockInterleavedViewMut::from_slice(&mut data, 2);
354        let view =
355            AudioBlockSequentialView::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], 2);
356        block.copy_from_block_exact(&view);
357    }
358
359    #[test]
360    fn test_for_each() {
361        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
362        let mut block = AudioBlockSequentialViewMut::from_slice(&mut data, 2);
363
364        let mut i = 0;
365        let mut c_exp = 0;
366        let mut f_exp = 0;
367        block.enumerate_including_non_visible(|c, f, v| {
368            assert_eq!(c, c_exp);
369            assert_eq!(f, f_exp);
370            assert_eq!(*v, i as f32);
371            if f_exp == 3 {
372                c_exp = (c_exp + 1) % 4;
373            }
374            f_exp = (f_exp + 1) % 4;
375            i += 1;
376        });
377
378        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
379        let mut block = AudioBlockInterleavedViewMut::from_slice(&mut data, 2);
380
381        let mut i = 0;
382        let mut f_exp = 0;
383        let mut c_exp = 0;
384        block.enumerate_including_non_visible(|c, f, v| {
385            assert_eq!(c, c_exp);
386            assert_eq!(f, f_exp);
387            assert_eq!(*v, i as f32);
388            if c_exp == 3 {
389                f_exp = (f_exp + 1) % 4;
390            }
391            c_exp = (c_exp + 1) % 4;
392            i += 1;
393        });
394
395        let mut data = [[0.0, 1.0, 2.0, 3.0], [4.0, 5.0, 6.0, 7.0]];
396        let mut block = AudioBlockPlanarViewMut::from_slice(&mut data);
397
398        let mut i = 0;
399        let mut c_exp = 0;
400        let mut f_exp = 0;
401        block.enumerate_including_non_visible(|c, f, v| {
402            assert_eq!(c, c_exp);
403            assert_eq!(f, f_exp);
404            assert_eq!(*v, i as f32);
405            if f_exp == 3 {
406                c_exp = (c_exp + 1) % 4;
407            }
408            f_exp = (f_exp + 1) % 4;
409            i += 1;
410        });
411    }
412
413    #[test]
414    fn test_clear() {
415        let mut data = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
416        let mut block = AudioBlockSequentialViewMut::from_slice(&mut data, 2);
417
418        block.fill_with(1.0);
419
420        assert_eq!(
421            block.channel_iter(0).copied().collect::<Vec<_>>(),
422            vec![1.0, 1.0, 1.0, 1.0]
423        );
424        assert_eq!(
425            block.channel_iter(1).copied().collect::<Vec<_>>(),
426            vec![1.0, 1.0, 1.0, 1.0]
427        );
428
429        block.clear();
430
431        assert_eq!(
432            block.channel_iter(0).copied().collect::<Vec<_>>(),
433            vec![0.0, 0.0, 0.0, 0.0]
434        );
435        assert_eq!(
436            block.channel_iter(1).copied().collect::<Vec<_>>(),
437            vec![0.0, 0.0, 0.0, 0.0]
438        );
439    }
440
441    #[test]
442    fn test_mix_to_mono() {
443        use crate::mono::AudioBlockMonoViewMut;
444
445        let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
446        let block = AudioBlockSequentialView::from_slice(&data, 2);
447
448        let mut mono_data = [0.0; 4];
449        let mut mono = AudioBlockMonoViewMut::from_slice(&mut mono_data);
450
451        block.mix_to_mono(&mut mono);
452
453        assert_eq!(mono.num_frames(), 4);
454        assert_eq!(
455            mono.samples().iter().copied().collect::<Vec<_>>(),
456            vec![3.0, 4.0, 5.0, 6.0] // (1+5)/2, (2+6)/2, (3+7)/2, (4+8)/2
457        );
458    }
459
460    #[test]
461    fn test_copy_mono_to_all_channels() {
462        use crate::mono::AudioBlockMonoView;
463
464        let mono_data = [1.0, 2.0, 3.0, 4.0];
465        let mono = AudioBlockMonoView::from_slice(&mono_data);
466
467        let mut data = [0.0; 12];
468        let mut block = AudioBlockSequentialViewMut::from_slice(&mut data, 3);
469
470        block.copy_mono_to_all_channels(&mono);
471
472        assert_eq!(
473            block.channel_iter(0).copied().collect::<Vec<_>>(),
474            vec![1.0, 2.0, 3.0, 4.0]
475        );
476        assert_eq!(
477            block.channel_iter(1).copied().collect::<Vec<_>>(),
478            vec![1.0, 2.0, 3.0, 4.0]
479        );
480        assert_eq!(
481            block.channel_iter(2).copied().collect::<Vec<_>>(),
482            vec![1.0, 2.0, 3.0, 4.0]
483        );
484    }
485}