Skip to main content

clack_host/process/
audio_buffers.rs

1//! Types to manipulate input and output audio buffers for processing.
2
3use crate::util::check_collection_clap_size_overflow;
4use clack_common::process::AudioPortProcessingInfo;
5use clap_sys::audio_buffer::clap_audio_buffer;
6use core::array::IntoIter;
7
8pub struct InputChannel<'a, T> {
9    pub buffer: &'a mut [T],
10    pub is_constant: bool,
11}
12
13impl<'a, T> InputChannel<'a, T> {
14    #[inline]
15    pub fn from_buffer<D: ?Sized + AsMut<[T]> + 'a>(buffer: &'a mut D, is_constant: bool) -> Self {
16        Self {
17            buffer: buffer.as_mut(),
18            is_constant,
19        }
20    }
21
22    #[inline]
23    pub fn variable<D: ?Sized + AsMut<[T]> + 'a>(buffer: &'a mut D) -> Self {
24        Self::from_buffer(buffer, false)
25    }
26
27    #[inline]
28    pub fn constant<D: ?Sized + AsMut<[T]> + 'a>(buffer: &'a mut D) -> Self {
29        Self::from_buffer(buffer, true)
30    }
31}
32
33pub enum AudioPortBufferType<I32, I64> {
34    F32(I32),
35    F64(I64),
36}
37
38impl<I32> AudioPortBufferType<I32, IntoIter<InputChannel<'static, f64>, 0>> {
39    #[inline]
40    pub fn f32_input_only(iterator: I32) -> Self {
41        Self::F32(iterator)
42    }
43}
44
45impl<I32> AudioPortBufferType<I32, IntoIter<&'static mut [f64], 0>> {
46    #[inline]
47    pub fn f32_output_only(iterator: I32) -> Self {
48        Self::F32(iterator)
49    }
50}
51
52impl<I64> AudioPortBufferType<IntoIter<InputChannel<'static, f32>, 0>, I64> {
53    #[inline]
54    pub fn f64_input_only(iterator: I64) -> Self {
55        Self::F64(iterator)
56    }
57}
58
59impl<I64> AudioPortBufferType<IntoIter<&'static mut [f32], 0>, I64> {
60    #[inline]
61    pub fn f64_output_only(iterator: I64) -> Self {
62        Self::F64(iterator)
63    }
64}
65
66pub struct AudioPortBuffer<I32, I64> {
67    pub channels: AudioPortBufferType<I32, I64>,
68    pub latency: u32,
69}
70
71// bikeshed
72pub struct AudioPorts {
73    buffer_lists: Vec<*mut f32>, // Can be f32 or f64, cast on-demand
74    buffer_configs: Vec<clap_audio_buffer>,
75}
76
77// SAFETY: The pointers are only temporary storage, they are not used unless AudioPorts is exclusively borrowed
78unsafe impl Send for AudioPorts {}
79// SAFETY: The pointers are only temporary storage, they are not used unless AudioPorts is exclusively borrowed
80unsafe impl Sync for AudioPorts {}
81
82impl AudioPorts {
83    #[cfg(feature = "clack-plugin")]
84    pub fn from_plugin_audio_mut<'a>(
85        audio: &'a mut clack_plugin::prelude::Audio,
86    ) -> (InputAudioBuffers<'a>, OutputAudioBuffers<'a>) {
87        let frames_count = audio.frames_count();
88        let (ins, outs) = audio.raw_buffers();
89
90        // SAFETY: the validity of the buffers is guaranteed by the Audio type
91        unsafe {
92            (
93                InputAudioBuffers::from_raw_buffers(ins, frames_count),
94                OutputAudioBuffers::from_raw_buffers(outs, frames_count),
95            )
96        }
97    }
98
99    #[cfg(feature = "clack-plugin")]
100    pub fn from_plugin_audio(
101        audio: clack_plugin::prelude::Audio,
102    ) -> (InputAudioBuffers, OutputAudioBuffers) {
103        let frames_count = audio.frames_count();
104        let (ins, outs) = audio.to_raw_buffers();
105
106        // SAFETY: the validity of the buffers is guaranteed by the Audio type
107        unsafe {
108            (
109                InputAudioBuffers::from_raw_buffers(ins, frames_count),
110                OutputAudioBuffers::from_raw_buffers(outs, frames_count),
111            )
112        }
113    }
114
115    pub fn with_capacity(total_channel_count: usize, port_count: usize) -> Self {
116        let mut bufs = Self {
117            buffer_configs: Vec::with_capacity(port_count),
118            buffer_lists: Vec::with_capacity(total_channel_count),
119        };
120        bufs.resize_buffer_configs(port_count);
121
122        bufs
123    }
124
125    #[inline]
126    pub fn port_capacity(&self) -> usize {
127        self.buffer_configs.len()
128    }
129
130    fn resize_buffer_configs(&mut self, new_size: usize) {
131        if new_size > self.buffer_configs.len() {
132            self.buffer_configs.resize(
133                new_size,
134                clap_audio_buffer {
135                    data32: core::ptr::null_mut(),
136                    data64: core::ptr::null_mut(),
137                    channel_count: 0,
138                    latency: 0,
139                    constant_mask: 0,
140                },
141            );
142        }
143    }
144
145    pub fn with_input_buffers<'a, I, Iter, ChannelIter32, ChannelIter64>(
146        &'a mut self,
147        iter: I,
148    ) -> InputAudioBuffers<'a>
149    where
150        I: IntoIterator<Item = AudioPortBuffer<ChannelIter32, ChannelIter64>, IntoIter = Iter>,
151        Iter: ExactSizeIterator<Item = AudioPortBuffer<ChannelIter32, ChannelIter64>>,
152        ChannelIter32: IntoIterator<Item = InputChannel<'a, f32>>,
153        ChannelIter64: IntoIterator<Item = InputChannel<'a, f64>>,
154    {
155        let iter = iter.into_iter();
156        self.resize_buffer_configs(iter.len());
157        self.buffer_lists.clear();
158
159        let mut min_channel_buffer_length = u32::MAX;
160        let mut total = 0;
161        let mut has_reallocated = false;
162
163        for (i, port) in iter.enumerate() {
164            total = i + 1;
165
166            let last = self.buffer_lists.len();
167
168            let mut constant_mask = 0u64;
169            let is_f64 = match port.channels {
170                AudioPortBufferType::F32(channels) => {
171                    for channel in channels {
172                        min_channel_buffer_length = min_channel_buffer_length
173                            .min(saturate_usize_to_clap_size(channel.buffer.len()));
174                        if channel.is_constant {
175                            constant_mask |= 1 << i as u64
176                        }
177
178                        check_collection_clap_size_overflow(&self.buffer_lists);
179
180                        if self.buffer_lists.len() >= self.buffer_lists.capacity() {
181                            has_reallocated = true;
182                        }
183
184                        self.buffer_lists.push(channel.buffer.as_mut_ptr().cast())
185                    }
186                    false
187                }
188                AudioPortBufferType::F64(channels) => {
189                    for channel in channels {
190                        min_channel_buffer_length = min_channel_buffer_length
191                            .min(saturate_usize_to_clap_size(channel.buffer.len()));
192                        if channel.is_constant {
193                            constant_mask |= 1 << i as u64
194                        }
195
196                        check_collection_clap_size_overflow(&self.buffer_lists);
197
198                        if self.buffer_lists.len() >= self.buffer_lists.capacity() {
199                            has_reallocated = true;
200                        }
201
202                        self.buffer_lists.push(channel.buffer.as_mut_ptr().cast())
203                    }
204                    true
205                }
206            };
207
208            let buffers = self.buffer_lists.get_mut(last..).unwrap_or(&mut []);
209
210            // PANIC: this can only panic with an invalid implementation of ExactSizeIterator
211            let descriptor = &mut self.buffer_configs[i];
212            #[allow(clippy::cast_possible_truncation)]
213            // We already checked above that it cannot be overflowed
214            {
215                descriptor.channel_count = buffers.len() as u32;
216            }
217            descriptor.latency = port.latency;
218            descriptor.constant_mask = constant_mask;
219
220            if is_f64 {
221                descriptor.data64 = buffers.as_mut_ptr().cast();
222                descriptor.data32 = core::ptr::null_mut();
223            } else {
224                descriptor.data64 = core::ptr::null_mut();
225                descriptor.data32 = buffers.as_mut_ptr();
226            }
227        }
228
229        // If a realloc occurred, we must rewrite all the pointers.
230        // Thankfully, we know we wrote them sequentially, and we stored the lengths, so it's easy
231        // to find them back.
232        if has_reallocated {
233            let mut last_len = 0;
234            for descriptor in &mut self.buffer_configs[..total] {
235                let channel_count = descriptor.channel_count as usize;
236                let buffers = self
237                    .buffer_lists
238                    .get_mut(last_len..channel_count)
239                    .unwrap_or(&mut []);
240                last_len += channel_count;
241
242                if descriptor.data32.is_null() {
243                    descriptor.data64 = buffers.as_mut_ptr().cast();
244                } else {
245                    descriptor.data32 = buffers.as_mut_ptr();
246                }
247            }
248        }
249
250        InputAudioBuffers {
251            buffers: &self.buffer_configs[..total],
252            frames_count: if min_channel_buffer_length == u32::MAX {
253                None
254            } else {
255                Some(min_channel_buffer_length)
256            },
257        }
258    }
259
260    pub fn with_output_buffers<'a, I, Iter, ChannelIter32, ChannelIter64>(
261        &'a mut self,
262        iter: I,
263    ) -> OutputAudioBuffers<'a>
264    where
265        I: IntoIterator<Item = AudioPortBuffer<ChannelIter32, ChannelIter64>, IntoIter = Iter>,
266        Iter: ExactSizeIterator<Item = AudioPortBuffer<ChannelIter32, ChannelIter64>>,
267        ChannelIter32: IntoIterator<Item = &'a mut [f32]>,
268        ChannelIter64: IntoIterator<Item = &'a mut [f64]>,
269    {
270        let iter = iter.into_iter();
271        self.resize_buffer_configs(iter.len());
272        self.buffer_lists.clear();
273
274        let mut min_channel_buffer_length = u32::MAX;
275        let mut total = 0;
276        let mut has_reallocated = false;
277
278        for (i, port) in iter.enumerate() {
279            total = i + 1;
280
281            let last = self.buffer_lists.len();
282
283            let is_f64 = match port.channels {
284                AudioPortBufferType::F32(channels) => {
285                    for channel in channels {
286                        min_channel_buffer_length = min_channel_buffer_length
287                            .min(saturate_usize_to_clap_size(channel.len()));
288
289                        check_collection_clap_size_overflow(&self.buffer_lists);
290
291                        if self.buffer_lists.len() >= self.buffer_lists.capacity() {
292                            has_reallocated = true;
293                        }
294
295                        self.buffer_lists.push(channel.as_mut_ptr().cast())
296                    }
297                    false
298                }
299                AudioPortBufferType::F64(channels) => {
300                    for channel in channels {
301                        min_channel_buffer_length = min_channel_buffer_length
302                            .min(saturate_usize_to_clap_size(channel.len()));
303
304                        check_collection_clap_size_overflow(&self.buffer_lists);
305
306                        if self.buffer_lists.len() >= self.buffer_lists.capacity() {
307                            has_reallocated = true;
308                        }
309
310                        self.buffer_lists.push(channel.as_mut_ptr().cast())
311                    }
312                    true
313                }
314            };
315
316            let buffers = self.buffer_lists.get_mut(last..).unwrap_or(&mut []);
317
318            // PANIC: this can only panic with an invalid implementation of ExactSizeIterator
319            let descriptor = &mut self.buffer_configs[i];
320            #[allow(clippy::cast_possible_truncation)]
321            // We already checked above that it cannot be overflowed
322            {
323                descriptor.channel_count = buffers.len() as u32;
324            }
325            descriptor.latency = port.latency;
326            descriptor.constant_mask = 0;
327
328            if is_f64 {
329                descriptor.data64 = buffers.as_mut_ptr().cast();
330                descriptor.data32 = core::ptr::null_mut();
331            } else {
332                descriptor.data64 = core::ptr::null_mut();
333                descriptor.data32 = buffers.as_mut_ptr();
334            }
335        }
336
337        // If a realloc occurred, we must rewrite all the pointers.
338        // Thankfully, we know we wrote them sequentially, and we stored the lengths, so it's easy
339        // to find them back.
340        if has_reallocated {
341            let mut last_len = 0;
342            for descriptor in &mut self.buffer_configs[..total] {
343                let channel_count = descriptor.channel_count as usize;
344                let buffers = self
345                    .buffer_lists
346                    .get_mut(last_len..channel_count)
347                    .unwrap_or(&mut []);
348                last_len += channel_count;
349
350                if descriptor.data32.is_null() {
351                    descriptor.data64 = buffers.as_mut_ptr().cast();
352                } else {
353                    descriptor.data32 = buffers.as_mut_ptr();
354                }
355            }
356        }
357
358        OutputAudioBuffers {
359            buffers: &mut self.buffer_configs[..total],
360            frames_count: if min_channel_buffer_length == u32::MAX {
361                None
362            } else {
363                Some(min_channel_buffer_length)
364            },
365        }
366    }
367
368    #[inline]
369    pub fn port_count(&self) -> usize {
370        self.buffer_configs.len()
371    }
372}
373
374#[inline]
375const fn saturate_usize_to_clap_size(value: usize) -> u32 {
376    if value > u32::MAX as usize {
377        u32::MAX - 1
378    } else {
379        #[allow(clippy::cast_possible_truncation)] // We just checked it above, it cannot overflow
380        {
381            value as u32
382        }
383    }
384}
385
386pub struct InputAudioBuffers<'a> {
387    buffers: &'a [clap_audio_buffer],
388    frames_count: Option<u32>,
389}
390
391impl<'a> InputAudioBuffers<'a> {
392    #[inline]
393    pub const fn empty() -> Self {
394        Self {
395            buffers: &[],
396            frames_count: None,
397        }
398    }
399
400    /// Shortens the [`frames_count`] of these input buffers.
401    ///
402    /// This does not actually change the underlying buffers themselves, it only reduces the
403    /// slice that will be exposed to the plugin.
404    ///
405    /// This method does nothing if `max_buffer_size` is greater or equal than the current [`frames_count`].
406    ///
407    /// [`frames_count`]: self.frames_count
408    pub fn truncate(&mut self, max_buffer_size: u32) {
409        if let Some(frames_count) = self.frames_count {
410            self.frames_count = Some(frames_count.min(max_buffer_size))
411        }
412    }
413
414    /// # Safety
415    ///
416    /// The caller must ensure all buffer structs are valid for 'a, including all the buffer
417    /// pointers they contain.
418    ///
419    /// The caller must also ensure `frames_count` is lower than or equal to the sizes of the
420    /// channel buffers pointed to by `buffers`.
421    #[inline]
422    pub unsafe fn from_raw_buffers(buffers: &'a [clap_audio_buffer], frames_count: u32) -> Self {
423        check_collection_clap_size_overflow(buffers);
424
425        Self {
426            buffers,
427            frames_count: Some(frames_count),
428        }
429    }
430
431    #[cfg(feature = "clack-plugin")]
432    pub fn from_plugin_audio(audio: &clack_plugin::prelude::Audio<'a>) -> InputAudioBuffers<'a> {
433        let frames_count = audio.frames_count();
434        let ins = audio.raw_input_buffers();
435
436        // SAFETY: the validity of the buffers is guaranteed by the Audio type
437        unsafe { InputAudioBuffers::from_raw_buffers(ins, frames_count) }
438    }
439
440    #[cfg(feature = "clack-plugin")]
441    pub fn as_plugin_audio(&self) -> clack_plugin::prelude::Audio<'a> {
442        // SAFETY: the validity of the buffers is guaranteed by this type
443        unsafe {
444            clack_plugin::prelude::Audio::from_raw_buffers(
445                self.buffers,
446                &mut [],
447                self.frames_count.unwrap_or(0),
448            )
449        }
450    }
451
452    #[inline]
453    pub fn as_raw_buffers(&self) -> &'a [clap_audio_buffer] {
454        self.buffers
455    }
456
457    /// The number of port buffers this [`InputAudioBuffers`] has been given.
458    #[inline]
459    pub fn port_count(&self) -> u32 {
460        #[allow(clippy::cast_possible_truncation)]
461        // We checked above that buffers' len cannot overflow u32
462        {
463            self.buffers.len() as u32
464        }
465    }
466
467    /// The number of frames in these input buffers.
468    ///
469    /// This is the minimum frame count of all the port buffers this has been given.
470    ///
471    /// If this has no port buffer (i.e. [`port_count`](self.port_count) is zero), this returns `None`.
472    #[inline]
473    pub fn frames_count(&self) -> Option<u32> {
474        self.frames_count
475    }
476
477    #[inline]
478    pub fn port_info(&self, port_index: u32) -> Option<AudioPortProcessingInfo> {
479        self.buffers
480            .get(port_index as usize)
481            .map(AudioPortProcessingInfo::from_raw)
482    }
483
484    #[inline]
485    pub fn port_infos(&self) -> impl Iterator<Item = AudioPortProcessingInfo> + '_ {
486        self.buffers.iter().map(AudioPortProcessingInfo::from_raw)
487    }
488
489    /// Returns the minimum number of frames available both in this [`InputAudioBuffers`] and
490    /// the given [`OutputAudioBuffers`].
491    ///
492    /// This is useful to ensure a safe frame count for a `process` batch that would receive those
493    /// input and output audio buffers.
494    pub fn min_available_frames_with(&self, outputs: &OutputAudioBuffers) -> u32 {
495        match (self.frames_count, outputs.frames_count) {
496            (Some(a), Some(b)) => a.min(b),
497            (Some(a), None) | (None, Some(a)) => a,
498            (None, None) => 0,
499        }
500    }
501}
502
503pub struct OutputAudioBuffers<'a> {
504    buffers: &'a mut [clap_audio_buffer],
505    frames_count: Option<u32>,
506}
507
508impl<'a> OutputAudioBuffers<'a> {
509    #[inline]
510    pub fn empty() -> Self {
511        Self {
512            buffers: &mut [],
513            frames_count: None,
514        }
515    }
516
517    /// Shortens the [`frames_count`] of these output buffers.
518    ///
519    /// This does not actually change the underlying buffers themselves, it only reduces the
520    /// slice that will be exposed to the plugin.
521    ///
522    /// This method does nothing if `max_buffer_size` is greater or equal than the current [`frames_count`].
523    ///
524    /// [`frames_count`]: self.frames_count
525    pub fn truncate(&mut self, max_buffer_size: u32) {
526        if let Some(frames_count) = self.frames_count {
527            self.frames_count = Some(frames_count.min(max_buffer_size))
528        }
529    }
530
531    /// # Safety
532    ///
533    /// The caller must ensure all buffer structs are valid for 'a, including all the buffer
534    /// pointers they contain.
535    ///
536    /// The caller must also ensure `frames_count` is lower than or equal to the sizes of the
537    /// channel buffers pointed to by `buffers`.
538    #[inline]
539    pub unsafe fn from_raw_buffers(
540        buffers: &'a mut [clap_audio_buffer],
541        frames_count: u32,
542    ) -> Self {
543        check_collection_clap_size_overflow(buffers);
544
545        Self {
546            buffers,
547            frames_count: Some(frames_count),
548        }
549    }
550
551    #[cfg(feature = "clack-plugin")]
552    pub fn from_plugin_audio(audio: clack_plugin::prelude::Audio<'a>) -> OutputAudioBuffers<'a> {
553        let frames_count = audio.frames_count();
554        let outs = audio.to_raw_output_buffers();
555
556        // SAFETY: the validity of the buffers is guaranteed by the Audio type
557        unsafe { OutputAudioBuffers::from_raw_buffers(outs, frames_count) }
558    }
559
560    #[cfg(feature = "clack-plugin")]
561    pub fn from_plugin_audio_mut(
562        audio: &'a mut clack_plugin::prelude::Audio,
563    ) -> OutputAudioBuffers<'a> {
564        let frames_count = audio.frames_count();
565        let outs = audio.raw_output_buffers();
566
567        // SAFETY: the validity of the buffers is guaranteed by the Audio type
568        unsafe { OutputAudioBuffers::from_raw_buffers(outs, frames_count) }
569    }
570
571    #[cfg(feature = "clack-plugin")]
572    pub fn as_plugin_audio(&'a mut self) -> clack_plugin::prelude::Audio<'a> {
573        // SAFETY: the validity of the buffers is guaranteed by this type
574        unsafe {
575            clack_plugin::prelude::Audio::from_raw_buffers(
576                &[],
577                self.buffers,
578                self.frames_count.unwrap_or(0),
579            )
580        }
581    }
582
583    #[cfg(feature = "clack-plugin")]
584    pub fn to_plugin_audio(self) -> clack_plugin::prelude::Audio<'a> {
585        // SAFETY: the validity of the buffers is guaranteed by this type
586        unsafe {
587            clack_plugin::prelude::Audio::from_raw_buffers(
588                &[],
589                self.buffers,
590                self.frames_count.unwrap_or(0),
591            )
592        }
593    }
594
595    #[cfg(feature = "clack-plugin")]
596    pub fn as_plugin_audio_with_inputs(
597        &'a mut self,
598        inputs: &InputAudioBuffers<'a>,
599    ) -> clack_plugin::prelude::Audio<'a> {
600        let frames_count = inputs.min_available_frames_with(self);
601
602        // SAFETY: the validity of the buffers is guaranteed by this type
603        unsafe {
604            clack_plugin::prelude::Audio::from_raw_buffers(
605                inputs.buffers,
606                self.buffers,
607                frames_count,
608            )
609        }
610    }
611
612    #[cfg(feature = "clack-plugin")]
613    pub fn to_plugin_audio_with_inputs(
614        self,
615        inputs: &InputAudioBuffers<'a>,
616    ) -> clack_plugin::prelude::Audio<'a> {
617        let frames_count = inputs.min_available_frames_with(&self);
618
619        // SAFETY: the validity of the buffers is guaranteed by this type
620        unsafe {
621            clack_plugin::prelude::Audio::from_raw_buffers(
622                inputs.buffers,
623                self.buffers,
624                frames_count,
625            )
626        }
627    }
628
629    #[inline]
630    pub fn as_raw_buffers(&mut self) -> &mut [clap_audio_buffer] {
631        self.buffers
632    }
633
634    #[inline]
635    pub fn into_raw_buffers(self) -> &'a mut [clap_audio_buffer] {
636        self.buffers
637    }
638
639    /// The number of port buffers this [`OutputAudioBuffers`] has been given.
640    #[inline]
641    pub fn port_count(&self) -> u32 {
642        #[allow(clippy::cast_possible_truncation)]
643        // We checked above that buffers' len cannot overflow u32
644        {
645            self.buffers.len() as u32
646        }
647    }
648
649    /// The number of frames in these output buffers.
650    ///
651    /// This is the minimum frame count of all the port buffers this has been given.
652    ///
653    /// If this has no port buffer (i.e. [`port_count`](self.port_count) is zero), this returns `None`.
654    #[inline]
655    pub fn frames_count(&self) -> Option<u32> {
656        self.frames_count
657    }
658
659    #[inline]
660    pub fn port_info(&self, port_index: u32) -> Option<AudioPortProcessingInfo> {
661        self.buffers
662            .get(port_index as usize)
663            .map(AudioPortProcessingInfo::from_raw)
664    }
665
666    #[inline]
667    pub fn port_infos(&self) -> impl Iterator<Item = AudioPortProcessingInfo> + '_ {
668        self.buffers.iter().map(AudioPortProcessingInfo::from_raw)
669    }
670}
671
672#[cfg(test)]
673mod test {
674    use super::*;
675    use clack_plugin::prelude::*;
676    use clap_sys::process::clap_process;
677    use std::cell::RefCell;
678    use std::ptr::null_mut;
679
680    #[test]
681    pub fn input_audio_buffers_work() {
682        let mut ports = AudioPorts::with_capacity(2, 1);
683        let mut bufs = [[0f32; 4]; 2];
684
685        let buffers = ports.with_input_buffers([AudioPortBuffer {
686            latency: 0,
687            channels: AudioPortBufferType::f32_input_only(bufs.iter_mut().map(|b| InputChannel {
688                buffer: b.as_mut_slice(),
689                is_constant: false,
690            })),
691        }]);
692
693        assert_eq!(buffers.buffers.len(), 1);
694        assert_eq!(buffers.frames_count, Some(4));
695    }
696
697    #[test]
698    pub fn output_audio_buffers_work() {
699        let mut ports = AudioPorts::with_capacity(2, 1);
700        let mut bufs = [[0f32; 4]; 2];
701
702        let buffers = ports.with_output_buffers([AudioPortBuffer {
703            latency: 0,
704            channels: AudioPortBufferType::f32_output_only(
705                bufs.iter_mut().map(|b| b.as_mut_slice()),
706            ),
707        }]);
708
709        assert_eq!(buffers.buffers.len(), 1);
710        assert_eq!(buffers.frames_count, Some(4));
711
712        assert_eq!(bufs.len(), 2); // Check borrow still works
713        assert_eq!(ports.port_count(), 1);
714    }
715
716    #[test]
717    pub fn input_audio_buffers_work_with_refcell() {
718        let mut ports = AudioPorts::with_capacity(2, 1);
719        let bufs = [RefCell::new([0f32; 4]), RefCell::new([0f32; 4])];
720        let mut borrowed: Vec<_> = bufs.iter().map(|c| c.borrow_mut()).collect();
721
722        let buffers = ports.with_input_buffers([AudioPortBuffer {
723            latency: 0,
724            channels: AudioPortBufferType::f32_input_only(borrowed.iter_mut().map(|b| {
725                InputChannel {
726                    buffer: b.as_mut_slice(),
727                    is_constant: false,
728                }
729            })),
730        }]);
731
732        assert_eq!(buffers.buffers.len(), 1);
733        assert_eq!(buffers.frames_count, Some(4));
734    }
735
736    #[test]
737    pub fn output_audio_buffers_work_with_refcell() {
738        let mut ports = AudioPorts::with_capacity(2, 1);
739        let bufs = [RefCell::new([0f32; 4]), RefCell::new([0f32; 4])];
740        let mut borrowed: Vec<_> = bufs.iter().map(|c| c.borrow_mut()).collect();
741
742        let buffers = ports.with_output_buffers([AudioPortBuffer {
743            latency: 0,
744            channels: AudioPortBufferType::f32_output_only(
745                borrowed.iter_mut().map(|b| b.as_mut_slice()),
746            ),
747        }]);
748
749        assert_eq!(buffers.buffers.len(), 1);
750        assert_eq!(buffers.frames_count, Some(4));
751
752        assert_eq!(bufs.len(), 2); // Check borrow still works
753        assert_eq!(ports.port_count(), 1);
754    }
755
756    #[test]
757    pub fn audio_buffers_work_with_wrong_capacity() {
758        let mut input_ports = AudioPorts::with_capacity(1, 1);
759        let mut output_ports = AudioPorts::with_capacity(1, 1);
760        let mut input_bufs = [[[42f32; 4]; 128], [[69f32; 4]; 128]];
761        let mut output_bufs = [[[42f32; 4]; 128], [[69f32; 4]; 128]];
762
763        let input_buffers =
764            input_ports.with_input_buffers(input_bufs.iter_mut().map(|bufs| AudioPortBuffer {
765                latency: 0,
766                channels: AudioPortBufferType::f32_input_only(bufs.iter_mut().map(|b| {
767                    InputChannel {
768                        buffer: b.as_mut_slice(),
769                        is_constant: false,
770                    }
771                })),
772            }));
773
774        let mut output_buffers =
775            output_ports.with_output_buffers(output_bufs.iter_mut().map(|bufs| AudioPortBuffer {
776                latency: 0,
777                channels: AudioPortBufferType::f32_output_only(
778                    bufs.iter_mut().map(|b| b.as_mut_slice()),
779                ),
780            }));
781
782        assert_eq!(input_buffers.buffers.len(), 2);
783        assert_eq!(input_buffers.frames_count, Some(4));
784        assert_eq!(output_buffers.buffers.len(), 2);
785        assert_eq!(output_buffers.frames_count, Some(4));
786
787        let raw_input_buffers = input_buffers.as_raw_buffers();
788        let raw_output_buffers = output_buffers.as_raw_buffers();
789        let process = clap_process {
790            audio_inputs: raw_input_buffers.as_ptr(),
791            audio_outputs: raw_output_buffers.as_ptr() as *mut _,
792            audio_inputs_count: input_buffers.port_count(),
793            audio_outputs_count: output_buffers.port_count(),
794
795            steady_time: 0,
796            frames_count: 4,
797            transport: null_mut(),
798            in_events: null_mut(),
799            out_events: null_mut(),
800        };
801
802        // SAFETY: we built the process struct above, it should be good.
803        let mut audio = unsafe { Audio::from_raw(&process) };
804
805        for (port, bufs) in audio.input_ports().zip(&input_bufs) {
806            let channels = port.channels().unwrap().into_f32().unwrap();
807
808            assert_eq!(channels.channel_count(), 128);
809            for (channel, buf) in channels.iter().zip(bufs.iter()) {
810                assert_eq!(buf, channel)
811            }
812        }
813
814        for (mut port, bufs) in audio.output_ports().zip(&output_bufs) {
815            let channels = port.channels().unwrap().into_f32().unwrap();
816
817            assert_eq!(channels.channel_count(), 128);
818            for (channel, buf) in channels.iter().zip(bufs.iter()) {
819                assert_eq!(buf, channel)
820            }
821        }
822    }
823}