Skip to main content

nice_plug_core/buffer/
blocks.rs

1//! Per-block per-channel per-sample iterators.
2
3use std::marker::PhantomData;
4
5#[cfg(feature = "simd")]
6use std::simd::Simd;
7
8use super::SamplesIter;
9
10/// An iterator over all samples in the buffer, slicing over the sample-dimension with a maximum
11/// size of `max_block_size`. See [`Buffer::iter_blocks()`][super::Buffer::iter_blocks()]. Yields
12/// both the block and the offset from the start of the buffer.
13pub struct BlocksIter<'slice, 'sample: 'slice> {
14    /// The raw output buffers.
15    pub(super) buffers: *mut [&'sample mut [f32]],
16    pub(super) max_block_size: usize,
17    pub(super) current_block_start: usize,
18    pub(super) _marker: PhantomData<&'slice mut [&'sample mut [f32]]>,
19}
20
21/// A block yielded by [`BlocksIter`]. Can be iterated over once or multiple times, and also
22/// supports direct access to the block's samples if needed.
23pub struct Block<'slice, 'sample: 'slice> {
24    /// The raw output buffers.
25    pub(self) buffers: *mut [&'sample mut [f32]],
26    pub(self) current_block_start: usize,
27    /// The index of the last sample in the block plus one.
28    pub(self) current_block_end: usize,
29    pub(self) _marker: PhantomData<&'slice mut [&'sample mut [f32]]>,
30}
31
32/// An iterator over all channels in a block yielded by [`Block`], returning an entire channel slice
33/// at a time.
34pub struct BlockChannelsIter<'slice, 'sample: 'slice> {
35    /// The raw output buffers.
36    pub(self) buffers: *mut [&'sample mut [f32]],
37    pub(self) current_block_start: usize,
38    pub(self) current_block_end: usize,
39    pub(self) current_channel: usize,
40    pub(self) _marker: PhantomData<&'slice mut [&'sample mut [f32]]>,
41}
42
43impl<'slice, 'sample> Iterator for BlocksIter<'slice, 'sample> {
44    type Item = (usize, Block<'slice, 'sample>);
45
46    #[inline]
47    fn next(&mut self) -> Option<Self::Item> {
48        let buffer_len = unsafe { (*self.buffers).first().map(|b| b.len()).unwrap_or(0) };
49        if self.current_block_start < buffer_len {
50            let current_block_start = self.current_block_start;
51            let current_block_end =
52                (self.current_block_start + self.max_block_size).min(buffer_len);
53            let block = Block {
54                buffers: self.buffers,
55                current_block_start,
56                current_block_end,
57                _marker: self._marker,
58            };
59
60            self.current_block_start += self.max_block_size;
61
62            Some((current_block_start, block))
63        } else {
64            None
65        }
66    }
67
68    #[inline]
69    fn size_hint(&self) -> (usize, Option<usize>) {
70        let buffer_len = unsafe { (*self.buffers).first().map(|b| b.len()).unwrap_or(0) };
71        let remaining = (buffer_len as f32 / self.max_block_size as f32).ceil() as usize;
72
73        (remaining, Some(remaining))
74    }
75}
76
77impl<'slice, 'sample> IntoIterator for Block<'slice, 'sample> {
78    type Item = &'sample mut [f32];
79    type IntoIter = BlockChannelsIter<'slice, 'sample>;
80
81    #[inline]
82    fn into_iter(self) -> Self::IntoIter {
83        BlockChannelsIter {
84            buffers: self.buffers,
85            current_block_start: self.current_block_start,
86            current_block_end: self.current_block_end,
87            current_channel: 0,
88            _marker: self._marker,
89        }
90    }
91}
92
93impl<'slice, 'sample> Iterator for BlockChannelsIter<'slice, 'sample> {
94    type Item = &'sample mut [f32];
95
96    #[inline]
97    fn next(&mut self) -> Option<Self::Item> {
98        if self.current_channel < unsafe { (&(*self.buffers)).len() } {
99            // SAFETY: These bounds have already been checked
100            // SAFETY: It is also not possible to have multiple mutable references to the same
101            //         sample at the same time
102            let slice = unsafe {
103                (&mut (*self.buffers))
104                    .get_unchecked_mut(self.current_channel)
105                    .get_unchecked_mut(self.current_block_start..self.current_block_end)
106            };
107
108            self.current_channel += 1;
109
110            Some(slice)
111        } else {
112            None
113        }
114    }
115
116    #[inline]
117    fn size_hint(&self) -> (usize, Option<usize>) {
118        let remaining = unsafe { (&(*self.buffers)).len() } - self.current_channel;
119
120        (remaining, Some(remaining))
121    }
122}
123
124impl ExactSizeIterator for BlocksIter<'_, '_> {}
125impl ExactSizeIterator for BlockChannelsIter<'_, '_> {}
126
127impl<'slice, 'sample> Block<'slice, 'sample> {
128    /// Get the number of samples per channel in the block.
129    #[inline]
130    pub fn samples(&self) -> usize {
131        self.current_block_end - self.current_block_start
132    }
133
134    /// Returns the number of channels in this buffer.
135    #[inline]
136    pub fn channels(&self) -> usize {
137        unsafe { (&(*self.buffers)).len() }
138    }
139
140    /// A resetting iterator. This lets you iterate over the same block multiple times. Otherwise
141    /// you don't need to use this function as [`Block`] already implements [`Iterator`]. You can
142    /// also use the direct accessor functions on this block instead.
143    #[inline]
144    pub fn iter_mut(&mut self) -> BlockChannelsIter<'slice, 'sample> {
145        BlockChannelsIter {
146            buffers: self.buffers,
147            current_block_start: self.current_block_start,
148            current_block_end: self.current_block_end,
149            current_channel: 0,
150            _marker: self._marker,
151        }
152    }
153
154    /// Iterate over this block on a per-sample per-channel basis. This is identical to
155    /// [`Buffer::iter_samples()`][super::Buffer::iter_samples()] but for a smaller block instead of
156    /// the entire buffer
157    #[inline]
158    pub fn iter_samples(&mut self) -> SamplesIter<'slice, 'sample> {
159        SamplesIter {
160            buffers: self.buffers,
161            current_sample: self.current_block_start,
162            samples_end: self.current_block_end,
163            _marker: self._marker,
164        }
165    }
166
167    /// Access a channel by index. Useful when you would otherwise iterate over this [`Block`]
168    /// multiple times.
169    #[inline]
170    pub fn get(&self, channel_index: usize) -> Option<&[f32]> {
171        // SAFETY: The block bound has already been checked
172        unsafe {
173            Some(
174                (&(*self.buffers))
175                    .get(channel_index)?
176                    .get_unchecked(self.current_block_start..self.current_block_end),
177            )
178        }
179    }
180
181    /// The same as [`get()`][Self::get], but without any bounds checking.
182    ///
183    /// # Safety
184    ///
185    /// `channel_index` must be in the range `0..Self::len()`.
186    #[inline]
187    pub unsafe fn get_unchecked(&self, channel_index: usize) -> &[f32] {
188        unsafe {
189            (&(*self.buffers))
190                .get_unchecked(channel_index)
191                .get_unchecked(self.current_block_start..self.current_block_end)
192        }
193    }
194
195    /// Access a mutable channel by index. Useful when you would otherwise iterate over this
196    /// [`Block`] multiple times.
197    #[inline]
198    pub fn get_mut(&mut self, channel_index: usize) -> Option<&mut [f32]> {
199        // SAFETY: The block bound has already been checked
200        unsafe {
201            Some(
202                (&mut (*self.buffers))
203                    .get_mut(channel_index)?
204                    .get_unchecked_mut(self.current_block_start..self.current_block_end),
205            )
206        }
207    }
208
209    /// The same as [`get_mut()`][Self::get_mut], but without any bounds checking.
210    ///
211    /// # Safety
212    ///
213    /// `channel_index` must be in the range `0..Self::len()`.
214    #[inline]
215    pub unsafe fn get_unchecked_mut(&mut self, channel_index: usize) -> &mut [f32] {
216        unsafe {
217            (&mut (*self.buffers))
218                .get_unchecked_mut(channel_index)
219                .get_unchecked_mut(self.current_block_start..self.current_block_end)
220        }
221    }
222
223    /// Get a SIMD vector containing the channel data for a specific sample in this block. If `LANES
224    /// > channels.len()` then this will be padded with zeroes. If `LANES < channels.len()` then
225    /// this won't contain all values.
226    ///
227    /// Returns a `None` value if `sample_index` is out of bounds.
228    #[cfg(feature = "simd")]
229    #[inline]
230    pub fn to_channel_simd<const LANES: usize>(
231        &self,
232        sample_index: usize,
233    ) -> Option<Simd<f32, LANES>> {
234        if sample_index > self.samples() {
235            return None;
236        }
237
238        let used_lanes = self.samples().max(LANES);
239        let mut values = [0.0; LANES];
240        for (channel_idx, value) in values.iter_mut().enumerate().take(used_lanes) {
241            *value = unsafe {
242                *(&(*self.buffers))
243                    .get_unchecked(channel_idx)
244                    .get_unchecked(self.current_block_start + sample_index)
245            };
246        }
247
248        Some(Simd::from_array(values))
249    }
250
251    /// Get a SIMD vector containing the channel data for a specific sample in this block. Will
252    /// always read exactly `LANES` channels, and does not perform bounds checks on `sample_index`.
253    ///
254    /// # Safety
255    ///
256    /// Undefined behavior if `LANES > block.len()` or if `sample_index > block.len()`.
257    #[cfg(feature = "simd")]
258    #[inline]
259    pub unsafe fn to_channel_simd_unchecked<const LANES: usize>(
260        &self,
261        sample_index: usize,
262    ) -> Simd<f32, LANES> {
263        let mut values = [0.0; LANES];
264        for (channel_idx, value) in values.iter_mut().enumerate() {
265            *value = unsafe {
266                *(&(*self.buffers))
267                    .get_unchecked(channel_idx)
268                    .get_unchecked(self.current_block_start + sample_index)
269            };
270        }
271
272        Simd::from_array(values)
273    }
274
275    /// Write data from a SIMD vector to this sample's channel data for a specific sample in this
276    /// block. This takes the padding added by [`to_channel_simd()`][Self::to_channel_simd()] into
277    /// account.
278    ///
279    /// Returns `false` if `sample_index` is out of bounds.
280    #[cfg(feature = "simd")]
281    #[allow(clippy::wrong_self_convention)]
282    #[inline]
283    pub fn from_channel_simd<const LANES: usize>(
284        &mut self,
285        sample_index: usize,
286        vector: Simd<f32, LANES>,
287    ) -> bool {
288        if sample_index > self.samples() {
289            return false;
290        }
291
292        let used_lanes = self.samples().max(LANES);
293        let values = vector.to_array();
294        for (channel_idx, value) in values.into_iter().enumerate().take(used_lanes) {
295            *unsafe {
296                (&mut (*self.buffers))
297                    .get_unchecked_mut(channel_idx)
298                    .get_unchecked_mut(self.current_block_start + sample_index)
299            } = value;
300        }
301
302        true
303    }
304
305    /// Write data from a SIMD vector to this sample's channel data for a specific sample in this
306    /// block.. This assumes `LANES` matches exactly with the number of channels in the buffer, and
307    /// does not perform bounds checks on `sample_index`.
308    ///
309    /// # Safety
310    ///
311    /// Undefined behavior if `LANES > block.len()` or if `sample_index > block.len()`.
312    #[cfg(feature = "simd")]
313    #[allow(clippy::wrong_self_convention)]
314    #[inline]
315    pub unsafe fn from_channel_simd_unchecked<const LANES: usize>(
316        &mut self,
317        sample_index: usize,
318        vector: Simd<f32, LANES>,
319    ) {
320        let values = vector.to_array();
321        for (channel_idx, value) in values.into_iter().enumerate() {
322            unsafe {
323                *(&mut (*self.buffers))
324                    .get_unchecked_mut(channel_idx)
325                    .get_unchecked_mut(self.current_block_start + sample_index) = value;
326            }
327        }
328    }
329}