Skip to main content

clack_plugin/process/audio/
pair.rs

1use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut};
2use crate::process::Audio;
3use crate::process::audio::pair::ChannelPair::*;
4use crate::process::audio::{BufferError, InputPort, OutputPort, SampleType};
5use clack_common::process::{AudioPortProcessingInfo, ConstantMask};
6use clap_sys::audio_buffer::clap_audio_buffer;
7use std::slice::{Iter, IterMut};
8
9/// A pair of Input and Output ports.
10///
11/// Note that in case of asymmetric port layouts (e.g. side-chain), there may not
12/// be an associated output port to an input port, or vice-versa. The same goes for
13/// paired channels.
14///
15/// In those cases, a given pair will only contain one port instead of two. However,
16/// a [`PortPair`] is always guaranteed to contain at least one port, be it an input or output.
17pub struct PortPair<'a> {
18    input: Option<&'a clap_audio_buffer>,
19    output: Option<&'a mut clap_audio_buffer>,
20    frames_count: u32,
21}
22
23impl<'a> PortPair<'a> {
24    /// # Safety
25    ///
26    /// * Both provided buffers must be valid (if not `None`);
27    /// * `frames_count` *must* match the size of the buffers.
28    #[inline]
29    pub(crate) unsafe fn from_raw(
30        input: Option<&'a clap_audio_buffer>,
31        output: Option<&'a mut clap_audio_buffer>,
32        frames_count: u32,
33    ) -> Option<Self> {
34        match (input, output) {
35            (None, None) => None,
36            (input, output) => Some(PortPair {
37                input,
38                output,
39                frames_count,
40            }),
41        }
42    }
43
44    /// Gets the [`InputPort`] of this pair.
45    ///
46    /// If the port layout is asymmetric and there is no input port, this returns [`None`].
47    #[inline]
48    pub fn input(&self) -> Option<InputPort<'_>> {
49        self.input
50            // SAFETY: this type ensures the buffer is valid and matches frame_count
51            .map(|i| unsafe { InputPort::from_raw(i, self.frames_count) })
52    }
53
54    /// Gets the [`OutputPort`] of this pair.
55    ///
56    /// If the port layout is asymmetric and there is no output port, this returns [`None`].
57    #[inline]
58    pub fn output(&mut self) -> Option<OutputPort<'_>> {
59        self.output
60            .as_mut()
61            // SAFETY: this type ensures the buffer is valid and matches frame_count
62            .map(|i| unsafe { OutputPort::from_raw(i, self.frames_count) })
63    }
64
65    /// Retrieves the port info for the input of this pair.
66    ///
67    /// If the port layout is asymmetric and there is no input port, this returns [`None`].
68    #[inline]
69    pub fn input_info(&self) -> Option<AudioPortProcessingInfo> {
70        self.input
71            .as_ref()
72            .map(|buf| AudioPortProcessingInfo::from_raw(buf))
73    }
74
75    /// Retrieves the port info for the output of this pair.
76    ///
77    /// If the port layout is asymmetric and there is no output port, this returns [`None`].
78    #[inline]
79    pub fn output_info(&self) -> Option<AudioPortProcessingInfo> {
80        self.output
81            .as_ref()
82            .map(|buf| AudioPortProcessingInfo::from_raw(buf))
83    }
84
85    /// Retrieves the port pair's channels.
86    ///
87    /// Because each port can hold either [`f32`] or [`f64`] sample data, this method returns a
88    /// [`SampleType`] enum of the paired channels, to indicate which one the ports holds.
89    ///
90    /// # Errors
91    ///
92    /// This method returns a [`BufferError::InvalidChannelBuffer`] if the host provided neither
93    /// [`f32`] nor [`f64`] buffer type, which is invalid per the CLAP specification.
94    ///
95    /// Additionally, if the two port have different buffer sample types (i.e. one holds [`f32`]
96    /// and the other holds [`f64`]), then a [`BufferError::MismatchedBufferPair`] error is returned.
97    ///
98    /// # Example
99    ///
100    /// ```
101    /// use clack_plugin::process::audio::{PairedChannels, PortPair, SampleType};
102    ///
103    /// # fn foo(port: PortPair) {
104    /// let mut port: PortPair = /* ... */
105    /// # port;
106    ///
107    /// // Decide what to do using by matching against every possible configuration
108    /// match port.channels().unwrap() {
109    ///     SampleType::F32(buf) => { /* Process the 32-bit buffers */ },
110    ///     SampleType::F64(buf) => { /* Process the 64-bit buffers */ },
111    ///     SampleType::Both(buf32, buf64) => { /* We have both types of buffers available */ }
112    /// }
113    ///
114    /// // If we're only interested in a single buffer type,
115    /// // we can use SampleType's helper methods:
116    /// let channels: PairedChannels<f32> = port.channels().unwrap().into_f32().unwrap();
117    /// # }
118    /// ```
119    #[inline]
120    pub fn channels(
121        &mut self,
122    ) -> Result<SampleType<PairedChannels<'a, f32>, PairedChannels<'a, f64>>, BufferError> {
123        let input = match self.input {
124            None => SampleType::Both([].as_slice(), [].as_slice()),
125            // SAFETY: this type ensures the buffer is valid
126            Some(buffer) => unsafe { SampleType::from_raw_buffer(buffer)? },
127        };
128
129        let output = match self.output.as_mut() {
130            None => SampleType::Both([].as_mut_slice(), [].as_mut_slice()),
131            // SAFETY: this type ensures the buffer is valid
132            Some(buffer) => unsafe { SampleType::from_raw_buffer_mut(buffer)? },
133        };
134
135        Ok(input.try_match_with(output)?.map(
136            |(i, o)| PairedChannels {
137                input_data: i,
138                output_data: o,
139                frames_count: self.frames_count,
140            },
141            |(i, o)| PairedChannels {
142                input_data: i,
143                output_data: o,
144                frames_count: self.frames_count,
145            },
146        ))
147    }
148
149    /// The number of channels in this port pair.
150    ///
151    /// Since there may be more channels in one port than in the other, this method also counts
152    /// the partial [`ChannelPair`]s that can be returned, and therefore returns the maximum number
153    /// of channels between the two ports.  
154    #[inline]
155    pub fn channel_pair_count(&self) -> usize {
156        let in_channels = self.input.map(|b| b.channel_count).unwrap_or(0);
157        let out_channels = self.output.as_ref().map(|b| b.channel_count).unwrap_or(0);
158
159        in_channels.max(out_channels) as usize
160    }
161
162    /// Returns the number of frames to process in this block.
163    ///
164    /// This will always match the number of samples of every audio channel buffer. The two ports
165    /// *cannot* have different buffer sizes.
166    #[inline]
167    pub fn frames_count(&self) -> u32 {
168        self.frames_count
169    }
170
171    /// The latency from and to the audio interface for this port pair, in samples.
172    ///
173    /// This returns a tuple containing the latenciess for the input and output port,
174    /// respectively.
175    ///
176    /// If one port isn't present in this pair, then [`None`] is returned.
177    #[inline]
178    pub fn latencies(&self) -> (Option<u32>, Option<u32>) {
179        (
180            self.input.map(|i| i.latency),
181            self.output.as_ref().map(|o| o.latency),
182        )
183    }
184
185    /// The [`ConstantMask`]s for the two ports.
186    ///
187    /// This returns a tuple containing the [`ConstantMask`]s for the input and output port,
188    /// respectively.
189    ///
190    /// If one port isn't present in this pair, then [`ConstantMask::FULLY_CONSTANT`] is returned.
191    #[inline]
192    pub fn constant_masks(&self) -> (ConstantMask, ConstantMask) {
193        (
194            self.input
195                .map(|i| ConstantMask::from_bits(i.constant_mask))
196                .unwrap_or(ConstantMask::FULLY_CONSTANT),
197            self.output
198                .as_ref()
199                .map(|o| ConstantMask::from_bits(o.constant_mask))
200                .unwrap_or(ConstantMask::FULLY_CONSTANT),
201        )
202    }
203}
204
205/// An [`PortPair`]'s channels' data buffers, which contains samples of a given type `S`.
206///
207/// The sample type `S` is always going to be either [`f32`] or [`f64`], as returned by
208/// [`PortPair::channels`].
209pub struct PairedChannels<'a, S> {
210    input_data: &'a [*mut S],
211    output_data: &'a mut [*mut S],
212    frames_count: u32,
213}
214
215impl<'a, S> PairedChannels<'a, S> {
216    /// Returns the number of frames to process in this block.
217    ///
218    /// This will always match the number of samples of every audio channel buffer, from both
219    /// the input and output port.
220    #[inline]
221    pub fn frames_count(&self) -> u32 {
222        self.frames_count
223    }
224
225    /// The number of input channels.
226    #[inline]
227    pub fn input_channel_count(&self) -> usize {
228        self.input_data.len()
229    }
230
231    /// The number of output channels.
232    #[inline]
233    pub fn output_channel_count(&self) -> usize {
234        self.output_data.len()
235    }
236
237    /// The total number of channel pairs.
238    ///
239    /// Since there may be more channels in one port than in the other, this method also counts
240    /// the partial [`ChannelPair`]s that can be returned, and therefore returns the maximum number
241    /// of channels between the input and output ports.  
242    #[inline]
243    pub fn channel_pair_count(&self) -> usize {
244        self.input_channel_count().max(self.output_channel_count())
245    }
246
247    /// Retrieves the pair of sample buffers for the pair of channels at a given index.
248    ///
249    /// If there is no channel at the given index (i.e. `channel_index` is greater or equal than
250    /// [`channel_pair_count`](Self::channel_pair_count)), this returns [`None`].
251    ///
252    /// See [`ChannelPair`]'s documentation for examples on how to access sample buffers from it.
253    #[inline]
254    pub fn channel_pair(&mut self, index: usize) -> Option<ChannelPair<'a, S>> {
255        let input = self
256            .input_data
257            .get(index)
258            // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
259            .map(|ptr| unsafe { slice_from_external_parts(*ptr, self.frames_count as usize) });
260
261        let output = self
262            .output_data
263            .get(index)
264            // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
265            .map(|ptr| unsafe { slice_from_external_parts_mut(*ptr, self.frames_count as usize) });
266
267        ChannelPair::from_optional_io(input, output)
268    }
269
270    /// Gets an iterator over all the ports' [`ChannelPair`]s.
271    #[inline]
272    pub fn iter_mut(&mut self) -> PairedChannelsIter<'_, S> {
273        PairedChannelsIter {
274            input_iter: self.input_data.iter(),
275            output_iter: self.output_data.iter_mut(),
276            frames_count: self.frames_count,
277        }
278    }
279}
280
281impl<'a, S> IntoIterator for PairedChannels<'a, S> {
282    type Item = ChannelPair<'a, S>;
283    type IntoIter = PairedChannelsIter<'a, S>;
284
285    #[inline]
286    fn into_iter(self) -> Self::IntoIter {
287        PairedChannelsIter {
288            input_iter: self.input_data.iter(),
289            output_iter: self.output_data.iter_mut(),
290            frames_count: self.frames_count,
291        }
292    }
293}
294
295/// An iterator over all of a [`PortPair`]'s [`ChannelPair`]s.
296pub struct PairedChannelsIter<'a, S> {
297    input_iter: Iter<'a, *mut S>,
298    output_iter: IterMut<'a, *mut S>,
299    frames_count: u32,
300}
301
302impl<'a, S> Iterator for PairedChannelsIter<'a, S> {
303    type Item = ChannelPair<'a, S>;
304
305    #[inline]
306    fn next(&mut self) -> Option<Self::Item> {
307        let input = self
308            .input_iter
309            .next()
310            // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
311            .map(|ptr| unsafe { slice_from_external_parts(*ptr, self.frames_count as usize) });
312
313        // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
314        let output = self.output_iter.next().map(|ptr| unsafe {
315            slice_from_external_parts_mut((*ptr) as *mut _, self.frames_count as usize)
316        });
317
318        ChannelPair::from_optional_io(input, output)
319    }
320
321    #[inline]
322    fn size_hint(&self) -> (usize, Option<usize>) {
323        (self.len(), Some(self.len()))
324    }
325}
326
327impl<S> ExactSizeIterator for PairedChannelsIter<'_, S> {
328    #[inline]
329    fn len(&self) -> usize {
330        self.input_iter.len().max(self.output_iter.len())
331    }
332}
333
334/// An iterator of all of the available [`PortPair`]s from an [`Audio`] struct.
335pub struct PortPairsIter<'a> {
336    inputs: Iter<'a, clap_audio_buffer>,
337    outputs: IterMut<'a, clap_audio_buffer>,
338    frames_count: u32,
339}
340
341impl<'a> PortPairsIter<'a> {
342    #[inline]
343    pub(crate) fn new(audio: &'a mut Audio<'_>) -> Self {
344        Self {
345            inputs: audio.inputs.iter(),
346            outputs: audio.outputs.iter_mut(),
347            frames_count: audio.frames_count,
348        }
349    }
350}
351
352impl<'a> Iterator for PortPairsIter<'a> {
353    type Item = PortPair<'a>;
354
355    #[inline]
356    fn next(&mut self) -> Option<Self::Item> {
357        // SAFETY: the audio type this is created from
358        unsafe { PortPair::from_raw(self.inputs.next(), self.outputs.next(), self.frames_count) }
359    }
360
361    #[inline]
362    fn size_hint(&self) -> (usize, Option<usize>) {
363        (self.len(), Some(self.len()))
364    }
365}
366
367impl ExactSizeIterator for PortPairsIter<'_> {
368    #[inline]
369    fn len(&self) -> usize {
370        self.inputs.len().max(self.outputs.len())
371    }
372}
373
374/// A pair of input and output channel buffers.
375///
376/// Because port and channel layouts may differ between inputs and outputs, a [`ChannelPair`]
377/// may actually contain only an input or an output instead of both.
378///
379/// This enum also allows to check for the fairly common case where Host may re-use the same
380/// buffer for both the input and output, and expect the plugin to do in-place processing.
381pub enum ChannelPair<'a, S> {
382    /// There is only an input channel present, there was no matching output.
383    InputOnly(&'a [S]),
384    /// There is only an output channel present, there was no matching input.
385    OutputOnly(&'a mut [S]),
386    /// Both the input and output channels are present, and available separately.
387    InputOutput(&'a [S], &'a mut [S]),
388    /// Both the input and output channels are present, but they actually share the same buffer.
389    ///
390    /// In this case, the slice is already filled with the input channel's data, and the host
391    /// considers the contents of this buffer after processing to be the output channel's data.
392    InPlace(&'a mut [S]),
393}
394
395impl<'a, S> ChannelPair<'a, S> {
396    #[inline]
397    pub(crate) fn from_optional_io(
398        input: Option<&'a [S]>,
399        output: Option<&'a mut [S]>,
400    ) -> Option<ChannelPair<'a, S>> {
401        match (input, output) {
402            (None, None) => None,
403            (Some(input), None) => Some(InputOnly(input)),
404            (None, Some(output)) => Some(OutputOnly(output)),
405            (Some(input), Some(output)) => Some(if input.as_ptr() == output.as_ptr() {
406                InPlace(output)
407            } else {
408                InputOutput(input, output)
409            }),
410        }
411    }
412
413    /// Attempts to retrieve the input channel's buffer data, if the input channel is present.
414    #[inline]
415    pub fn input(&'a self) -> Option<&'a [S]> {
416        match self {
417            InputOnly(i) | InputOutput(i, _) => Some(i),
418            OutputOnly(_) => None,
419            InPlace(io) => Some(io),
420        }
421    }
422
423    /// Attempts to retrieve a read-only reference to the output channel's buffer data,
424    /// if the output channel is present.
425    #[inline]
426    pub fn output(&'a self) -> Option<&'a [S]> {
427        match self {
428            OutputOnly(o) | InputOutput(_, o) | InPlace(o) => Some(o),
429            InputOnly(_) => None,
430        }
431    }
432
433    /// Attempts to retrieve a read-write reference to the output channel's buffer data,
434    /// if the output channel is present.
435    #[inline]
436    pub fn output_mut(&'a mut self) -> Option<&'a mut [S]> {
437        match self {
438            OutputOnly(o) | InputOutput(_, o) | InPlace(o) => Some(o),
439            InputOnly(_) => None,
440        }
441    }
442}