Skip to main content

nice_plug_core/buffer/
samples.rs

1//! Per-sample per-channel iterators.
2
3use std::marker::PhantomData;
4
5#[cfg(feature = "simd")]
6use std::simd::Simd;
7
8/// An iterator over all samples in a buffer or block, yielding iterators over each channel for
9/// every sample. This iteration order offers good cache locality for per-sample access.
10pub struct SamplesIter<'slice, 'sample: 'slice> {
11    /// The raw output buffers.
12    pub(super) buffers: *mut [&'sample mut [f32]],
13    pub(super) current_sample: usize,
14    /// The last sample index to iterate over plus one. Would be equal to `buffers.len()` when
15    /// iterating over an entire buffer, but this can also be used to iterate over smaller blocks in
16    /// a similar fashion.
17    pub(super) samples_end: usize,
18    pub(super) _marker: PhantomData<&'slice mut [&'sample mut [f32]]>,
19}
20
21/// Can construct iterators over actual iterator over the channel data for a sample, yielded by
22/// [`SamplesIter`]. Can be turned into an iterator, or [`ChannelSamples::iter_mut()`] can be used
23/// to iterate over the channel data multiple times, or more efficiently you can use
24/// [`ChannelSamples::get_unchecked_mut()`] to do the same thing.
25pub struct ChannelSamples<'slice, 'sample: 'slice> {
26    /// The raw output buffers.
27    pub(self) buffers: *mut [&'sample mut [f32]],
28    pub(self) current_sample: usize,
29    pub(self) _marker: PhantomData<&'slice mut [&'sample mut [f32]]>,
30}
31
32/// The actual iterator over the channel data for a sample, yielded by [`ChannelSamples`].
33pub struct ChannelSamplesIter<'slice, 'sample: 'slice> {
34    /// The raw output buffers.
35    pub(self) buffers: *mut [&'sample mut [f32]],
36    pub(self) current_sample: usize,
37    pub(self) current_channel: usize,
38    pub(self) _marker: PhantomData<&'slice mut [&'sample mut [f32]]>,
39}
40
41impl<'slice, 'sample> Iterator for SamplesIter<'slice, 'sample> {
42    type Item = ChannelSamples<'slice, 'sample>;
43
44    #[inline]
45    fn next(&mut self) -> Option<Self::Item> {
46        // This can iterate over both the entire buffer or over a smaller sample slice of it
47        if self.current_sample < self.samples_end {
48            let channels = ChannelSamples {
49                buffers: self.buffers,
50                current_sample: self.current_sample,
51                _marker: self._marker,
52            };
53
54            self.current_sample += 1;
55
56            Some(channels)
57        } else {
58            None
59        }
60    }
61
62    #[inline]
63    fn size_hint(&self) -> (usize, Option<usize>) {
64        let remaining = self.samples_end - self.current_sample;
65
66        (remaining, Some(remaining))
67    }
68}
69
70impl<'slice, 'sample> IntoIterator for ChannelSamples<'slice, 'sample> {
71    type Item = &'sample mut f32;
72    type IntoIter = ChannelSamplesIter<'slice, 'sample>;
73
74    #[inline]
75    fn into_iter(self) -> Self::IntoIter {
76        ChannelSamplesIter {
77            buffers: self.buffers,
78            current_sample: self.current_sample,
79            current_channel: 0,
80            _marker: self._marker,
81        }
82    }
83}
84
85impl<'slice, 'sample> Iterator for ChannelSamplesIter<'slice, 'sample> {
86    type Item = &'sample mut f32;
87
88    #[inline]
89    fn next(&mut self) -> Option<Self::Item> {
90        if self.current_channel < unsafe { (&(*self.buffers)).len() } {
91            // SAFETY: These bounds have already been checked
92            // SAFETY: It is also not possible to have multiple mutable references to the same
93            // sample at the same time
94            let sample = unsafe {
95                (&mut (*self.buffers))
96                    .get_unchecked_mut(self.current_channel)
97                    .get_unchecked_mut(self.current_sample)
98            };
99
100            self.current_channel += 1;
101
102            Some(sample)
103        } else {
104            None
105        }
106    }
107
108    #[inline]
109    fn size_hint(&self) -> (usize, Option<usize>) {
110        let remaining = unsafe { (&(*self.buffers)).len() } - self.current_channel;
111
112        (remaining, Some(remaining))
113    }
114}
115
116impl ExactSizeIterator for SamplesIter<'_, '_> {}
117impl ExactSizeIterator for ChannelSamplesIter<'_, '_> {}
118
119impl<'slice, 'sample> ChannelSamples<'slice, 'sample> {
120    /// Get the number of channels.
121    #[allow(clippy::len_without_is_empty)]
122    #[inline]
123    pub fn len(&self) -> usize {
124        unsafe { (&(*self.buffers)).len() }
125    }
126
127    /// A resetting iterator. This lets you iterate over the same channels multiple times. Otherwise
128    /// you don't need to use this function as [`ChannelSamples`] already implements
129    /// [`IntoIterator`].
130    #[inline]
131    pub fn iter_mut(&mut self) -> ChannelSamplesIter<'slice, 'sample> {
132        ChannelSamplesIter {
133            buffers: self.buffers,
134            current_sample: self.current_sample,
135            current_channel: 0,
136            _marker: self._marker,
137        }
138    }
139
140    /// Access a sample by index. Useful when you would otherwise iterate over this 'Channels'
141    /// iterator multiple times.
142    #[inline]
143    pub fn get_mut(&mut self, channel_index: usize) -> Option<&mut f32> {
144        // SAFETY: The sample bound has already been checked
145        unsafe {
146            Some(
147                (&mut (*self.buffers))
148                    .get_mut(channel_index)?
149                    .get_unchecked_mut(self.current_sample),
150            )
151        }
152    }
153
154    /// The same as [`get_mut()`][Self::get_mut()], but without any bounds checking.
155    ///
156    /// # Safety
157    ///
158    /// `channel_index` must be in the range `0..Self::len()`.
159    #[inline]
160    pub unsafe fn get_unchecked_mut(&mut self, channel_index: usize) -> &mut f32 {
161        unsafe {
162            (&mut (*self.buffers))
163                .get_unchecked_mut(channel_index)
164                .get_unchecked_mut(self.current_sample)
165        }
166    }
167
168    /// Get a SIMD vector containing the channel data for this buffer. If `LANES > channels.len()`
169    /// then this will be padded with zeroes. If `LANES < channels.len()` then this won't contain
170    /// all values.
171    #[cfg(feature = "simd")]
172    #[inline]
173    pub fn to_simd<const LANES: usize>(&self) -> Simd<f32, LANES> {
174        let used_lanes = self.len().max(LANES);
175        let mut values = [0.0; LANES];
176        for (channel_idx, value) in values.iter_mut().enumerate().take(used_lanes) {
177            *value = unsafe {
178                *(&(*self.buffers))
179                    .get_unchecked(channel_idx)
180                    .get_unchecked(self.current_sample)
181            };
182        }
183
184        Simd::from_array(values)
185    }
186
187    /// Get a SIMD vector containing the channel data for this buffer. Will always read exactly
188    /// `LANES` channels.
189    ///
190    /// # Safety
191    ///
192    /// Undefined behavior if `LANES > channels.len()`.
193    #[cfg(feature = "simd")]
194    #[inline]
195    pub unsafe fn to_simd_unchecked<const LANES: usize>(&self) -> Simd<f32, LANES> {
196        let mut values = [0.0; LANES];
197        for (channel_idx, value) in values.iter_mut().enumerate() {
198            *value = unsafe {
199                *(&(*self.buffers))
200                    .get_unchecked(channel_idx)
201                    .get_unchecked(self.current_sample)
202            };
203        }
204
205        Simd::from_array(values)
206    }
207
208    /// Write data from a SIMD vector to this sample's channel data. This takes the padding added by
209    /// [`to_simd()`][Self::to_simd()] into account.
210    #[cfg(feature = "simd")]
211    #[allow(clippy::wrong_self_convention)]
212    #[inline]
213    pub fn from_simd<const LANES: usize>(&mut self, vector: Simd<f32, LANES>) {
214        let used_lanes = self.len().max(LANES);
215        let values = vector.to_array();
216        for (channel_idx, value) in values.into_iter().enumerate().take(used_lanes) {
217            *unsafe {
218                (&mut (*self.buffers))
219                    .get_unchecked_mut(channel_idx)
220                    .get_unchecked_mut(self.current_sample)
221            } = value;
222        }
223    }
224
225    /// Write data from a SIMD vector to this sample's channel data. This assumes `LANES` matches
226    /// exactly with the number of channels in the buffer.
227    ///
228    /// # Safety
229    ///
230    /// Undefined behavior if `LANES > channels.len()`.
231    #[cfg(feature = "simd")]
232    #[allow(clippy::wrong_self_convention)]
233    #[inline]
234    pub unsafe fn from_simd_unchecked<const LANES: usize>(&mut self, vector: Simd<f32, LANES>) {
235        let values = vector.to_array();
236        for (channel_idx, value) in values.into_iter().enumerate() {
237            unsafe {
238                *(&mut (*self.buffers))
239                    .get_unchecked_mut(channel_idx)
240                    .get_unchecked_mut(self.current_sample) = value;
241            }
242        }
243    }
244}