clack-plugin 0.2.0

Low-level, safe Rust wrappers to create audio plugins using the CLAP audio plugin API.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut};
use crate::process::Audio;
use crate::process::audio::pair::ChannelPair::*;
use crate::process::audio::{BufferError, InputPort, OutputPort, SampleType};
use clack_common::process::{AudioPortProcessingInfo, ConstantMask};
use clap_sys::audio_buffer::clap_audio_buffer;
use std::slice::{Iter, IterMut};

/// A pair of Input and Output ports.
///
/// Note that in case of asymmetric port layouts (e.g. side-chain), there may not
/// be an associated output port to an input port, or vice-versa. The same goes for
/// paired channels.
///
/// In those cases, a given pair will only contain one port instead of two. However,
/// a [`PortPair`] is always guaranteed to contain at least one port, be it an input or output.
pub struct PortPair<'a> {
    input: Option<&'a clap_audio_buffer>,
    output: Option<&'a mut clap_audio_buffer>,
    frames_count: u32,
}

impl<'a> PortPair<'a> {
    /// # Safety
    ///
    /// * Both provided buffers must be valid (if not `None`);
    /// * `frames_count` *must* match the size of the buffers.
    #[inline]
    pub(crate) unsafe fn from_raw(
        input: Option<&'a clap_audio_buffer>,
        output: Option<&'a mut clap_audio_buffer>,
        frames_count: u32,
    ) -> Option<Self> {
        match (input, output) {
            (None, None) => None,
            (input, output) => Some(PortPair {
                input,
                output,
                frames_count,
            }),
        }
    }

    /// Gets the [`InputPort`] of this pair.
    ///
    /// If the port layout is asymmetric and there is no input port, this returns [`None`].
    #[inline]
    pub fn input(&self) -> Option<InputPort<'_>> {
        self.input
            // SAFETY: this type ensures the buffer is valid and matches frame_count
            .map(|i| unsafe { InputPort::from_raw(i, self.frames_count) })
    }

    /// Gets the [`OutputPort`] of this pair.
    ///
    /// If the port layout is asymmetric and there is no output port, this returns [`None`].
    #[inline]
    pub fn output(&mut self) -> Option<OutputPort<'_>> {
        self.output
            .as_mut()
            // SAFETY: this type ensures the buffer is valid and matches frame_count
            .map(|i| unsafe { OutputPort::from_raw(i, self.frames_count) })
    }

    /// Retrieves the port info for the input of this pair.
    ///
    /// If the port layout is asymmetric and there is no input port, this returns [`None`].
    #[inline]
    pub fn input_info(&self) -> Option<AudioPortProcessingInfo> {
        self.input
            .as_ref()
            .map(|buf| AudioPortProcessingInfo::from_raw(buf))
    }

    /// Retrieves the port info for the output of this pair.
    ///
    /// If the port layout is asymmetric and there is no output port, this returns [`None`].
    #[inline]
    pub fn output_info(&self) -> Option<AudioPortProcessingInfo> {
        self.output
            .as_ref()
            .map(|buf| AudioPortProcessingInfo::from_raw(buf))
    }

    /// Retrieves the port pair's channels.
    ///
    /// Because each port can hold either [`f32`] or [`f64`] sample data, this method returns a
    /// [`SampleType`] enum of the paired channels, to indicate which one the ports holds.
    ///
    /// # Errors
    ///
    /// This method returns a [`BufferError::InvalidChannelBuffer`] if the host provided neither
    /// [`f32`] nor [`f64`] buffer type, which is invalid per the CLAP specification.
    ///
    /// Additionally, if the two port have different buffer sample types (i.e. one holds [`f32`]
    /// and the other holds [`f64`]), then a [`BufferError::MismatchedBufferPair`] error is returned.
    ///
    /// # Example
    ///
    /// ```
    /// use clack_plugin::process::audio::{PairedChannels, PortPair, SampleType};
    ///
    /// # fn foo(port: PortPair) {
    /// let mut port: PortPair = /* ... */
    /// # port;
    ///
    /// // Decide what to do using by matching against every possible configuration
    /// match port.channels().unwrap() {
    ///     SampleType::F32(buf) => { /* Process the 32-bit buffers */ },
    ///     SampleType::F64(buf) => { /* Process the 64-bit buffers */ },
    ///     SampleType::Both(buf32, buf64) => { /* We have both types of buffers available */ }
    /// }
    ///
    /// // If we're only interested in a single buffer type,
    /// // we can use SampleType's helper methods:
    /// let channels: PairedChannels<f32> = port.channels().unwrap().into_f32().unwrap();
    /// # }
    /// ```
    #[inline]
    pub fn channels(
        &mut self,
    ) -> Result<SampleType<PairedChannels<'a, f32>, PairedChannels<'a, f64>>, BufferError> {
        let input = match self.input {
            None => SampleType::Both([].as_slice(), [].as_slice()),
            // SAFETY: this type ensures the buffer is valid
            Some(buffer) => unsafe { SampleType::from_raw_buffer(buffer)? },
        };

        let output = match self.output.as_mut() {
            None => SampleType::Both([].as_mut_slice(), [].as_mut_slice()),
            // SAFETY: this type ensures the buffer is valid
            Some(buffer) => unsafe { SampleType::from_raw_buffer_mut(buffer)? },
        };

        Ok(input.try_match_with(output)?.map(
            |(i, o)| PairedChannels {
                input_data: i,
                output_data: o,
                frames_count: self.frames_count,
            },
            |(i, o)| PairedChannels {
                input_data: i,
                output_data: o,
                frames_count: self.frames_count,
            },
        ))
    }

    /// The number of channels in this port pair.
    ///
    /// Since there may be more channels in one port than in the other, this method also counts
    /// the partial [`ChannelPair`]s that can be returned, and therefore returns the maximum number
    /// of channels between the two ports.  
    #[inline]
    pub fn channel_pair_count(&self) -> usize {
        let in_channels = self.input.map(|b| b.channel_count).unwrap_or(0);
        let out_channels = self.output.as_ref().map(|b| b.channel_count).unwrap_or(0);

        in_channels.max(out_channels) as usize
    }

    /// Returns the number of frames to process in this block.
    ///
    /// This will always match the number of samples of every audio channel buffer. The two ports
    /// *cannot* have different buffer sizes.
    #[inline]
    pub fn frames_count(&self) -> u32 {
        self.frames_count
    }

    /// The latency from and to the audio interface for this port pair, in samples.
    ///
    /// This returns a tuple containing the latenciess for the input and output port,
    /// respectively.
    ///
    /// If one port isn't present in this pair, then [`None`] is returned.
    #[inline]
    pub fn latencies(&self) -> (Option<u32>, Option<u32>) {
        (
            self.input.map(|i| i.latency),
            self.output.as_ref().map(|o| o.latency),
        )
    }

    /// The [`ConstantMask`]s for the two ports.
    ///
    /// This returns a tuple containing the [`ConstantMask`]s for the input and output port,
    /// respectively.
    ///
    /// If one port isn't present in this pair, then [`ConstantMask::FULLY_CONSTANT`] is returned.
    #[inline]
    pub fn constant_masks(&self) -> (ConstantMask, ConstantMask) {
        (
            self.input
                .map(|i| ConstantMask::from_bits(i.constant_mask))
                .unwrap_or(ConstantMask::FULLY_CONSTANT),
            self.output
                .as_ref()
                .map(|o| ConstantMask::from_bits(o.constant_mask))
                .unwrap_or(ConstantMask::FULLY_CONSTANT),
        )
    }
}

/// An [`PortPair`]'s channels' data buffers, which contains samples of a given type `S`.
///
/// The sample type `S` is always going to be either [`f32`] or [`f64`], as returned by
/// [`PortPair::channels`].
pub struct PairedChannels<'a, S> {
    input_data: &'a [*mut S],
    output_data: &'a mut [*mut S],
    frames_count: u32,
}

impl<'a, S> PairedChannels<'a, S> {
    /// Returns the number of frames to process in this block.
    ///
    /// This will always match the number of samples of every audio channel buffer, from both
    /// the input and output port.
    #[inline]
    pub fn frames_count(&self) -> u32 {
        self.frames_count
    }

    /// The number of input channels.
    #[inline]
    pub fn input_channel_count(&self) -> usize {
        self.input_data.len()
    }

    /// The number of output channels.
    #[inline]
    pub fn output_channel_count(&self) -> usize {
        self.output_data.len()
    }

    /// The total number of channel pairs.
    ///
    /// Since there may be more channels in one port than in the other, this method also counts
    /// the partial [`ChannelPair`]s that can be returned, and therefore returns the maximum number
    /// of channels between the input and output ports.  
    #[inline]
    pub fn channel_pair_count(&self) -> usize {
        self.input_channel_count().max(self.output_channel_count())
    }

    /// Retrieves the pair of sample buffers for the pair of channels at a given index.
    ///
    /// If there is no channel at the given index (i.e. `channel_index` is greater or equal than
    /// [`channel_pair_count`](Self::channel_pair_count)), this returns [`None`].
    ///
    /// See [`ChannelPair`]'s documentation for examples on how to access sample buffers from it.
    #[inline]
    pub fn channel_pair(&mut self, index: usize) -> Option<ChannelPair<'a, S>> {
        let input = self
            .input_data
            .get(index)
            // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
            .map(|ptr| unsafe { slice_from_external_parts(*ptr, self.frames_count as usize) });

        let output = self
            .output_data
            .get(index)
            // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
            .map(|ptr| unsafe { slice_from_external_parts_mut(*ptr, self.frames_count as usize) });

        ChannelPair::from_optional_io(input, output)
    }

    /// Gets an iterator over all the ports' [`ChannelPair`]s.
    #[inline]
    pub fn iter_mut(&mut self) -> PairedChannelsIter<'_, S> {
        PairedChannelsIter {
            input_iter: self.input_data.iter(),
            output_iter: self.output_data.iter_mut(),
            frames_count: self.frames_count,
        }
    }
}

impl<'a, S> IntoIterator for PairedChannels<'a, S> {
    type Item = ChannelPair<'a, S>;
    type IntoIter = PairedChannelsIter<'a, S>;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        PairedChannelsIter {
            input_iter: self.input_data.iter(),
            output_iter: self.output_data.iter_mut(),
            frames_count: self.frames_count,
        }
    }
}

/// An iterator over all of a [`PortPair`]'s [`ChannelPair`]s.
pub struct PairedChannelsIter<'a, S> {
    input_iter: Iter<'a, *mut S>,
    output_iter: IterMut<'a, *mut S>,
    frames_count: u32,
}

impl<'a, S> Iterator for PairedChannelsIter<'a, S> {
    type Item = ChannelPair<'a, S>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        let input = self
            .input_iter
            .next()
            // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
            .map(|ptr| unsafe { slice_from_external_parts(*ptr, self.frames_count as usize) });

        // SAFETY: this type ensures the pointer is valid and the slice is frames_count-long
        let output = self.output_iter.next().map(|ptr| unsafe {
            slice_from_external_parts_mut((*ptr) as *mut _, self.frames_count as usize)
        });

        ChannelPair::from_optional_io(input, output)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len(), Some(self.len()))
    }
}

impl<S> ExactSizeIterator for PairedChannelsIter<'_, S> {
    #[inline]
    fn len(&self) -> usize {
        self.input_iter.len().max(self.output_iter.len())
    }
}

/// An iterator of all of the available [`PortPair`]s from an [`Audio`] struct.
pub struct PortPairsIter<'a> {
    inputs: Iter<'a, clap_audio_buffer>,
    outputs: IterMut<'a, clap_audio_buffer>,
    frames_count: u32,
}

impl<'a> PortPairsIter<'a> {
    #[inline]
    pub(crate) fn new(audio: &'a mut Audio<'_>) -> Self {
        Self {
            inputs: audio.inputs.iter(),
            outputs: audio.outputs.iter_mut(),
            frames_count: audio.frames_count,
        }
    }
}

impl<'a> Iterator for PortPairsIter<'a> {
    type Item = PortPair<'a>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        // SAFETY: the audio type this is created from
        unsafe { PortPair::from_raw(self.inputs.next(), self.outputs.next(), self.frames_count) }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len(), Some(self.len()))
    }
}

impl ExactSizeIterator for PortPairsIter<'_> {
    #[inline]
    fn len(&self) -> usize {
        self.inputs.len().max(self.outputs.len())
    }
}

/// A pair of input and output channel buffers.
///
/// Because port and channel layouts may differ between inputs and outputs, a [`ChannelPair`]
/// may actually contain only an input or an output instead of both.
///
/// This enum also allows to check for the fairly common case where Host may re-use the same
/// buffer for both the input and output, and expect the plugin to do in-place processing.
pub enum ChannelPair<'a, S> {
    /// There is only an input channel present, there was no matching output.
    InputOnly(&'a [S]),
    /// There is only an output channel present, there was no matching input.
    OutputOnly(&'a mut [S]),
    /// Both the input and output channels are present, and available separately.
    InputOutput(&'a [S], &'a mut [S]),
    /// Both the input and output channels are present, but they actually share the same buffer.
    ///
    /// In this case, the slice is already filled with the input channel's data, and the host
    /// considers the contents of this buffer after processing to be the output channel's data.
    InPlace(&'a mut [S]),
}

impl<'a, S> ChannelPair<'a, S> {
    #[inline]
    pub(crate) fn from_optional_io(
        input: Option<&'a [S]>,
        output: Option<&'a mut [S]>,
    ) -> Option<ChannelPair<'a, S>> {
        match (input, output) {
            (None, None) => None,
            (Some(input), None) => Some(InputOnly(input)),
            (None, Some(output)) => Some(OutputOnly(output)),
            (Some(input), Some(output)) => Some(if input.as_ptr() == output.as_ptr() {
                InPlace(output)
            } else {
                InputOutput(input, output)
            }),
        }
    }

    /// Attempts to retrieve the input channel's buffer data, if the input channel is present.
    #[inline]
    pub fn input(&'a self) -> Option<&'a [S]> {
        match self {
            InputOnly(i) | InputOutput(i, _) => Some(i),
            OutputOnly(_) => None,
            InPlace(io) => Some(io),
        }
    }

    /// Attempts to retrieve a read-only reference to the output channel's buffer data,
    /// if the output channel is present.
    #[inline]
    pub fn output(&'a self) -> Option<&'a [S]> {
        match self {
            OutputOnly(o) | InputOutput(_, o) | InPlace(o) => Some(o),
            InputOnly(_) => None,
        }
    }

    /// Attempts to retrieve a read-write reference to the output channel's buffer data,
    /// if the output channel is present.
    #[inline]
    pub fn output_mut(&'a mut self) -> Option<&'a mut [S]> {
        match self {
            OutputOnly(o) | InputOutput(_, o) | InPlace(o) => Some(o),
            InputOnly(_) => None,
        }
    }
}