diff --git a/README.md b/README.md index 8786c924..1a816bbe 100644 --- a/README.md +++ b/README.md @@ -108,10 +108,10 @@ impl<'a> PluginAudioProcessor<'a, (), ()> for MyGainPluginAudioProcessor { Ok(Self) } - fn process(&mut self, _process: Process, mut audio: Audio, _events: Events) -> Result { - for mut port_pair in &mut audio { + fn process(&mut self, _process: Process, audio: Audio, _events: Events) -> Result { + for mut port_pair in audio { // For this example, we'll only care about 32-bit sample data. - let Some(channel_pairs) = port_pair.channels()?.into_f32() else { continue; }; + let Some(channel_pairs) = port_pair.channels()?.to_f32() else { continue; }; for channel_pair in channel_pairs { match channel_pair { @@ -119,12 +119,12 @@ impl<'a> PluginAudioProcessor<'a, (), ()> for MyGainPluginAudioProcessor { ChannelPair::OutputOnly(buf) => buf.fill(0.0), ChannelPair::InputOutput(input, output) => { for (input, output) in input.iter().zip(output) { - *output = input * 2.0 + output.set(input.get() * 2.0) } } ChannelPair::InPlace(buf) => { for sample in buf { - *sample *= 2.0 + sample.set(sample.get() * 2.0) } } } diff --git a/common/src/process.rs b/common/src/process.rs index 78a24266..5cccfadc 100644 --- a/common/src/process.rs +++ b/common/src/process.rs @@ -1,5 +1,6 @@ use clap_sys::process::*; use std::fmt::Debug; +use std::ptr::addr_of; mod constant_mask; pub use constant_mask::*; @@ -77,9 +78,15 @@ use clap_sys::audio_buffer::clap_audio_buffer; /// Processing-related information about an audio port. pub struct AudioPortProcessingInfo { - channel_count: u32, - latency: u32, - constant_mask: ConstantMask, + /// The number of audio channels this port provides. + pub channel_count: u32, + /// The latency to or from the audio interface, in samples. + /// + /// Whether this latency is to or from the audio interface depends on which kind of port + /// this describes, an output port or an input port respectively. + pub latency: u32, + /// The [`ConstantMask`] of this port, hinting which audio channels are constant. + pub constant_mask: ConstantMask, } impl AudioPortProcessingInfo { @@ -94,24 +101,22 @@ impl AudioPortProcessingInfo { } } - /// Returns the number of audio channels this port provides. - #[inline] - pub fn channel_count(&self) -> u32 { - self.channel_count - } - - /// Returns the latency to or from the audio interface, in samples. + /// Extracts the processing-related information from a raw, C-FFI compatible audio buffer + /// descriptor. /// - /// Whether this latency is to or from the audio interface depends on which kind of port - /// this describes, an output port or an input port respectively - #[inline] - pub fn latency(&self) -> u32 { - self.latency - } - - /// Returns the [`ConstantMask`] of this port, hinting which audio channels are constant. + /// Unlike [`from_raw`](Self::from_raw), this method does not require any references to perform + /// the read. + /// + /// # Safety + /// + /// The caller must ensure the given pointer is well-aligned, and points to an initialized + /// `clap_audio_buffer` instance that is valid for reads. #[inline] - pub fn constant_mask(&self) -> ConstantMask { - self.constant_mask + pub unsafe fn from_raw_ptr(raw: *const clap_audio_buffer) -> Self { + Self { + channel_count: addr_of!((*raw).channel_count).read(), + latency: addr_of!((*raw).latency).read(), + constant_mask: ConstantMask::from_bits(addr_of!((*raw).constant_mask).read()), + } } } diff --git a/extensions/src/__doc_utils.rs b/extensions/src/__doc_utils.rs index d896c41d..65420344 100644 --- a/extensions/src/__doc_utils.rs +++ b/extensions/src/__doc_utils.rs @@ -90,22 +90,20 @@ mod diva_stub { fn process( &mut self, _process: Process, - mut audio: Audio, + audio: Audio, _events: Events, ) -> Result { self.shared.host.request_callback(); for event in _events.input { - _events.output.try_push(event).unwrap(); + _events.output.try_push(event)?; } - let mut output_channels = audio.output_port(0).unwrap().channels().unwrap(); - let output_buf = output_channels.as_f32_mut().unwrap().iter_mut(); + let output_channels = audio.output_port(0).unwrap().channels()?; + let output_buf = output_channels.to_f32().unwrap(); for channel in output_buf { - for (input, output) in [42.0f32, 69.0, 21.0, 34.5].iter().zip(channel.iter_mut()) { - *output = *input; - } + channel.copy_from_slice(&[42.0f32, 69.0, 21.0, 34.5]); } Ok(ProcessStatus::Sleep) } diff --git a/host/examples/cpal/src/host/audio.rs b/host/examples/cpal/src/host/audio.rs index 6122da8b..c1f738e1 100644 --- a/host/examples/cpal/src/host/audio.rs +++ b/host/examples/cpal/src/host/audio.rs @@ -141,7 +141,7 @@ impl StreamAudioProcessor { self.buffers.ensure_buffer_size_matches(data.len()); let sample_count = self.buffers.cpal_buf_len_to_frame_count(data.len()); - let (ins, mut outs) = self.buffers.prepare_plugin_buffers(data.len()); + let (ins, outs) = self.buffers.prepare_plugin_buffers(data.len()); let events = if let Some(midi) = self.midi_receiver.as_mut() { midi.receive_all_events(sample_count as u64) @@ -151,7 +151,7 @@ impl StreamAudioProcessor { match self.audio_processor.process( &ins, - &mut outs, + &outs, &events, &mut OutputEvents::void(), Some(self.steady_counter), diff --git a/host/examples/cpal/src/host/audio/buffers.rs b/host/examples/cpal/src/host/audio/buffers.rs index 03fed34b..67ef055a 100644 --- a/host/examples/cpal/src/host/audio/buffers.rs +++ b/host/examples/cpal/src/host/audio/buffers.rs @@ -1,7 +1,6 @@ use crate::host::audio::config::FullAudioConfig; use clack_host::prelude::{ - AudioPortBuffer, AudioPortBufferType, AudioPorts, InputAudioBuffers, InputChannel, - OutputAudioBuffers, + AudioBuffers, AudioPortBuffer, AudioPortBufferType, AudioPorts, InputChannel, }; use cpal::{FromSample, Sample}; @@ -116,10 +115,7 @@ impl HostAudioBuffers { } /// Prepares the plugin's input and output buffers. - pub fn prepare_plugin_buffers( - &mut self, - cpal_buf_len: usize, - ) -> (InputAudioBuffers, OutputAudioBuffers) { + pub fn prepare_plugin_buffers(&mut self, cpal_buf_len: usize) -> (AudioBuffers, AudioBuffers) { let sample_count = self.cpal_buf_len_to_frame_count(cpal_buf_len); assert!(sample_count <= self.actual_frame_count); diff --git a/host/src/bundle/diva_stub.rs b/host/src/bundle/diva_stub.rs index 43edb4ac..3d4b1d4f 100644 --- a/host/src/bundle/diva_stub.rs +++ b/host/src/bundle/diva_stub.rs @@ -52,22 +52,20 @@ impl<'a> PluginAudioProcessor<'a, DivaPluginStubShared<'a>, ()> fn process( &mut self, _process: Process, - mut audio: Audio, + audio: Audio, _events: Events, ) -> Result { self.shared.host.request_callback(); for event in _events.input { - _events.output.try_push(event).unwrap(); + _events.output.try_push(event)?; } - let mut output_channels = audio.output_port(0).unwrap().channels().unwrap(); - let output_buf = output_channels.as_f32_mut().unwrap().iter_mut(); + let output_channels = audio.output_port(0).unwrap().channels()?; + let output_buf = output_channels.to_f32().unwrap(); for channel in output_buf { - for (input, output) in [42.0f32, 69.0, 21.0, 34.5].iter().zip(channel.iter_mut()) { - *output = *input; - } + channel.copy_from_slice(&[42.0f32, 69.0, 21.0, 34.5]); } Ok(ProcessStatus::Sleep) } diff --git a/host/src/lib.rs b/host/src/lib.rs index a9dacc11..e7df5c85 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -86,11 +86,10 @@ //! //! Those buffer wrappers are [`InputEvents`](events::io::InputEvents) and //! [`OutputEvents`](events::io::OutputEvents) for events, and -//! [`InputAudioBuffers`](process::audio_buffers::InputAudioBuffers) and -//! [`OutputAudioBuffers`](process::audio_buffers::OutputAudioBuffers) for audio (obtained via a call to -//! [`AudioPorts::with_input_buffers`](process::audio_buffers::AudioPorts::with_input_buffers) and. +//! [`AudioBuffers`](process::audio_buffers::AudioBuffers) for audio (obtained via a call to +//! [`AudioPorts::with_input_buffers`](process::audio_buffers::AudioPorts::with_input_buffers) or //! [`AudioPorts::with_output_buffers`](process::audio_buffers::AudioPorts::with_output_buffers) -//! respectively). +//! ). //! //! See the documentation of those buffer types for more detail on what types they support, as //! well as the [`process`](process::StartedPluginAudioProcessor::process) method's @@ -290,8 +289,7 @@ pub mod prelude { }, process::{ audio_buffers::{ - AudioPortBuffer, AudioPortBufferType, AudioPorts, InputAudioBuffers, InputChannel, - OutputAudioBuffers, + AudioBuffers, AudioPortBuffer, AudioPortBufferType, AudioPorts, InputChannel, }, AudioPortProcessingInfo, PluginAudioConfiguration, ProcessStatus, StoppedPluginAudioProcessor, diff --git a/host/src/process.rs b/host/src/process.rs index d3d0a7c2..f2e79155 100644 --- a/host/src/process.rs +++ b/host/src/process.rs @@ -13,10 +13,9 @@ #![deny(missing_docs)] -use self::audio_buffers::InputAudioBuffers; use crate::host::HostHandlers; use crate::plugin::{PluginAudioProcessorHandle, PluginInstanceError, PluginSharedHandle}; -use crate::prelude::{OutputAudioBuffers, PluginInstance}; +use crate::prelude::{AudioBuffers, PluginInstance}; use crate::process::PluginAudioProcessor::*; use clack_common::events::event_types::TransportEvent; use clack_common::events::io::{InputEvents, OutputEvents}; @@ -417,10 +416,10 @@ impl StartedPluginAudioProcessor { /// Process a chunk of audio frames and events. /// /// This plugin function requires the following arguments: - /// * `audio_inputs`: The [`InputAudioBuffers`] the plugin is going to read audio frames from. - /// Can be [`InputAudioBuffers::empty`] if the plugin takes no audio input at all. - /// * `audio_output`: The [`OutputAudioBuffers`] the plugin is going to read audio frames from. - /// Can be [`OutputAudioBuffers::empty`] if the plugin produces no audio output at all. + /// * `audio_inputs`: The [`AudioBuffers`] the plugin is going to read audio frames from. + /// Can be [`AudioBuffers::empty`] if the plugin takes no audio input at all. + /// * `audio_output`: The [`AudioBuffers`] the plugin is going to read audio frames from. + /// Can be [`AudioBuffers::empty`] if the plugin produces no audio output at all. /// * `input_events`: The [`InputEvents`] list the plugin is going to receive events from. /// Can be [`InputEvents::empty`] if the plugin doesn't need to receive any events. /// * `output_events`: The [`OutputEvents`] buffer the plugin is going to write the events it @@ -435,7 +434,7 @@ impl StartedPluginAudioProcessor { /// other plugin instances may receive. /// /// The only requirement is that this value must be increased by at least the frame count - /// of the audio buffers (see [`InputAudioBuffers::min_available_frames_with`]) for the next + /// of the audio buffers (see [`AudioBuffers::min_available_frames_with`]) for the next /// call to `process`. /// /// This value can never decrease between two calls to `process`, unless [`reset`] @@ -463,8 +462,8 @@ impl StartedPluginAudioProcessor { /// [`reset`]: Self::reset pub fn process( &mut self, - audio_inputs: &InputAudioBuffers, - audio_outputs: &mut OutputAudioBuffers, + audio_inputs: &AudioBuffers, + audio_outputs: &AudioBuffers, input_events: &InputEvents, output_events: &mut OutputEvents, steady_time: Option, @@ -472,19 +471,16 @@ impl StartedPluginAudioProcessor { ) -> Result { let frames_count = audio_inputs.min_available_frames_with(audio_outputs); - let audio_inputs = audio_inputs.as_raw_buffers(); - let audio_outputs = audio_outputs.as_raw_buffers(); - let process = clap_process { frames_count, in_events: input_events.as_raw(), out_events: output_events.as_raw_mut(), - audio_inputs: audio_inputs.as_ptr(), - audio_outputs: audio_outputs.as_mut_ptr(), - audio_inputs_count: audio_inputs.len() as u32, - audio_outputs_count: audio_outputs.len() as u32, + audio_inputs: audio_inputs.as_raw_buffers().cast(), + audio_outputs: audio_outputs.as_raw_buffers().cast(), + audio_inputs_count: audio_inputs.port_count() as u32, + audio_outputs_count: audio_outputs.port_count() as u32, steady_time: match steady_time { None => -1, diff --git a/host/src/process/audio_buffers.rs b/host/src/process/audio_buffers.rs index 71079971..8b6cd837 100644 --- a/host/src/process/audio_buffers.rs +++ b/host/src/process/audio_buffers.rs @@ -3,6 +3,7 @@ use clack_common::process::AudioPortProcessingInfo; use clap_sys::audio_buffer::clap_audio_buffer; use core::array::IntoIter; +use std::cell::Cell; pub struct InputChannel<'a, T> { pub buffer: &'a mut [T], @@ -67,7 +68,7 @@ pub struct AudioPortBuffer { pub latency: u32, } -// bikeshed +// TODO: bikeshed pub struct AudioPorts { buffer_lists: Vec<*mut f32>, // Can be f32 or f64, cast on-demand buffer_configs: Vec, @@ -79,38 +80,6 @@ unsafe impl Send for AudioPorts {} unsafe impl Sync for AudioPorts {} impl AudioPorts { - #[cfg(feature = "clack-plugin")] - pub fn from_plugin_audio_mut<'a>( - audio: &'a mut clack_plugin::prelude::Audio, - ) -> (InputAudioBuffers<'a>, OutputAudioBuffers<'a>) { - let frames_count = audio.frames_count(); - let (ins, outs) = audio.raw_buffers(); - - // SAFETY: the validity of the buffers is guaranteed by the Audio type - unsafe { - ( - InputAudioBuffers::from_raw_buffers(ins, frames_count), - OutputAudioBuffers::from_raw_buffers(outs, frames_count), - ) - } - } - - #[cfg(feature = "clack-plugin")] - pub fn from_plugin_audio( - audio: clack_plugin::prelude::Audio, - ) -> (InputAudioBuffers, OutputAudioBuffers) { - let frames_count = audio.frames_count(); - let (ins, outs) = audio.to_raw_buffers(); - - // SAFETY: the validity of the buffers is guaranteed by the Audio type - unsafe { - ( - InputAudioBuffers::from_raw_buffers(ins, frames_count), - OutputAudioBuffers::from_raw_buffers(outs, frames_count), - ) - } - } - pub fn with_capacity(total_channel_count: usize, port_count: usize) -> Self { let mut bufs = Self { buffer_configs: Vec::with_capacity(port_count), @@ -144,7 +113,7 @@ impl AudioPorts { pub fn with_input_buffers<'a, I, Iter, ChannelIter32, ChannelIter64>( &'a mut self, iter: I, - ) -> InputAudioBuffers<'a> + ) -> AudioBuffers<'a> where I: IntoIterator, IntoIter = Iter>, Iter: ExactSizeIterator>, @@ -238,8 +207,8 @@ impl AudioPorts { } } - InputAudioBuffers { - buffers: &self.buffer_configs[..total], + AudioBuffers { + buffers: CelledClapAudioBuffer::from_raw_slice(&mut self.buffer_configs[..total]), frames_count: if min_channel_buffer_length == usize::MAX { None } else { @@ -251,7 +220,7 @@ impl AudioPorts { pub fn with_output_buffers<'a, I, Iter, ChannelIter32, ChannelIter64>( &'a mut self, iter: I, - ) -> OutputAudioBuffers<'a> + ) -> AudioBuffers<'a> where I: IntoIterator, IntoIter = Iter>, Iter: ExactSizeIterator>, @@ -336,8 +305,8 @@ impl AudioPorts { } } - OutputAudioBuffers { - buffers: &mut self.buffer_configs[..total], + AudioBuffers { + buffers: CelledClapAudioBuffer::from_raw_slice(&mut self.buffer_configs[..total]), frames_count: if min_channel_buffer_length == usize::MAX { None } else { @@ -352,132 +321,49 @@ impl AudioPorts { } } -pub struct InputAudioBuffers<'a> { - buffers: &'a [clap_audio_buffer], - frames_count: Option, +#[repr(C)] +pub(crate) struct CelledClapAudioBuffer { + pub data32: *const *const f32, + pub data64: *const *const f64, + pub channel_count: u32, + pub latency: u32, + pub constant_mask: Cell, // Cell has the same memory layout as the inner type } -impl<'a> InputAudioBuffers<'a> { - #[inline] - pub const fn empty() -> Self { - Self { - buffers: &[], - frames_count: None, - } - } - - /// Shortens the [`frames_count`] of these input buffers. - /// - /// This does not actually change the underlying buffers themselves, it only reduces the - /// slice that will be exposed to the plugin. - /// - /// This method does nothing if `max_buffer_size` is greater or equal than the current [`frames_count`]. - /// - /// [`frames_count`]: self.frames_count - pub fn truncate(&mut self, max_buffer_size: u32) { - if let Some(frames_count) = self.frames_count { - self.frames_count = Some(frames_count.min(max_buffer_size)) - } - } - - /// # Safety - /// - /// The caller must ensure all buffer structs are valid for 'a, including all the buffer - /// pointers they contain. - /// - /// The caller must also ensure `frames_count` is lower than or equal to the sizes of the - /// channel buffers pointed to by `buffers`. +impl CelledClapAudioBuffer { #[inline] - pub unsafe fn from_raw_buffers(buffers: &'a [clap_audio_buffer], frames_count: u32) -> Self { - Self { - buffers, - frames_count: Some(frames_count), - } - } - - #[cfg(feature = "clack-plugin")] - pub fn from_plugin_audio(audio: &clack_plugin::prelude::Audio<'a>) -> InputAudioBuffers<'a> { - let frames_count = audio.frames_count(); - let ins = audio.raw_input_buffers(); - - // SAFETY: the validity of the buffers is guaranteed by the Audio type - unsafe { InputAudioBuffers::from_raw_buffers(ins, frames_count) } - } - - #[cfg(feature = "clack-plugin")] - pub fn as_plugin_audio(&self) -> clack_plugin::prelude::Audio<'a> { - // SAFETY: the validity of the buffers is guaranteed by this type - unsafe { - clack_plugin::prelude::Audio::from_raw_buffers( - self.buffers, - &mut [], - self.frames_count.unwrap_or(0), - ) - } + pub(crate) fn as_raw_ptr(&self) -> *mut clap_audio_buffer { + self as *const _ as *const _ as *mut _ } #[inline] - pub fn as_raw_buffers(&self) -> &'a [clap_audio_buffer] { - self.buffers + pub(crate) fn slice_as_raw_ptr(slice: &[CelledClapAudioBuffer]) -> *mut [clap_audio_buffer] { + slice as *const _ as *const _ as *mut _ } - /// The number of port buffers this [`InputAudioBuffers`] has been given. #[inline] - pub fn port_count(&self) -> usize { - self.buffers.len() - } - - /// The number of frames in these input buffers. - /// - /// This is the minimum frame count of all the port buffers this has been given. - /// - /// If this has no port buffer (i.e. [`port_count`](self.port_count) is zero), this returns `None`. - #[inline] - pub fn frames_count(&self) -> Option { - self.frames_count - } - - #[inline] - pub fn port_info(&self, port_index: u32) -> Option { - self.buffers - .get(port_index as usize) - .map(AudioPortProcessingInfo::from_raw) - } - - #[inline] - pub fn port_infos(&self) -> impl Iterator + '_ { - self.buffers.iter().map(AudioPortProcessingInfo::from_raw) - } - - /// Returns the minimum number of frames available both in this [`InputAudioBuffers`] and - /// the given [`OutputAudioBuffers`]. - /// - /// This is useful to ensure a safe frame count for a `process` batch that would receive those - /// input and output audio buffers. - pub fn min_available_frames_with(&self, outputs: &OutputAudioBuffers) -> u32 { - match (self.frames_count, outputs.frames_count) { - (Some(a), Some(b)) => a.min(b), - (Some(a), None) | (None, Some(a)) => a, - (None, None) => 0, - } + pub(crate) fn from_raw_slice(slice: &mut [clap_audio_buffer]) -> &[Self] { + // SAFETY: TODO + unsafe { &*(slice as *mut [clap_audio_buffer] as *mut [CelledClapAudioBuffer]) } } } -pub struct OutputAudioBuffers<'a> { - buffers: &'a mut [clap_audio_buffer], +#[derive(Copy, Clone)] +pub struct AudioBuffers<'a> { + buffers: &'a [CelledClapAudioBuffer], frames_count: Option, } -impl<'a> OutputAudioBuffers<'a> { +impl<'a> AudioBuffers<'a> { #[inline] - pub fn empty() -> Self { + pub const fn empty() -> Self { Self { - buffers: &mut [], + buffers: &[], frames_count: None, } } - /// Shortens the [`frames_count`] of these output buffers. + /// Shortens the [`frames_count`] of these input buffers. /// /// This does not actually change the underlying buffers themselves, it only reduces the /// slice that will be exposed to the plugin. @@ -493,117 +379,61 @@ impl<'a> OutputAudioBuffers<'a> { /// # Safety /// - /// The caller must ensure all buffer structs are valid for 'a, including all the buffer - /// pointers they contain. + /// The caller must ensure the given pointers to all buffer structs are valid for 'a, + /// including all the buffer pointers they themselves contain. /// /// The caller must also ensure `frames_count` is lower than or equal to the sizes of the /// channel buffers pointed to by `buffers`. #[inline] - pub unsafe fn from_raw_buffers( - buffers: &'a mut [clap_audio_buffer], - frames_count: u32, - ) -> Self { + pub unsafe fn from_raw_buffers(buffers: *mut [clap_audio_buffer], frames_count: u32) -> Self { Self { - buffers, + buffers: *(buffers as *const _ as *const _), frames_count: Some(frames_count), } } - #[cfg(feature = "clack-plugin")] - pub fn from_plugin_audio(audio: clack_plugin::prelude::Audio<'a>) -> OutputAudioBuffers<'a> { - let frames_count = audio.frames_count(); - let outs = audio.to_raw_output_buffers(); - - // SAFETY: the validity of the buffers is guaranteed by the Audio type - unsafe { OutputAudioBuffers::from_raw_buffers(outs, frames_count) } + #[inline] + pub fn as_raw_buffers(&self) -> *mut [clap_audio_buffer] { + CelledClapAudioBuffer::slice_as_raw_ptr(self.buffers) } #[cfg(feature = "clack-plugin")] - pub fn from_plugin_audio_mut( - audio: &'a mut clack_plugin::prelude::Audio, - ) -> OutputAudioBuffers<'a> { + pub fn from_plugin_audio_inputs(audio: &'a clack_plugin::prelude::Audio) -> Self { let frames_count = audio.frames_count(); - let outs = audio.raw_output_buffers(); // SAFETY: the validity of the buffers is guaranteed by the Audio type - unsafe { OutputAudioBuffers::from_raw_buffers(outs, frames_count) } + unsafe { AudioBuffers::from_raw_buffers(audio.raw_inputs(), frames_count) } } #[cfg(feature = "clack-plugin")] - pub fn as_plugin_audio(&'a mut self) -> clack_plugin::prelude::Audio<'a> { - // SAFETY: the validity of the buffers is guaranteed by this type - unsafe { - clack_plugin::prelude::Audio::from_raw_buffers( - &[], - self.buffers, - self.frames_count.unwrap_or(0), - ) - } - } - - #[cfg(feature = "clack-plugin")] - pub fn to_plugin_audio(self) -> clack_plugin::prelude::Audio<'a> { - // SAFETY: the validity of the buffers is guaranteed by this type - unsafe { - clack_plugin::prelude::Audio::from_raw_buffers( - &[], - self.buffers, - self.frames_count.unwrap_or(0), - ) - } - } - - #[cfg(feature = "clack-plugin")] - pub fn as_plugin_audio_with_inputs( - &'a mut self, - inputs: &InputAudioBuffers<'a>, - ) -> clack_plugin::prelude::Audio<'a> { - let frames_count = inputs.min_available_frames_with(self); + pub fn from_plugin_audio_outputs(audio: &'a clack_plugin::prelude::Audio) -> Self { + let frames_count = audio.frames_count(); - // SAFETY: the validity of the buffers is guaranteed by this type - unsafe { - clack_plugin::prelude::Audio::from_raw_buffers( - inputs.buffers, - self.buffers, - frames_count, - ) - } + // SAFETY: the validity of the buffers is guaranteed by the Audio type + unsafe { AudioBuffers::from_raw_buffers(audio.raw_outputs(), frames_count) } } #[cfg(feature = "clack-plugin")] - pub fn to_plugin_audio_with_inputs( - self, - inputs: &InputAudioBuffers<'a>, - ) -> clack_plugin::prelude::Audio<'a> { - let frames_count = inputs.min_available_frames_with(&self); + pub fn as_plugin_audio_with_outputs(&self, outputs: &Self) -> clack_plugin::prelude::Audio<'a> { + let frames_count = self.min_available_frames_with(outputs); // SAFETY: the validity of the buffers is guaranteed by this type unsafe { clack_plugin::prelude::Audio::from_raw_buffers( - inputs.buffers, - self.buffers, + self.as_raw_buffers(), + outputs.as_raw_buffers(), frames_count, ) } } - #[inline] - pub fn as_raw_buffers(&mut self) -> &mut [clap_audio_buffer] { - self.buffers - } - - #[inline] - pub fn into_raw_buffers(self) -> &'a mut [clap_audio_buffer] { - self.buffers - } - - /// The number of port buffers this [`OutputAudioBuffers`] has been given. + /// The number of port buffers this [`AudioBuffers`] has been given. #[inline] pub fn port_count(&self) -> usize { self.buffers.len() } - /// The number of frames in these output buffers. + /// The number of frames in these input buffers. /// /// This is the minimum frame count of all the port buffers this has been given. /// @@ -615,14 +445,32 @@ impl<'a> OutputAudioBuffers<'a> { #[inline] pub fn port_info(&self, port_index: u32) -> Option { - self.buffers - .get(port_index as usize) - .map(AudioPortProcessingInfo::from_raw) + // SAFETY: We checked port_index was in-bounds above. + let info_ptr = self.buffers.get(port_index as usize)?; + + // SAFETY: TODO + unsafe { Some(AudioPortProcessingInfo::from_raw_ptr(info_ptr.as_raw_ptr())) } } #[inline] pub fn port_infos(&self) -> impl Iterator + '_ { - self.buffers.iter().map(AudioPortProcessingInfo::from_raw) + self.buffers + .iter() + // SAFETY: TODO + .map(|i| unsafe { AudioPortProcessingInfo::from_raw_ptr(i.as_raw_ptr()) }) + } + + /// Returns the minimum number of frames available both in this [`AudioBuffers`] and + /// the given [`AudioBuffers`]. + /// + /// This is useful to ensure a safe frame count for a `process` batch that would receive those + /// input and output audio buffers. + pub fn min_available_frames_with(&self, outputs: &AudioBuffers) -> u32 { + match (self.frames_count, outputs.frames_count) { + (Some(a), Some(b)) => a.min(b), + (Some(a), None) | (None, Some(a)) => a, + (None, None) => 0, + } } } @@ -728,7 +576,7 @@ mod test { })), })); - let mut output_buffers = + let output_buffers = output_ports.with_output_buffers(output_bufs.iter_mut().map(|bufs| AudioPortBuffer { latency: 0, channels: AudioPortBufferType::f32_output_only( @@ -741,13 +589,11 @@ mod test { assert_eq!(output_buffers.buffers.len(), 2); assert_eq!(output_buffers.frames_count, Some(4)); - let raw_input_buffers = input_buffers.as_raw_buffers(); - let raw_output_buffers = output_buffers.as_raw_buffers(); let process = clap_process { - audio_inputs: raw_input_buffers.as_ptr(), - audio_outputs: raw_output_buffers.as_ptr() as *mut _, - audio_inputs_count: raw_input_buffers.len() as u32, - audio_outputs_count: raw_output_buffers.len() as u32, + audio_inputs: input_buffers.as_raw_buffers().cast(), + audio_outputs: output_buffers.as_raw_buffers().cast(), + audio_inputs_count: input_buffers.port_count() as u32, + audio_outputs_count: output_buffers.port_count() as u32, steady_time: 0, frames_count: 4, @@ -757,23 +603,23 @@ mod test { }; // SAFETY: we built the process struct above, it should be good. - let mut audio = unsafe { Audio::from_raw(&process) }; + let audio = unsafe { Audio::from_raw(&process) }; for (port, bufs) in audio.input_ports().zip(&input_bufs) { - let channels = port.channels().unwrap().into_f32().unwrap(); + let channels = port.channels().unwrap().to_f32().unwrap(); assert_eq!(channels.channel_count(), 128); for (channel, buf) in channels.iter().zip(bufs.iter()) { - assert_eq!(buf, channel) + assert_eq!(channel, buf) } } - for (mut port, bufs) in audio.output_ports().zip(&output_bufs) { - let channels = port.channels().unwrap().into_f32().unwrap(); + for (port, bufs) in audio.output_ports().zip(&output_bufs) { + let channels = port.channels().unwrap().to_f32().unwrap(); assert_eq!(channels.channel_count(), 128); for (channel, buf) in channels.iter().zip(bufs.iter()) { - assert_eq!(buf, channel) + assert_eq!(channel, buf) } } } diff --git a/plugin/Cargo.toml b/plugin/Cargo.toml index 3c1554d4..ef277ce1 100644 --- a/plugin/Cargo.toml +++ b/plugin/Cargo.toml @@ -10,6 +10,7 @@ rust-version = "1.72.0" [dependencies] clap-sys = { workspace = true } clack-common = { workspace = true } +ringbuf = "0.4.7" [dev-dependencies] clack-host = { workspace = true, default-features = false, features = ["clack-plugin"] } diff --git a/plugin/examples/gain/src/lib.rs b/plugin/examples/gain/src/lib.rs index 8ca630f4..2eb475a4 100644 --- a/plugin/examples/gain/src/lib.rs +++ b/plugin/examples/gain/src/lib.rs @@ -77,32 +77,32 @@ impl<'a> PluginAudioProcessor<'a, GainPluginShared, GainPluginMainThread<'a>> fn process( &mut self, _process: Process, - mut audio: Audio, + audio: Audio, events: Events, ) -> Result { // First, we have to make a few sanity checks. // We want at least a single input/output port pair, which contains channels of `f32` // audio sample data. - let mut port_pair = audio + let port_pair = audio .port_pair(0) .ok_or(PluginError::Message("No input/output ports found"))?; - let mut output_channels = port_pair + let output_channels = port_pair .channels()? - .into_f32() + .to_f32() .ok_or(PluginError::Message("Expected f32 input/output"))?; let mut channel_buffers = [None, None]; // Extract the buffer slices that we need, while making sure they are paired correctly and // check for either in-place or separate buffers. - for (pair, buf) in output_channels.iter_mut().zip(&mut channel_buffers) { + for (pair, buf) in output_channels.iter().zip(&mut channel_buffers) { *buf = match pair { ChannelPair::InputOnly(_) => None, ChannelPair::OutputOnly(_) => None, ChannelPair::InPlace(b) => Some(b), ChannelPair::InputOutput(i, o) => { - o.copy_from_slice(i); + o.copy_from_buffer(i); Some(o) } } @@ -120,9 +120,9 @@ impl<'a> PluginAudioProcessor<'a, GainPluginShared, GainPluginMainThread<'a>> // Get the volume value after all parameter changes have been handled. let volume = self.shared.params.get_volume(); - for buf in channel_buffers.iter_mut().flatten() { - for sample in buf.iter_mut() { - *sample *= volume + for buf in channel_buffers.iter().flatten() { + for sample in *buf { + sample.set(sample.get() * volume) } } } diff --git a/plugin/examples/gain/tests/test_gain.rs b/plugin/examples/gain/tests/test_gain.rs index 79d3bcbd..bbd56bca 100644 --- a/plugin/examples/gain/tests/test_gain.rs +++ b/plugin/examples/gain/tests/test_gain.rs @@ -108,7 +108,7 @@ pub fn it_works() { latency: 0, }]); - let mut output_channels = outputs_descriptors.with_output_buffers([AudioPortBuffer { + let output_channels = outputs_descriptors.with_output_buffers([AudioPortBuffer { channels: AudioPortBufferType::f32_output_only( output_buffers.iter_mut().map(|b| b.as_mut_slice()), ), @@ -118,7 +118,7 @@ pub fn it_works() { processor .process( &input_channels, - &mut output_channels, + &output_channels, &input_events.as_input(), &mut output_events.as_output(), None, diff --git a/plugin/examples/polysynth/src/lib.rs b/plugin/examples/polysynth/src/lib.rs index e4ddf17c..5e5cfe82 100644 --- a/plugin/examples/polysynth/src/lib.rs +++ b/plugin/examples/polysynth/src/lib.rs @@ -88,23 +88,23 @@ impl<'a> PluginAudioProcessor<'a, PolySynthPluginShared, PolySynthPluginMainThre fn process( &mut self, _process: Process, - mut audio: Audio, + audio: Audio, events: Events, ) -> Result { // First, we have to make a few sanity checks. // We want at least a single output port, which contains at least one channel of `f32` // audio sample data. - let mut output_port = audio + let output_port = audio .output_port(0) .ok_or(PluginError::Message("No output port found"))?; - let mut output_channels = output_port + let output_channels = output_port .channels()? - .into_f32() + .to_f32() .ok_or(PluginError::Message("Expected f32 output"))?; let output_buffer = output_channels - .channel_mut(0) + .channel(0) .ok_or(PluginError::Message("Expected at least one channel"))?; // Ensure the buffer is zero-filled, as all oscillators will just add to it. @@ -119,7 +119,7 @@ impl<'a> PluginAudioProcessor<'a, PolySynthPluginShared, PolySynthPluginMainThre // With all the events out of the way, we can now handle a whole batch of sample // all at once. - let output_buffer = &mut output_buffer[event_batch.sample_bounds()]; + let output_buffer = &output_buffer[event_batch.sample_bounds()]; self.poly_osc.generate_next_samples( output_buffer, self.shared.params.get_volume(), @@ -129,13 +129,12 @@ impl<'a> PluginAudioProcessor<'a, PolySynthPluginShared, PolySynthPluginMainThre // If somehow the host didn't give us a mono output, we copy the output to all channels if output_channels.channel_count() > 1 { - let (first_channel, other_channels) = output_channels.split_at_mut(1); // PANIC: we just checked that channel_count is > 1. - let first_channel = first_channel.channel(0).unwrap(); + let first_channel = &output_channels[0]; // Copy the first channel into all the other channels. - for other_channel in other_channels { - other_channel.copy_from_slice(first_channel) + for other_channel in output_channels.iter().skip(1) { + other_channel.copy_from_buffer(first_channel) } } diff --git a/plugin/examples/polysynth/src/oscillator.rs b/plugin/examples/polysynth/src/oscillator.rs index 2b032254..da8f00fd 100644 --- a/plugin/examples/polysynth/src/oscillator.rs +++ b/plugin/examples/polysynth/src/oscillator.rs @@ -2,6 +2,7 @@ //! //! This is where all the DSP math happens. +use clack_plugin::process::audio::AudioBuffer; use std::f32::consts::{PI, TAU}; /// A very basic Square Wave oscillator. @@ -55,12 +56,12 @@ impl SquareOscillator { /// /// The given volume should be in the `0..1` range. #[inline] - pub fn add_next_samples_to_buffer(&mut self, buf: &mut [f32], volume: f32) { + pub fn add_next_samples_to_buffer(&mut self, buf: &AudioBuffer, volume: f32) { for value in buf { if self.current_phase <= PI { - *value += volume; + value.set(value.get() + volume); } else { - *value -= volume; + value.set(value.get() - volume); } self.current_phase += self.phase_increment; diff --git a/plugin/examples/polysynth/src/poly_oscillator.rs b/plugin/examples/polysynth/src/poly_oscillator.rs index c52b4c93..240deffd 100644 --- a/plugin/examples/polysynth/src/poly_oscillator.rs +++ b/plugin/examples/polysynth/src/poly_oscillator.rs @@ -6,6 +6,7 @@ use clack_plugin::events::event_types::{ NoteOffEvent, NoteOnEvent, ParamModEvent, ParamValueEvent, }; use clack_plugin::events::Match; +use clack_plugin::process::audio::AudioBuffer; /// A voice in the polyphonic oscillator. /// @@ -195,7 +196,7 @@ impl PolyOscillator { /// This method assumes the buffer is initialized with `0`s. pub fn generate_next_samples( &mut self, - output_buffer: &mut [f32], + output_buffer: &AudioBuffer, global_volume: f32, global_volume_mod: f32, ) { diff --git a/plugin/src/internal_utils.rs b/plugin/src/internal_utils.rs index caf90972..fba7b781 100644 --- a/plugin/src/internal_utils.rs +++ b/plugin/src/internal_utils.rs @@ -25,21 +25,6 @@ pub(crate) unsafe fn slice_from_external_parts<'a, T>(data: *const T, len: usize core::slice::from_raw_parts(data, len) } -/// Same as [`slice_from_external_parts`] but for mut slices. -/// -/// # Safety -/// -/// Same as [`core::slice::from_raw_parts_mut`], except the provided pointer *can* be null or -/// dangling for zero-length slices. -#[inline] -pub(crate) unsafe fn slice_from_external_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { - if len == 0 { - return &mut []; - } - - core::slice::from_raw_parts_mut(data, len) -} - /// Equivalent in spirit to `UnsafeCell>`, except you can read if the cell is set or not /// without invalidating potential active &mut references to the data. pub(crate) struct UnsafeOptionCell { diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index ed135a06..d6e58184 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -3,8 +3,6 @@ #![deny(clippy::undocumented_unsafe_blocks)] #![deny(missing_docs)] -extern crate core; - #[macro_use] pub mod entry; pub mod extensions; @@ -35,7 +33,7 @@ pub mod prelude { PluginShared, }, process::{ - audio::{ChannelPair, SampleType}, + audio::{AudioBuffer, ChannelPair, SampleType}, Audio, Events, PluginAudioConfiguration, Process, ProcessStatus, }, utils::ClapId, diff --git a/plugin/src/process.rs b/plugin/src/process.rs index 543b1bea..8bdbb323 100644 --- a/plugin/src/process.rs +++ b/plugin/src/process.rs @@ -3,15 +3,15 @@ //! All of those types are exclusively used in the [`Plugin::process`](crate::plugin::PluginAudioProcessor::process) //! method. See the [`Plugin`](crate::plugin::PluginAudioProcessor) trait documentation for examples on how these types interact. +use crate::internal_utils::slice_from_external_parts; use clack_common::events::event_types::TransportEvent; use clack_common::events::io::{InputEvents, OutputEvents}; +pub use clack_common::process::*; use clap_sys::audio_buffer::clap_audio_buffer; use clap_sys::process::clap_process; use std::ops::RangeBounds; -pub use clack_common::process::*; pub mod audio; -use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut}; use audio::*; /// Metadata about the current process call. @@ -93,20 +93,20 @@ impl Events<'_> { /// * Each channel is a raw buffer (i.e. slice) of either [`f32`] or [`f64`] samples. /// /// This structure applies both to inputs and outputs: the [`Audio`] struct allows to retrieve -/// [`InputPort`]s and [`OutputPort`]s separately, but they can also be accessed together as +/// input and output [`Port`]s separately, but they can also be accessed together as /// Input/Output [`PortPair`]s. This allows for the common use-case of borrowing both an input /// and its matching output for processing, while also being safe to hosts using the same buffer for /// both. /// /// For each port type (input, output, or paired), ports can be accessed either individually with -/// an index, or all at once with an iterator. For instance, [`InputPort`]s can be accessed either +/// an index, or all at once with an iterator. For instance, [`Port`]s can be accessed either /// one-at-a-time with [`Audio::input_port`], or with an iterator from [`Audio::input_ports`]. A /// [`Audio::input_port_count`] method is also available. The same methods are available for -/// [`OutputPort`]s and [`PortPair`]s. +/// the output [`Port`]s and [`PortPair`]s. /// /// Note that because ports can individually hold either 32-bit or 64-bit sample data, an extra /// sample type detection step is necessary before the port's channels themselves can be accessed. -/// This is done through the [`channels`](InputPort::channels) methods on each port type, and +/// This is done through the [`channels`](Port::channels) methods on each port type, and /// returns a [`SampleType`] enum indicating whether the port's buffers hold 32-bit or 64-bit samples. /// /// # Example @@ -117,10 +117,10 @@ impl Events<'_> { /// ``` /// use clack_plugin::prelude::*; /// -/// pub fn process(mut audio: Audio) -> Result { -/// for mut port_pair in &mut audio { +/// pub fn process(audio: Audio) -> Result { +/// for mut port_pair in audio { /// // For this example, we'll only care about 32-bit sample data. -/// let Some(channel_pairs) = port_pair.channels()?.into_f32() else { continue }; +/// let Some(channel_pairs) = port_pair.channels()?.to_f32() else { continue }; /// /// for channel_pair in channel_pairs { /// match channel_pair { @@ -132,7 +132,7 @@ impl Events<'_> { /// // we can copy the input to the outputs while multiplying /// ChannelPair::InputOutput(input, output) => { /// for (input, output) in input.iter().zip(output) { -/// *output = input * 2.0 +/// output.set(input.get() * 2.0) /// } /// } /// // If the host sent us a single buffer to be processed in-place @@ -140,7 +140,7 @@ impl Events<'_> { /// // then we can do the processing in-place directly. /// ChannelPair::InPlace(buf) => { /// for sample in buf { -/// *sample *= 2.0 +/// sample.set(sample.get() * 2.0) /// } /// } /// } @@ -157,20 +157,20 @@ impl Events<'_> { /// ``` /// use clack_plugin::prelude::*; /// -/// pub fn process(mut audio: Audio) -> Result { -/// for mut port_pair in &mut audio { +/// pub fn process(audio: Audio) -> Result { +/// for mut port_pair in audio { /// // For this example, we'll only care about 32-bit sample data. -/// let Some(mut channel_pairs) = port_pair.channels()?.into_f32() else { continue; }; +/// let Some(mut channel_pairs) = port_pair.channels()?.to_f32() else { continue; }; /// /// // Buffers to hold pointers to the left and right channels -/// let mut input_channels: [Option<&[f32]>; 2] = [None, None]; -/// let mut output_channels: [Option<&mut [f32]>; 2] = [None, None]; +/// let mut input_channels: [Option<&AudioBuffer>; 2] = [None, None]; +/// let mut output_channels: [Option<&AudioBuffer>; 2] = [None, None]; /// /// // Before we can process the buffers, we need to check all the necessary channels /// // are present, in case the host messed up the port configuration. /// // (Yes, some do.) -/// for ((channel_pair, in_ptr), out_ptr) in channel_pairs -/// .iter_mut() +/// for ((channel_pair, in_buf), out_buf) in channel_pairs +/// .iter() /// .zip(&mut input_channels) /// .zip(&mut output_channels) /// { @@ -179,26 +179,27 @@ impl Events<'_> { /// // the other cases, including having spare input buffers ready for /// // in-place processing. /// if let ChannelPair::InputOutput(input, output) = channel_pair { -/// *in_ptr = Some(input); -/// *out_ptr = Some(output); +/// *in_buf = Some(input); +/// *out_buf = Some(output); /// } /// } /// /// // Channel swap! (assuming all channels are there) -/// if let (Some(in_l), Some(out_r)) = (&input_channels[0], &mut output_channels[1]) { -/// out_r.copy_from_slice(in_l) +/// if let (Some(in_l), Some(out_r)) = (input_channels[0], output_channels[1]) { +/// out_r.copy_from_buffer(in_l) /// } -/// if let (Some(in_r), Some(out_l)) = (&input_channels[1], &mut output_channels[0]) { -/// out_l.copy_from_slice(in_r) +/// if let (Some(in_r), Some(out_l)) = (input_channels[1], output_channels[0]) { +/// out_l.copy_from_buffer(in_r) /// } /// } /// /// Ok(ProcessStatus::Continue) /// } /// ``` +#[derive(Copy, Clone)] pub struct Audio<'a> { - inputs: &'a [clap_audio_buffer], - outputs: &'a mut [clap_audio_buffer], + inputs: &'a [CelledClapAudioBuffer], + outputs: &'a [CelledClapAudioBuffer], frames_count: u32, } @@ -209,17 +210,17 @@ impl<'a> Audio<'a> { /// # Safety /// /// Users must ensure all fields of the given `raw_process` are valid, and that all buffers - /// it points to stay valid for the given lifetime. + /// it points to remain valid for the given `'a` lifetime. #[inline] - pub unsafe fn from_raw(raw_process: &clap_process) -> Audio { + pub unsafe fn from_raw(raw_process: &clap_process) -> Audio<'a> { Audio { frames_count: raw_process.frames_count, inputs: slice_from_external_parts( - raw_process.audio_inputs, + raw_process.audio_inputs.cast(), raw_process.audio_inputs_count as usize, ), - outputs: slice_from_external_parts_mut( - raw_process.audio_outputs, + outputs: slice_from_external_parts( + raw_process.audio_outputs.cast(), raw_process.audio_outputs_count as usize, ), } @@ -229,159 +230,136 @@ impl<'a> Audio<'a> { /// /// # Safety /// - /// The caller must ensure all buffer structs are valid for 'a, including all the buffer - /// pointers they contain. + /// The caller must ensure the given pointers to all buffer structs are valid for both reads + /// and writes for the duration of 'a, including all the buffer pointers they themselves contain. /// /// The caller must also ensure `frames_count` is lower than or equal to the sizes of the /// channel buffers pointed to by `buffers`. #[inline] pub unsafe fn from_raw_buffers( - inputs: &'a [clap_audio_buffer], - outputs: &'a mut [clap_audio_buffer], + inputs: *mut [clap_audio_buffer], + outputs: *mut [clap_audio_buffer], frames_count: u32, ) -> Self { Self { - inputs, - outputs, + inputs: &*(inputs as *const _), + outputs: &*(outputs as *const _), frames_count, } } - /// Returns the raw input and output buffers structs, respectively. - #[inline] - pub fn raw_buffers(&mut self) -> (&'a [clap_audio_buffer], &mut [clap_audio_buffer]) { - (self.inputs, self.outputs) - } - - /// Returns the raw input and output buffers structs, respectively, consuming the audio struct. - #[inline] - pub fn to_raw_buffers(self) -> (&'a [clap_audio_buffer], &'a mut [clap_audio_buffer]) { - (self.inputs, self.outputs) - } - - /// Returns the raw input buffers structs. - #[inline] - pub fn raw_input_buffers(&self) -> &'a [clap_audio_buffer] { - self.inputs - } - - /// Returns the raw output buffers structs. + /// Returns a raw pointer to a slice of the raw input buffers structs. + /// + /// # Safety + /// + /// While this function is safe to use, there are many cases where using the resulting pointer + /// is not. + /// + /// This is because the contents slice of buffer structs (as well as the raw audio buffers these + /// point to) are valid for both reads and writes from other, potentially aliased pointers to + /// that data. + /// + /// This means it is not valid to create either shared (`&`) or mutable (`&mut`) Rust references + /// to these buffers or their data. + /// + /// In order to safely access the data, you can either use [`Cell`]s, or perform direct + /// read or write operations, e.g. using [`ptr::read`] or [`ptr::write`]. + /// + /// [`ptr::read`]: core::ptr::read + /// [`ptr::write`]: core::ptr::write + /// [`Cell`]: core::cell::Cell #[inline] - pub fn raw_output_buffers(&mut self) -> &mut [clap_audio_buffer] { - self.outputs + pub fn raw_inputs(&self) -> *mut [clap_audio_buffer] { + core::ptr::slice_from_raw_parts_mut( + self.inputs.as_ptr().cast_mut().cast(), + self.inputs.len(), + ) } - /// Returns the raw output buffers structs, consuming the audio struct. + /// Returns a raw pointer to a C array of the raw output buffers structs. + /// + /// # Safety + /// + /// While this function is safe to use, there are many cases where using the resulting pointer + /// is not. + /// + /// This is because the contents slice of buffer structs (as well as the raw audio buffers these + /// point to) are valid for both reads and writes from other, potentially aliased pointers to + /// that data. + /// + /// This means it is not valid to create either shared (`&`) or mutable (`&mut`) Rust references + /// to these buffers or their data. + /// + /// In order to safely access the data, you can either use [`Cell`]s, or perform direct + /// read or write operations, e.g. using [`ptr::read`] or [`ptr::write`]. + /// + /// [`ptr::read`]: core::ptr::read + /// [`ptr::write`]: core::ptr::write + /// [`Cell`]: core::cell::Cell #[inline] - pub fn to_raw_output_buffers(self) -> &'a mut [clap_audio_buffer] { - self.outputs + pub fn raw_outputs(&self) -> *mut [clap_audio_buffer] { + core::ptr::slice_from_raw_parts_mut( + self.outputs.as_ptr().cast_mut().cast(), + self.outputs.len(), + ) } - /// Retrieves the [`InputPort`] at a given index. + /// Retrieves the input [`Port`] at a given index. /// /// This returns [`None`] if there is no input port at the given index. /// /// See also the [`input_port_count`](Audio::input_port_count) method to know how many input /// ports are available, and the [`input_ports`](Audio::input_ports) method to get all input ports at once. #[inline] - pub fn input_port(&self, index: usize) -> Option { + pub fn input_port(&self, index: usize) -> Option> { self.inputs .get(index) // SAFETY: this type ensures the provided buffer is valid and frames_count is correct - .map(|buf| unsafe { InputPort::from_raw(buf, self.frames_count) }) - } - - /// Retrieves the [`AudioPortProcessingInfo`] of the [`InputPort`] at a given index. - /// - /// This returns [`None`] if there is no input port at the given index. - /// - /// See also the [`input_port_count`](Audio::input_port_count) method to know how many output - /// ports are available, and the [`input_ports_infos`](Audio::input_ports_infos) method to get the info of - /// all input ports at once. - #[inline] - pub fn input_port_info(&self, index: usize) -> Option { - self.inputs - .get(index) - .map(AudioPortProcessingInfo::from_raw) + .map(|buf| unsafe { Port::from_raw(buf, self.frames_count) }) } - /// Retrieves the number of available [`InputPort`]s. + /// Retrieves the number of available input [`Port`]s. #[inline] pub fn input_port_count(&self) -> usize { self.inputs.len() } - /// Returns an iterator of all the available [`InputPort`]s at once. + /// Returns an iterator of all the available input [`Port`]s at once. /// /// See also the [`input_port`](Audio::input_port) method to retrieve a single input port by /// its index. #[inline] - pub fn input_ports(&self) -> InputPortsIter { - InputPortsIter::new(self) + pub fn input_ports(&self) -> PortsIter<'a> { + PortsIter::new(self.inputs, self.frames_count) } - /// Retrieves the [`AudioPortProcessingInfo`] of all the available [`OutputPort`]s at once. - /// - /// See also the [`output_port_info`](Audio::output_port_info) method to retrieve a single - /// output port by its index. - #[inline] - pub fn input_ports_infos(&self) -> impl ExactSizeIterator + '_ { - self.inputs.iter().map(AudioPortProcessingInfo::from_raw) - } - - /// Retrieves the [`OutputPort`] at a given index. + /// Retrieves the output [`Port`] at a given index. /// /// This returns [`None`] if there is no output port at the given index. /// /// See also the [`output_port_count`](Audio::output_port_count) method to know how many output /// ports are available, and the [`output_ports`](Audio::output_ports) method to get all output ports at once. #[inline] - pub fn output_port(&mut self, index: usize) -> Option { - self.outputs - .get_mut(index) - // SAFETY: this type ensures the provided buffer is valid and frames_count is correct. - // Also, &mut ensures there is no input being read concurrently - .map(|buf| unsafe { OutputPort::from_raw(buf, self.frames_count) }) - } - - /// Retrieves the [`AudioPortProcessingInfo`] of the [`OutputPort`] at a given index. - /// - /// This returns [`None`] if there is no output port at the given index. - /// - /// See also the [`output_port_count`](Audio::output_port_count) method to know how many output - /// ports are available, and the [`output_ports_infos`](Audio::output_ports_infos) method to get the info of - /// all output ports at once. - #[inline] - pub fn output_port_info(&self, index: usize) -> Option { + pub fn output_port(&self, index: usize) -> Option> { self.outputs .get(index) - .map(AudioPortProcessingInfo::from_raw) + // SAFETY: this type ensures the provided buffer is valid and frames_count is correct. + .map(|buf| unsafe { Port::from_raw(buf, self.frames_count) }) } - /// Retrieves the number of available [`OutputPort`]s. + /// Retrieves the number of available output [`Port`]s. #[inline] pub fn output_port_count(&self) -> usize { self.outputs.len() } - /// Returns an iterator of all the available [`OutputPort`]s at once. + /// Returns an iterator of all the available output [`Port`]s at once. /// /// See also the [`output_port`](Audio::output_port) method to retrieve a single output port by /// its index. #[inline] - pub fn output_ports(&mut self) -> OutputPortsIter { - OutputPortsIter::new(self) - } - - /// Retrieves the [`AudioPortProcessingInfo`] of all the available [`OutputPort`]s at once. - /// - /// See also the [`output_port_info`](Audio::output_port_info) method to retrieve a single - /// output port by its index. - #[inline] - pub fn output_ports_infos( - &self, - ) -> impl ExactSizeIterator + '_ { - self.outputs.iter().map(AudioPortProcessingInfo::from_raw) + pub fn output_ports(&self) -> PortsIter<'a> { + PortsIter::new(self.outputs, self.frames_count) } /// Retrieves the [`PortPair`] at a given index. @@ -391,12 +369,12 @@ impl<'a> Audio<'a> { /// See also the [`port_pair_count`](Audio::port_pair_count) method to know how many port /// pairs are available, and the [`port_pairs`](Audio::port_pairs) method to get all port pairs at once. #[inline] - pub fn port_pair(&mut self, index: usize) -> Option { + pub fn port_pair(&self, index: usize) -> Option> { // SAFETY: this type ensures the provided buffers are valid and frames_count is correct unsafe { PortPair::from_raw( self.inputs.get(index), - self.outputs.get_mut(index), + self.outputs.get(index), self.frames_count, ) } @@ -417,13 +395,13 @@ impl<'a> Audio<'a> { /// See also the [`port_pair`](Audio::port_pair) method to retrieve a single input port by /// its index. #[inline] - pub fn port_pairs(&mut self) -> PortPairsIter { + pub fn port_pairs(&self) -> PortPairsIter<'a> { PortPairsIter::new(self) } /// Returns a sub-range of ports as a new [`Audio`] struct, similar to a subslice of items. #[inline] - pub fn port_sub_range + Clone>(&mut self, range: R) -> Audio { + pub fn port_sub_range(&self, range: impl RangeBounds + Clone) -> Audio<'a> { let inputs = self .inputs .get((range.start_bound().cloned(), range.end_bound().cloned())) @@ -431,8 +409,8 @@ impl<'a> Audio<'a> { let outputs = self .outputs - .get_mut((range.start_bound().cloned(), range.end_bound().cloned())) - .unwrap_or(&mut []); + .get((range.start_bound().cloned(), range.end_bound().cloned())) + .unwrap_or(&[]); Audio { inputs, @@ -450,7 +428,19 @@ impl<'a> Audio<'a> { } } -impl<'buf: 'a, 'a> IntoIterator for &'a mut Audio<'buf> { +impl<'a> IntoIterator for Audio<'a> { + type Item = PortPair<'a>; + type IntoIter = PortPairsIter<'a>; + + /// Returns an iterator of all the available [`PortPair`]s at once. This is equivalent to using + /// [`port_pairs`](Audio::port_pairs). + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.port_pairs() + } +} + +impl<'a> IntoIterator for &Audio<'a> { type Item = PortPair<'a>; type IntoIter = PortPairsIter<'a>; diff --git a/plugin/src/process/audio.rs b/plugin/src/process/audio.rs index 4bc1b3f5..ccfbc4e2 100644 --- a/plugin/src/process/audio.rs +++ b/plugin/src/process/audio.rs @@ -1,16 +1,27 @@ //! Various types related to accessing [`Audio`](super::Audio) buffers. +// TODO: explain hierarchy +mod buffer; mod error; -mod input; -mod output; mod pair; +mod port; mod sample_type; +pub use buffer::*; pub use error::BufferError; -pub use input::*; -pub use output::*; pub use pair::*; +pub use port::*; pub use sample_type::SampleType; +use std::cell::Cell; + +#[repr(C)] +pub(crate) struct CelledClapAudioBuffer { + pub data32: *const *const f32, + pub data64: *const *const f64, + pub channel_count: u32, + pub latency: u32, + pub constant_mask: Cell, // Cell has the same memory layout as the inner type +} #[cfg(test)] #[allow(missing_docs)] @@ -42,10 +53,13 @@ pub mod tests { let frames_count = input_buffers.min_available_frames_with(&output_buffers); - Audio { - inputs: input_buffers.as_raw_buffers(), - frames_count, - outputs: output_buffers.into_raw_buffers(), + // SAFETY: the validity of the buffers is guaranteed by this type + unsafe { + Audio::from_raw_buffers( + input_buffers.as_raw_buffers(), + output_buffers.as_raw_buffers(), + frames_count, + ) } } @@ -57,16 +71,16 @@ pub mod tests { let mut input_ports = AudioPorts::with_capacity(2, 1); let mut output_ports = AudioPorts::with_capacity(2, 1); - let mut audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); + let audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); let mut ports = audio.port_pairs(); assert_eq!(ports.len(), 1); assert_eq!(ports.size_hint(), (1, Some(1))); - let mut port = ports.next().unwrap(); + let port = ports.next().unwrap(); assert!(ports.next().is_none()); - let mut channels = port.channels().unwrap().into_f32().unwrap(); - assert_eq!(channels.iter_mut().len(), 2); - assert_eq!(channels.iter_mut().size_hint(), (2, Some(2))); + let channels = port.channels().unwrap().to_f32().unwrap(); + assert_eq!(channels.iter().len(), 2); + assert_eq!(channels.iter().size_hint(), (2, Some(2))); let mut constant_mask = ConstantMask::FULLY_CONSTANT; let mut total = 0; @@ -74,7 +88,7 @@ pub mod tests { let ChannelPair::InputOutput(i, o) = channel else { panic!("Expected I/O channel") }; - o.copy_from_slice(i); + o.copy_from_buffer(i); total += 1; constant_mask.set_channel_constant(total, false); } @@ -93,13 +107,13 @@ pub mod tests { let mut input_ports = AudioPorts::with_capacity(2, 1); let mut output_ports = AudioPorts::with_capacity(2, 1); - let mut audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); + let audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); assert_eq!(audio.port_pair_count(), 1); - let mut port = audio.port_pair(0).unwrap(); + let port = audio.port_pair(0).unwrap(); assert_eq!(port.channel_pair_count(), 2); - let mut channels = port.channels().unwrap().into_f32().unwrap(); + let channels = port.channels().unwrap().to_f32().unwrap(); assert_eq!(channels.channel_pair_count(), 2); let mut constant_mask = ConstantMask::FULLY_CONSTANT; @@ -109,7 +123,7 @@ pub mod tests { let ChannelPair::InputOutput(input, output) = channel else { panic!("Expected I/O channel") }; - output.copy_from_slice(input); + output.copy_from_buffer(input); constant_mask.set_channel_constant(i as u64, false); } @@ -127,7 +141,7 @@ pub mod tests { let mut input_ports = AudioPorts::with_capacity(2, 1); let mut output_ports = AudioPorts::with_capacity(2, 1); - let mut audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); + let audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); let mut ports = audio.input_ports(); assert_eq!(ports.len(), 1); @@ -135,13 +149,13 @@ pub mod tests { let port = ports.next().unwrap(); assert!(ports.next().is_none()); - let channels = port.channels().unwrap().into_f32().unwrap(); + let channels = port.channels().unwrap().to_f32().unwrap(); assert_eq!(channels.iter().len(), 2); assert_eq!(channels.iter().size_hint(), (2, Some(2))); let mut total = 0; for channel in channels { - assert!(channel.iter().all(|f| *f == 1.0)); + assert!(channel.iter().all(|f| f.get() == 1.0)); total += 1; } @@ -150,12 +164,12 @@ pub mod tests { let mut ports = audio.output_ports(); assert_eq!(ports.len(), 1); assert_eq!(ports.size_hint(), (1, Some(1))); - let mut port = ports.next().unwrap(); + let port = ports.next().unwrap(); assert!(ports.next().is_none()); - let mut channels = port.channels().unwrap().into_f32().unwrap(); - assert_eq!(channels.iter_mut().len(), 2); - assert_eq!(channels.iter_mut().size_hint(), (2, Some(2))); + let channels = port.channels().unwrap().to_f32().unwrap(); + assert_eq!(channels.iter().len(), 2); + assert_eq!(channels.iter().size_hint(), (2, Some(2))); let mut total = 0; for channel in channels { @@ -176,30 +190,30 @@ pub mod tests { let mut input_ports = AudioPorts::with_capacity(2, 1); let mut output_ports = AudioPorts::with_capacity(2, 1); - let mut audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); + let audio = get_audio(&mut ins, &mut outs, &mut input_ports, &mut output_ports); assert_eq!(audio.port_pair_count(), 1); let port = audio.input_port(0).unwrap(); assert_eq!(port.channel_count(), 2); - let channels = port.channels().unwrap().into_f32().unwrap(); + let channels = port.channels().unwrap().to_f32().unwrap(); assert_eq!(channels.channel_count(), 2); for i in 0..port.channel_count() { let channel = channels.channel(i).unwrap(); - assert!(channel.iter().all(|f| *f == 1.0)); + assert!(channel.iter().all(|f| f.get() == 1.0)); } - let mut port = audio.output_port(0).unwrap(); + let port = audio.output_port(0).unwrap(); assert_eq!(port.channel_count(), 2); - let mut channels = port.channels().unwrap().into_f32().unwrap(); + let channels = port.channels().unwrap().to_f32().unwrap(); assert_eq!(channels.channel_count(), 2); let constant_mask = ConstantMask::FULLY_CONSTANT; for i in 0..port.channel_count() { - let channel = channels.channel_mut(i).unwrap(); + let channel = channels.channel(i).unwrap(); channel.fill(1.0); } diff --git a/plugin/src/process/audio/buffer.rs b/plugin/src/process/audio/buffer.rs new file mode 100644 index 00000000..e26567f5 --- /dev/null +++ b/plugin/src/process/audio/buffer.rs @@ -0,0 +1,654 @@ +use core::cell::Cell; +use core::fmt::{Debug, Formatter}; +use core::ops::Index; +use core::ptr; +use core::slice::SliceIndex; + +/// A read/write buffer of audio samples of type `S`. +/// +/// Unlike the `&[T]` or `&mut [T]` slice types that can be used to represent contiguous audio +/// buffers, a single reference to an [`AudioBuffer`] can be both shared *and* mutable. +/// +/// However, because they allow shared mutability, it is not possible to obtain a reference +/// (shared or mutable) to a value in an [`AudioBuffer`], nor is it possible to get standard slices +/// from an [`AudioBuffer`] reference, as the values may be modified from any other reference to the +/// buffer. +/// +/// One can create a shareable [`AudioBuffer`] reference from a mutable slice reference (`&mut [T]`) +/// using the [`from_mut_slice`](AudioBuffer::from_mut_slice) method. +/// Alternatively, it is also possible to convert between [`AudioBuffer`] and slice of +/// [`Cell`s](Cell) references with the [`from_slice_of_cells`](AudioBuffer::from_slice_of_cells) and +/// [`as_slice_of_cells`](AudioBuffer::as_slice_of_cells) methods. +/// +/// [`AudioBuffer`s](AudioBuffer) can also be indexed into and sub-sliced like normal slices using +/// the [`Index`] operator. +/// +/// As long as the sample type `S` is `Copy`, the following operations are also directly available: +/// +/// * Reading sample data, using [`get`], [`try_get`] or [`get_unchecked`]; +/// * Writing sample data, using [`put`] or [`put_unchecked`]; +/// * Copying between buffers, using [`copy_from_buffer`] or [`copy_to_buffer`]; +/// * Copying from and to regular slices, using [`copy_from_slice`] or [`copy_to_slice`]; +/// * Filling the buffer with a single value using [`fill`]. +/// +/// # Example +/// +/// ``` +/// use clack_plugin::prelude::AudioBuffer; +/// let mut data = [0.0, 1.0, 2.0, 3.0, 4.0]; +/// +/// let buf1: &AudioBuffer = AudioBuffer::from_mut_slice(&mut data); +/// let buf2 = buf1; +/// +/// buf2.put(1, 11.0); +/// buf1.put(2, 22.0); +/// +/// assert_eq!(buf1, &[0.0, 11.0, 22.0, 3.0, 4.0]); +/// ``` +/// +/// ``` +/// use clack_plugin::prelude::AudioBuffer; +/// +/// // By taking AudioBuffers, this functions supports in-place processing. +/// fn double_input(input: &AudioBuffer, output: &AudioBuffer) { +/// for (i, o) in input.iter().zip(output) { +/// o.set(i.get() * 2.0) +/// } +/// } +/// +/// let mut input = [0.0, 1.0, 2.0, 3.0]; +/// let mut output = [0.0; 4]; +/// +/// let input_buf: &AudioBuffer = AudioBuffer::from_mut_slice(&mut input); +/// let output_buf: &AudioBuffer = AudioBuffer::from_mut_slice(&mut output); +/// +/// double_input(input_buf, output_buf); +/// assert_eq!(output, [0.0, 2.0, 4.0, 6.0]); +/// +/// // Processes the data in-place with a single buffer for both input and output. +/// double_input(input_buf, input_buf); +/// assert_eq!(input, [0.0, 2.0, 4.0, 6.0]); // Input buffer has been modified. +/// +/// ``` +/// +/// [`get`]: AudioBuffer::get +/// [`try_get`]: AudioBuffer::try_get +/// [`get_unchecked`]: AudioBuffer::get_unchecked +/// +/// [`put`]: AudioBuffer::put +/// [`put_unchecked`]: AudioBuffer::put_unchecked +/// +/// [`copy_from_buffer`]: AudioBuffer::copy_from_buffer +/// [`copy_to_buffer`]: AudioBuffer::copy_to_buffer +/// [`copy_from_slice`]: AudioBuffer::copy_from_slice +/// [`copy_to_slice`]: AudioBuffer::copy_to_slice +/// [`fill`]: AudioBuffer::fill +#[repr(transparent)] +pub struct AudioBuffer { + inner: [Cell], +} + +impl AudioBuffer { + /// Creates a reference to an empty buffer. + /// + /// # Example + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// + /// let buf: &AudioBuffer = AudioBuffer::empty(); + /// + /// assert_eq!(buf.len(), 0) + /// ``` + #[inline] + #[must_use] + pub const fn empty() -> &'static Self { + Self::from_slice_of_cells(&[]) + } + + /// Creates a buffer reference from a mutable reference to a slice of samples. + /// + /// If you do not have exclusive (`&mut`) access to the sample data, you may consider using + /// [`AudioBuffer::from_slice_of_cells`] instead. + /// + /// # Example + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// + /// let mut data = [0.0, 1.0, 2.0]; + /// + /// let buf: &AudioBuffer = AudioBuffer::from_mut_slice(&mut data); + /// + /// assert_eq!(buf.len(), 3) + /// ``` + #[inline] + pub fn from_mut_slice(slice: &mut [S]) -> &Self { + Self::from_slice_of_cells(Cell::from_mut(slice).as_slice_of_cells()) + } + + /// Creates a buffer reference from a shared reference to a slice of cells of samples. + /// + /// This is an alternative to [`AudioBuffer::from_mut_slice`] that allows to obtain an + /// [`AudioBuffer`] reference in case you do not have exclusive (`&mut`) access to the sample + /// data. + /// + /// # Example + /// + /// ``` + /// use std::cell::Cell; + /// use clack_plugin::prelude::AudioBuffer; + /// + /// // This function does *not* have &mut access to the data. + /// fn use_shared_buffer(data: &[Cell]) { + /// let buf = AudioBuffer::from_slice_of_cells(data); + /// buf.fill(42.0) + /// } + /// + /// let mut data = [0.0f32, 1.0, 2.0]; + /// let shareable: &[Cell] = Cell::from_mut(&mut data[..]).as_slice_of_cells(); + /// + /// // Use or store a copy of the reference to the data. + /// let shared_copy: &[Cell] = shareable; + /// + /// use_shared_buffer(shareable); + /// + /// assert_eq!(data, [42.0; 3]) + /// ``` + #[inline] + #[must_use] + pub const fn from_slice_of_cells(slice: &[Cell]) -> &Self { + // SAFETY: This type is repr(transparent), so the two types have the same memory layout + unsafe { &*(slice as *const [Cell] as *const Self) } + } + + /// Forms a slice from a pointer and a length. + /// + /// The `len` argument is the number of *samples*, not the number of bytes. + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `data` must be [valid] for both reads *and* writes for `len * mem::size_of::()` many bytes, + /// and it must be properly aligned. This means in particular: + /// + /// * The entire memory range of this slice must be contained within a single allocated object! + /// Slices can never span across multiple allocated objects. See [below](#incorrect-usage) + /// for an example incorrectly not taking this into account. + /// * `data` must be non-null and aligned even for zero-length slices. One + /// reason for this is that enum layout optimizations may rely on references + /// (including slices of any length) being aligned and non-null to distinguish + /// them from other data. You can obtain a pointer that is usable as `data` + /// for zero-length slices using [`NonNull::dangling()`]. + /// + /// * `data` must point to `len` consecutive properly initialized values of type `T`. + /// + /// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`, + /// and adding that size to `data` must not "wrap around" the address space. + /// See the safety documentation of [`pointer::offset`]. + /// + /// Note that unlike with the similar [`slice::from_raw_parts`] function, the memory referenced + /// by the returned buffer *can* be mutated during the lifetime `'a`. + /// + /// # Caveat + /// + /// The lifetime for the returned slice is inferred from its usage. To + /// prevent accidental misuse, it's suggested to tie the lifetime to whichever + /// source lifetime is safe in the context, such as by providing a helper + /// function taking the lifetime of a host value for the slice, or by explicit + /// annotation. + /// + /// [valid]: ptr#safety + /// [`pointer::offset`]: ptr::NonNull::offset + /// [`NonNull::dangling()`]: ptr::NonNull::dangling + /// [`slice::from_raw_parts`]: core::slice::from_raw_parts + #[inline] + pub unsafe fn from_raw_parts<'a>(ptr: *mut S, len: usize) -> &'a Self { + if ptr.is_null() { + null_audio_buffer() + }; + + Self::from_slice_of_cells(core::slice::from_raw_parts(ptr.cast(), len)) + } + + /// Returns the number of samples in the buffer. + #[inline] + #[must_use] + pub const fn len(&self) -> usize { + self.inner.len() + } + + /// Returns `true` if the buffer is empty (i.e. its [`len`] is `0`), `false` otherwise. + /// + /// [`len`]: AudioBuffer::len + #[inline] + pub const fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Returns a raw pointer to the sample buffer. + /// + /// The resulting pointer can be used for both reading and writing sample data from/to the buffer. + /// + /// The caller must ensure that the slice outlives the pointer this function returns, or else + /// it will end up dangling. + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// + /// let mut data = [1.0, 2.0, 3.0]; + /// let buf: &AudioBuffer = AudioBuffer::from_mut_slice(&mut data); + /// + /// let buf_ptr = buf.as_ptr(); + /// + /// unsafe { + /// for i in 0..buf.len() { + /// assert_eq!(buf.get(i), *buf_ptr.add(i)); // Read + /// buf_ptr.add(i).write(42.0); // Write + /// } + /// } + /// + /// assert_eq!(buf, &[42.0; 3]) + /// ``` + #[inline] + pub const fn as_ptr(&self) -> *mut S { + self.inner.as_ptr().cast_mut().cast() + } + + /// Returns this buffer as a slice of cells. + #[inline] + pub fn as_slice_of_cells(&self) -> &[Cell] { + &self.inner + } + + /// Returns an iterator over the buffer. + /// + /// The iterator yields reference to [`Cell`s](Cell) of the samples, which allows for both + /// reading and writing the samples. + /// + /// # Example + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// let mut data = [1.0; 3]; + /// + /// let buf: &AudioBuffer = AudioBuffer::from_mut_slice(&mut data); + /// + /// for sample in buf { // sample is &Cell + /// assert_eq!(sample.get(), 1.0); + /// sample.set(42.0); + /// } + /// + /// assert_eq!(data, [42.0; 3]) + /// + /// ``` + #[inline] + pub fn iter(&self) -> AudioBufferIter { + AudioBufferIter { + inner: self.inner.iter(), + } + } + + #[inline] + fn re_slice], Output = [Cell]>>(&self, index: I) -> &Self { + AudioBuffer::from_slice_of_cells(self.inner.get(index).unwrap_or(&[])) + } +} + +impl AudioBuffer { + /// Returns the value of the sample at the given `index` in the buffer. + /// + /// # Panics + /// + /// This method will panic if `index` is out of bounds. + /// See [`try_get`](Self::try_get) for a non-panicking version of this method. + /// + /// # Example + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// + /// let mut data = [0.0, 1.0, 2.0]; + /// let buf = AudioBuffer::from_mut_slice(&mut data); + /// + /// assert_eq!(buf.get(0), 0.0); + /// assert_eq!(buf.get(1), 1.0); + /// assert_eq!(buf.get(2), 2.0); + /// ``` + #[inline] + #[must_use] + pub fn get(&self, index: usize) -> S { + self.inner[index].get() + } + + /// Writes the given sample `value` at the given `index` in the buffer. + /// + /// # Panics + /// + /// This method will panic if `index` is out of bounds. + /// + /// # Example + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// + /// let mut data = [0.0, 1.0, 2.0]; + /// let buf = AudioBuffer::from_mut_slice(&mut data); + /// + /// buf.put(1, 42.0); + /// + /// assert_eq!(data, [0.0, 42.0, 2.0]); + /// ``` + #[inline] + pub fn put(&self, index: usize, value: S) { + self.inner[index].set(value) + } + + /// Returns the value of the sample at the given `index` in the buffer, or `None` if `index` is + /// out of bounds. + /// + /// # Example + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// + /// let mut data = [0.0, 1.0, 2.0]; + /// let buf = AudioBuffer::from_mut_slice(&mut data); + /// + /// assert_eq!(buf.try_get(0), Some(0.0)); + /// assert_eq!(buf.try_get(1), Some(1.0)); + /// assert_eq!(buf.try_get(2), Some(2.0)); + /// assert_eq!(buf.try_get(3), None); + /// ``` + #[inline] + #[must_use] + pub fn try_get(&self, index: usize) -> Option { + Some(self.inner.get(index)?.get()) + } + + /// Returns the value of the sample at the given `index` in the buffer, without doing bounds + /// checking. + /// + /// For a safe alternative, see [`get`](Self::get). + /// + /// # Safety + /// + /// Calling this method with an out-of-bounds index is *undefined behavior*. + #[inline] + #[must_use] + pub unsafe fn get_unchecked(&self, index: usize) -> S { + self.inner.get_unchecked(index).get() + } + + /// Writes the given sample `value` at the given `index` in the buffer, without doing bounds + /// checking. + /// + /// For a safe alternative, see [`put`](Self::put). + /// + /// # Safety + /// + /// Calling this method with an out-of-bounds index is *undefined behavior*. + #[inline] + pub unsafe fn put_unchecked(&self, index: usize, value: S) { + self.inner.get_unchecked(index).set(value) + } + + /// Copies all the samples in this buffer into the given `buf` slice. + /// + /// # Panics + /// + /// This function will panic if the buffer and the `buf` slice have different lengths. + #[inline] + pub fn copy_to_slice(&self, buf: &mut [S]) { + if buf.len() != self.len() { + slice_len_mismatch(self.len(), buf.len()) + } + + // SAFETY: buf is guaranteed to be valid for writes, and this type guarantees the buffer + // is valid for both reads and writes. + // Buf was checked above to have the same length as this buffer. + // Both are guaranteed to be properly aligned, since they are slices already. + // Buf cannot overlap with this buffer, as it is behind an exclusive mutable reference. + unsafe { ptr::copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } + } + + /// Copies all the samples in this buffer into a different buffer. + /// + /// # Panics + /// + /// This function will panic if the two buffers have different lengths. + #[inline] + pub fn copy_to_buffer(&self, buf: &AudioBuffer) { + buf.copy_from_buffer(self) + } + + /// Copies all the samples in the given `buf` slice into this buffer. + /// + /// # Panics + /// + /// This function will panic if the buffer and the `buf` slice have different lengths. + #[inline] + pub fn copy_from_slice(&self, buf: &[S]) { + if buf.len() != self.len() { + slice_len_mismatch(buf.len(), self.len()) + } + + // SAFETY: buf is guaranteed to be valid for reads, and this type guarantees the buffer + // is valid for both reads and writes. + // Buf was checked above to have the same length as this buffer. + // Both are guaranteed to be properly aligned, since they are slices already. + // Buf cannot overlap with this buffer, as it is behind a shared immutable reference. + unsafe { ptr::copy_nonoverlapping(buf.as_ptr(), self.as_ptr(), buf.len()) } + } + + /// Copies all the samples from another buffer into this buffer. + /// + /// # Panics + /// + /// This function will panic if the two buffers have different lengths. + #[inline] + pub fn copy_from_buffer(&self, buf: &AudioBuffer) { + if buf.len() != self.len() { + slice_len_mismatch(buf.len(), self.len()) + } + + // SAFETY: This type guarantees the buffer are aligned and valid for both reads and writes. + // Buf was checked above to have the same length as this buffer. + unsafe { ptr::copy(buf.as_ptr(), self.as_ptr(), buf.len()) } + } + + /// Fills the buffer with the given value. + /// + /// # Example + /// + /// ``` + /// use clack_plugin::prelude::AudioBuffer; + /// + /// let mut data = [0.0, 1.0, 2.0]; + /// let buf = AudioBuffer::from_mut_slice(&mut data); + /// + /// buf.fill(42.0); + /// + /// assert_eq!(data, [42.0; 3]); + /// ``` + #[inline] + pub fn fill(&self, value: S) { + for i in &self.inner { + i.set(value) + } + } +} + +/// An iterator over the samples in an [`AudioBuffer`]. +#[derive(Clone)] +pub struct AudioBufferIter<'a, S> { + inner: core::slice::Iter<'a, Cell>, +} + +impl<'a, S> Iterator for AudioBufferIter<'a, S> { + type Item = &'a Cell; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next() + } +} + +impl<'a, S> IntoIterator for &'a AudioBuffer { + type Item = &'a Cell; + type IntoIter = AudioBufferIter<'a, S>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl Index for AudioBuffer { + type Output = Cell; + + #[inline] + fn index(&self, index: usize) -> &Self::Output { + &self.inner[index] + } +} +impl Index for AudioBuffer { + type Output = Self; + + #[inline] + fn index(&self, index: core::ops::RangeFull) -> &Self::Output { + self.re_slice(index) + } +} +impl Index> for AudioBuffer { + type Output = Self; + + #[inline] + fn index(&self, index: core::ops::Range) -> &Self::Output { + self.re_slice(index) + } +} +impl Index> for AudioBuffer { + type Output = Self; + + #[inline] + fn index(&self, index: core::ops::RangeFrom) -> &Self::Output { + self.re_slice(index) + } +} +impl Index> for AudioBuffer { + type Output = Self; + + #[inline] + fn index(&self, index: core::ops::RangeTo) -> &Self::Output { + self.re_slice(index) + } +} +impl Index> for AudioBuffer { + type Output = Self; + + #[inline] + fn index(&self, index: core::ops::RangeInclusive) -> &Self::Output { + self.re_slice(index) + } +} +impl Index> for AudioBuffer { + type Output = Self; + + #[inline] + fn index(&self, index: core::ops::RangeToInclusive) -> &Self::Output { + self.re_slice(index) + } +} +impl Index<(core::ops::Bound, core::ops::Bound)> for AudioBuffer { + type Output = Self; + + #[inline] + fn index(&self, index: (core::ops::Bound, core::ops::Bound)) -> &Self::Output { + self.re_slice(index) + } +} + +impl Debug for AudioBuffer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut dbg = f.debug_list(); + for s in self { + dbg.entry(&s.get()); + } + dbg.finish() + } +} + +impl PartialEq for AudioBuffer { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.inner.eq(&other.inner) + } +} + +impl PartialEq<[S]> for AudioBuffer { + fn eq(&self, other: &[S]) -> bool { + if self.len() != other.len() { + return false; + } + + for (a, b) in self.iter().zip(other) { + if a.get() != *b { + return false; + } + } + + true + } +} + +impl PartialEq<[S; N]> for AudioBuffer { + #[inline] + fn eq(&self, other: &[S; N]) -> bool { + PartialEq::<[S]>::eq(self, other) + } +} + +impl PartialEq<&[S]> for AudioBuffer { + #[inline] + fn eq(&self, other: &&[S]) -> bool { + PartialEq::<[S]>::eq(self, other) + } +} + +impl PartialEq<&[S; N]> for AudioBuffer { + #[inline] + fn eq(&self, other: &&[S; N]) -> bool { + PartialEq::<[S]>::eq(self, *other) + } +} + +impl<'a, S> From<&'a mut [S]> for &'a AudioBuffer { + #[inline] + fn from(value: &mut [S]) -> &AudioBuffer { + AudioBuffer::from_mut_slice(value) + } +} + +impl<'a, S> From<&'a [Cell]> for &'a AudioBuffer { + #[inline] + fn from(value: &[Cell]) -> &AudioBuffer { + AudioBuffer::from_slice_of_cells(value) + } +} + +#[cold] +#[inline(never)] +fn null_audio_buffer() -> ! { + panic!("Invalid audio buffer: buffer pointer is NULL.") +} + +#[cold] +#[inline(never)] +fn slice_len_mismatch(src_len: usize, dst_len: usize) -> ! { + panic!( + "Buffer size mismatch: source has length {}, but destination has length {}", + src_len, dst_len + ) +} diff --git a/plugin/src/process/audio/error.rs b/plugin/src/process/audio/error.rs index 08144665..3a5bfaa4 100644 --- a/plugin/src/process/audio/error.rs +++ b/plugin/src/process/audio/error.rs @@ -8,8 +8,7 @@ pub enum BufferError { /// /// This happens when both the [`f32`] and [`f64`] buffer pointers provided by the host are null. /// - /// This error can be returned by the [`InputPort::channels`](super::InputPort::channels), - /// [`OutputPort::channels`](super::OutputPort::channels), or + /// This error can be returned by the [`InputPort::channels`](super::Port::channels) or /// [`PortPair::channels`](super::PortPair::channels) methods. InvalidChannelBuffer, /// A pair of mismatched buffer types (i.e. one [`f32`] and the other [`f64`]) were tried to diff --git a/plugin/src/process/audio/output.rs b/plugin/src/process/audio/output.rs deleted file mode 100644 index 9cbe310c..00000000 --- a/plugin/src/process/audio/output.rs +++ /dev/null @@ -1,325 +0,0 @@ -use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut}; -use crate::prelude::Audio; -use crate::process::audio::{BufferError, SampleType}; -use crate::process::InputChannelsIter; -use clack_common::process::ConstantMask; -use clap_sys::audio_buffer::clap_audio_buffer; -use std::slice::IterMut; - -/// An iterator of all the available [`OutputPort`]s from an [`Audio`] struct. -pub struct OutputPortsIter<'a> { - outputs: IterMut<'a, clap_audio_buffer>, - frames_count: u32, -} - -impl<'a> OutputPortsIter<'a> { - #[inline] - pub(crate) fn new(audio: &'a mut Audio<'_>) -> Self { - Self { - outputs: audio.outputs.iter_mut(), - frames_count: audio.frames_count, - } - } -} - -impl<'a> Iterator for OutputPortsIter<'a> { - type Item = OutputPort<'a>; - - #[inline] - fn next(&mut self) -> Option { - self.outputs - .next() - // SAFETY: The Audio type this is built from ensures each buffer is valid - // and is of length frames_count. - .map(|buf| unsafe { OutputPort::from_raw(buf, self.frames_count) }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.outputs.size_hint() - } -} - -impl ExactSizeIterator for OutputPortsIter<'_> { - #[inline] - fn len(&self) -> usize { - self.outputs.len() - } -} - -/// An output audio port. -pub struct OutputPort<'a> { - inner: &'a mut clap_audio_buffer, - frames_count: u32, -} - -impl<'a> OutputPort<'a> { - /// # Safety - /// - /// * The provided buffer must be valid; - /// * `frames_count` *must* match the size of the buffers. - #[inline] - pub(crate) unsafe fn from_raw(inner: &'a mut clap_audio_buffer, frames_count: u32) -> Self { - Self { - inner, - frames_count, - } - } - - /// Retrieves the output port's channels. - /// - /// Because each port can hold either [`f32`] or [`f64`] sample data, this method returns a - /// [`SampleType`] enum of the input channels, to indicate which one the port holds. - /// - /// # Errors - /// - /// This method returns a [`BufferError::InvalidChannelBuffer`] if the host provided neither - /// [`f32`] or [`f64`] buffer type, which is invalid per the CLAP specification. - /// - /// # Example - /// - /// ``` - /// use clack_plugin::process::audio::{OutputChannels, OutputPort, SampleType}; - /// - /// # fn foo(port: OutputPort) { - /// let mut port: OutputPort = /* ... */ - /// # port; - /// - /// // Decide what to do using by matching against every possible configuration - /// match port.channels().unwrap() { - /// SampleType::F32(buf) => { /* Process the 32-bit buffer */ }, - /// SampleType::F64(buf) => { /* Process the 64-bit buffer */ }, - /// SampleType::Both(buf32, buf64) => { /* We have both buffers available */ } - /// } - /// - /// // If we're only interested in a single buffer type, - /// // we can use SampleType's helper methods: - /// let channels: OutputChannels = port.channels().unwrap().into_f32().unwrap(); - /// # } - /// ``` - #[inline] - pub fn channels( - &mut self, - ) -> Result, OutputChannels<'a, f64>>, BufferError> { - // SAFETY: this type ensures the buffer is valid - Ok(unsafe { SampleType::from_raw_buffer_mut(self.inner) }?.map( - |data| OutputChannels { - data, - frames_count: self.frames_count, - }, - |data| OutputChannels { - data, - frames_count: self.frames_count, - }, - )) - } - - /// Returns the number of frames to process in this block. - /// - /// This will always match the number of samples of every audio channel buffer. - #[inline] - pub fn frames_count(&self) -> u32 { - self.frames_count - } - - /// The number of channels in this port. - #[inline] - pub fn channel_count(&self) -> u32 { - self.inner.channel_count - } - - /// The latency from the audio interface for this port, in samples. - #[inline] - pub fn latency(&self) -> u32 { - self.inner.latency - } - - /// The constant mask for this port. - #[inline] - pub fn constant_mask(&self) -> ConstantMask { - ConstantMask::from_bits(self.inner.constant_mask) - } - - /// Sets the constant mask for this port. - #[inline] - pub fn set_constant_mask(&mut self, new_mask: ConstantMask) { - self.inner.constant_mask = new_mask.to_bits() - } -} - -/// An [`OutputPort`]'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 -/// [`OutputPort::channels`]. -pub struct OutputChannels<'a, S> { - pub(crate) frames_count: u32, - pub(crate) data: &'a mut [*mut S], -} - -impl OutputChannels<'_, S> { - /// Returns the number of frames to process in this block. - /// - /// This will always match the number of samples of every audio channel buffer. - #[inline] - pub fn frames_count(&self) -> u32 { - self.frames_count - } - - /// Returns the raw pointer data, as provided by the host. - /// - /// In CLAP's API, hosts provide a port's audio data as an array of raw pointers, each of which points - /// to the start of a sample array of type `S` and of [`frames_count`](Self::frames_count) length. - #[inline] - pub fn raw_data(&self) -> &[*mut S] { - self.data - } - - /// The number of channels. - #[inline] - pub fn channel_count(&self) -> u32 { - self.data.len() as u32 - } - - /// Retrieves the sample buffer of the channel at a given index, as a read-only slice. - #[inline] - pub fn channel(&self, channel_index: u32) -> Option<&[S]> { - // SAFETY: this type enforces that the buffer is valid, and has length frames_count. - unsafe { - self.data.get(channel_index as usize).map(|data| { - slice_from_external_parts(*data as *const _, self.frames_count as usize) - }) - } - } - - /// Retrieves the sample buffer of the channel at a given index, as a mutable slice. - #[inline] - pub fn channel_mut(&mut self, channel_index: u32) -> Option<&mut [S]> { - // SAFETY: this type enforces that the buffer is valid, has length frames_count, and has - // exclusive access. - unsafe { - self.data.get(channel_index as usize).map(|data| { - slice_from_external_parts_mut(*data as *mut _, self.frames_count as usize) - }) - } - } - - /// Gets a read-only iterator over all the port's channels' sample buffers. - #[inline] - pub fn iter(&self) -> InputChannelsIter { - InputChannelsIter { - data: self.data.iter(), - frames_count: self.frames_count, - } - } - - /// Gets an iterator over all the port's channels' writable sample buffers. - #[inline] - pub fn iter_mut(&mut self) -> OutputChannelsIter { - OutputChannelsIter { - data: self.data.as_mut().iter_mut(), - frames_count: self.frames_count, - } - } - - /// Divides the output channels into two at an index. - /// - /// The first will contain all channels with indices from `[0, mid)` (excluding - /// the index `mid` itself) and the second will contain all channels with - /// indices from `[mid, channel_count)`. - /// - /// Unlike [`slice::split_at_mut`], this method does not panic if - /// `mid` is larger than `channel_count`. - /// The second [`OutputChannels`] will only be empty in this case. - #[inline] - pub fn split_at_mut(&mut self, mid: u32) -> (OutputChannels, OutputChannels) { - let mid = mid as usize; - if mid >= self.data.len() { - return ( - OutputChannels { - data: self.data, - frames_count: self.frames_count, - }, - OutputChannels { - data: &mut [], - frames_count: self.frames_count, - }, - ); - } - // PANIC: Checked that mid < len above - let (left, right) = self.data.split_at_mut(mid); - - ( - OutputChannels { - data: left, - frames_count: self.frames_count, - }, - OutputChannels { - data: right, - frames_count: self.frames_count, - }, - ) - } -} -impl<'a, T> IntoIterator for &'a OutputChannels<'a, T> { - type Item = &'a [T]; - type IntoIter = InputChannelsIter<'a, T>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl<'a, T> IntoIterator for &'a mut OutputChannels<'a, T> { - type Item = &'a mut [T]; - type IntoIter = OutputChannelsIter<'a, T>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter_mut() - } -} - -impl<'a, T> IntoIterator for OutputChannels<'a, T> { - type Item = &'a mut [T]; - type IntoIter = OutputChannelsIter<'a, T>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - OutputChannelsIter { - data: self.data.as_mut().iter_mut(), - frames_count: self.frames_count, - } - } -} - -/// An iterator over all of an [`OutputPort`]'s channels' writable sample buffers. -pub struct OutputChannelsIter<'a, T> { - data: IterMut<'a, *mut T>, - frames_count: u32, -} - -impl<'a, T> Iterator for OutputChannelsIter<'a, T> { - type Item = &'a mut [T]; - - #[inline] - fn next(&mut self) -> Option { - // SAFETY: iterator can only get created from an OutputChannels, which guarantees - // the buffer is both valid and of length frames_count - self.data.next().map(|ptr| unsafe { - slice_from_external_parts_mut(*ptr as *mut _, self.frames_count as usize) - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.data.size_hint() - } -} - -impl ExactSizeIterator for OutputChannelsIter<'_, S> { - #[inline] - fn len(&self) -> usize { - self.data.len() - } -} diff --git a/plugin/src/process/audio/pair.rs b/plugin/src/process/audio/pair.rs index 57a7016c..eb277bdc 100644 --- a/plugin/src/process/audio/pair.rs +++ b/plugin/src/process/audio/pair.rs @@ -1,10 +1,9 @@ -use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut}; use crate::process::audio::pair::ChannelPair::*; -use crate::process::audio::{BufferError, InputPort, OutputPort, SampleType}; +use crate::process::audio::{ + AudioBuffer, BufferError, CelledClapAudioBuffer, Channels, Port, SampleType, +}; use crate::process::Audio; -use clack_common::process::{AudioPortProcessingInfo, ConstantMask}; -use clap_sys::audio_buffer::clap_audio_buffer; -use std::slice::{Iter, IterMut}; +use std::slice::Iter; /// A pair of Input and Output ports. /// @@ -14,9 +13,10 @@ use std::slice::{Iter, IterMut}; /// /// 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. +#[derive(Copy, Clone)] pub struct PortPair<'a> { - input: Option<&'a clap_audio_buffer>, - output: Option<&'a mut clap_audio_buffer>, + input: Option<&'a CelledClapAudioBuffer>, + output: Option<&'a CelledClapAudioBuffer>, frames_count: u32, } @@ -27,8 +27,8 @@ impl<'a> PortPair<'a> { /// * `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>, + input: Option<&'a CelledClapAudioBuffer>, + output: Option<&'a CelledClapAudioBuffer>, frames_count: u32, ) -> Option { match (input, output) { @@ -41,45 +41,24 @@ impl<'a> PortPair<'a> { } } - /// Gets the [`InputPort`] of this pair. + /// Gets the input [`Port`] of this pair. /// /// If the port layout is asymmetric and there is no input port, this returns [`None`]. #[inline] - pub fn input(&self) -> Option { + pub fn input(&self) -> Option> { self.input // SAFETY: this type ensures the buffer is valid and matches frame_count - .map(|i| unsafe { InputPort::from_raw(i, self.frames_count) }) + .map(|i| unsafe { Port::from_raw(i, self.frames_count) }) } - /// Gets the [`OutputPort`] of this pair. + /// Gets the output [`Port`] 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 { + pub fn output(&self) -> Option> { 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 { - 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 { - self.output - .as_ref() - .map(|buf| AudioPortProcessingInfo::from_raw(buf)) + .map(|i| unsafe { Port::from_raw(i, self.frames_count) }) } /// Retrieves the port pair's channels. @@ -98,7 +77,7 @@ impl<'a> PortPair<'a> { /// # Example /// /// ``` - /// use clack_plugin::process::audio::{PairedChannels, PortPair, SampleType}; + /// use clack_plugin::process::audio::{ChannelsPairs, PortPair, SampleType}; /// /// # fn foo(port: PortPair) { /// let mut port: PortPair = /* ... */ @@ -113,34 +92,34 @@ impl<'a> PortPair<'a> { /// /// // If we're only interested in a single buffer type, /// // we can use SampleType's helper methods: - /// let channels: PairedChannels = port.channels().unwrap().into_f32().unwrap(); + /// let channels: ChannelsPairs = port.channels().unwrap().to_f32().unwrap(); /// # } /// ``` #[inline] pub fn channels( - &mut self, - ) -> Result, PairedChannels<'a, f64>>, BufferError> { + &self, + ) -> Result, ChannelsPairs<'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()), + let output = match self.output { + None => SampleType::Both([].as_slice(), [].as_slice()), // SAFETY: this type ensures the buffer is valid - Some(buffer) => unsafe { SampleType::from_raw_buffer_mut(buffer)? }, + Some(buffer) => unsafe { SampleType::from_raw_buffer(buffer)? }, }; Ok(input.try_match_with(output)?.map( - |(i, o)| PairedChannels { - input_data: i, - output_data: o, + |(i, o)| ChannelsPairs { + inputs: i, + outputs: o, frames_count: self.frames_count, }, - |(i, o)| PairedChannels { - input_data: i, - output_data: o, + |(i, o)| ChannelsPairs { + inputs: i, + outputs: o, frames_count: self.frames_count, }, )) @@ -167,52 +146,45 @@ impl<'a> PortPair<'a> { 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, Option) { - ( - 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], +pub struct ChannelsPairs<'a, S> { + inputs: &'a [*mut S], + outputs: &'a [*mut S], frames_count: u32, } -impl<'a, S> PairedChannels<'a, S> { +impl<'a, S> ChannelsPairs<'a, S> { + /// Creates a new pair of [`Channels`] list from an input and output channels list. + /// + /// # Panics + /// + /// This function will panic if `inputs` and `outputs` don't have the same `frame_count`. + pub fn from_channels(inputs: Channels<'a, S>, outputs: Channels<'a, S>) -> Self { + if inputs.frames_count() != outputs.frames_count() { + mismatched_frames_count(inputs.frames_count(), outputs.frames_count()) + } + + #[inline(never)] + #[cold] + fn mismatched_frames_count(input_frames: u32, output_frames: u32) -> ! { + panic!( + "Tried to pair two channels with different frame counts (input: {}, output: {}.", + input_frames, output_frames + ) + } + + Self { + inputs: inputs.raw_data(), + outputs: outputs.raw_data(), + frames_count: inputs.frames_count(), + } + } + /// 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 @@ -225,13 +197,13 @@ impl<'a, S> PairedChannels<'a, S> { /// The number of input channels. #[inline] pub fn input_channel_count(&self) -> usize { - self.input_data.len() + self.inputs.len() } /// The number of output channels. #[inline] pub fn output_channel_count(&self) -> usize { - self.output_data.len() + self.outputs.len() } /// The total number of channel pairs. @@ -244,6 +216,18 @@ impl<'a, S> PairedChannels<'a, S> { self.input_channel_count().max(self.output_channel_count()) } + /// Returns the input channels. + pub fn input_channels(&self) -> Channels<'a, S> { + // SAFETY: The input_data and frames_count fields are guaranteed to be valid + unsafe { Channels::from_raw(self.inputs, self.frames_count) } + } + + /// Returns the output channels. + pub fn output_channels(&self) -> Channels<'a, S> { + // SAFETY: The input_data and frames_count fields are guaranteed to be valid + unsafe { Channels::from_raw(self.outputs, self.frames_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 @@ -251,55 +235,68 @@ impl<'a, S> PairedChannels<'a, S> { /// /// See [`ChannelPair`]'s documentation for examples on how to access sample buffers from it. #[inline] - pub fn channel_pair(&mut self, index: usize) -> Option> { + pub fn channel_pair(&self, index: usize) -> Option> { let input = self - .input_data + .inputs .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) }); + .map(|ptr| unsafe { AudioBuffer::from_raw_parts(*ptr, self.frames_count as usize) }); let output = self - .output_data + .outputs .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) }); + .map(|ptr| unsafe { AudioBuffer::from_raw_parts(*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 { - PairedChannelsIter { - input_iter: self.input_data.iter(), - output_iter: self.output_data.iter_mut(), + pub fn iter(&self) -> ChannelsPairsIter<'a, S> { + ChannelsPairsIter { + input_iter: self.inputs.iter(), + output_iter: self.outputs.iter(), frames_count: self.frames_count, } } } -impl<'a, S> IntoIterator for PairedChannels<'a, S> { +impl<'a, S> Copy for ChannelsPairs<'a, S> {} +impl<'a, S> Clone for ChannelsPairs<'a, S> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, S> IntoIterator for ChannelsPairs<'a, S> { type Item = ChannelPair<'a, S>; - type IntoIter = PairedChannelsIter<'a, S>; + type IntoIter = ChannelsPairsIter<'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, - } + self.iter() + } +} + +impl<'a, S> IntoIterator for &ChannelsPairs<'a, S> { + type Item = ChannelPair<'a, S>; + type IntoIter = ChannelsPairsIter<'a, S>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() } } /// An iterator over all of a [`PortPair`]'s [`ChannelPair`]s. -pub struct PairedChannelsIter<'a, S> { +pub struct ChannelsPairsIter<'a, S> { input_iter: Iter<'a, *mut S>, - output_iter: IterMut<'a, *mut S>, + output_iter: Iter<'a, *mut S>, frames_count: u32, } -impl<'a, S> Iterator for PairedChannelsIter<'a, S> { +impl<'a, S> Iterator for ChannelsPairsIter<'a, S> { type Item = ChannelPair<'a, S>; #[inline] @@ -308,11 +305,11 @@ impl<'a, S> Iterator for PairedChannelsIter<'a, S> { .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) }); + .map(|ptr| unsafe { AudioBuffer::from_raw_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) + AudioBuffer::from_raw_parts((*ptr) as *mut _, self.frames_count as usize) }); ChannelPair::from_optional_io(input, output) @@ -324,26 +321,38 @@ impl<'a, S> Iterator for PairedChannelsIter<'a, S> { } } -impl ExactSizeIterator for PairedChannelsIter<'_, S> { +impl ExactSizeIterator for ChannelsPairsIter<'_, S> { #[inline] fn len(&self) -> usize { self.input_iter.len().max(self.output_iter.len()) } } +impl<'a, S> Clone for ChannelsPairsIter<'a, S> { + #[inline] + fn clone(&self) -> Self { + Self { + input_iter: self.input_iter.clone(), + output_iter: self.output_iter.clone(), + frames_count: self.frames_count, + } + } +} + /// An iterator of all of the available [`PortPair`]s from an [`Audio`] struct. +#[derive(Clone)] pub struct PortPairsIter<'a> { - inputs: Iter<'a, clap_audio_buffer>, - outputs: IterMut<'a, clap_audio_buffer>, + inputs: Iter<'a, CelledClapAudioBuffer>, + outputs: Iter<'a, CelledClapAudioBuffer>, frames_count: u32, } impl<'a> PortPairsIter<'a> { #[inline] - pub(crate) fn new(audio: &'a mut Audio<'_>) -> Self { + pub(crate) fn new(audio: &Audio<'a>) -> Self { Self { inputs: audio.inputs.iter(), - outputs: audio.outputs.iter_mut(), + outputs: audio.outputs.iter(), frames_count: audio.frames_count, } } @@ -378,25 +387,26 @@ impl ExactSizeIterator for PortPairsIter<'_> { /// /// 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. +#[derive(Copy, Clone)] pub enum ChannelPair<'a, S> { /// There is only an input channel present, there was no matching output. - InputOnly(&'a [S]), + InputOnly(&'a AudioBuffer), /// There is only an output channel present, there was no matching input. - OutputOnly(&'a mut [S]), + OutputOnly(&'a AudioBuffer), /// Both the input and output channels are present, and available separately. - InputOutput(&'a [S], &'a mut [S]), + InputOutput(&'a AudioBuffer, &'a AudioBuffer), /// 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]), + InPlace(&'a AudioBuffer), } impl<'a, S> ChannelPair<'a, S> { #[inline] pub(crate) fn from_optional_io( - input: Option<&'a [S]>, - output: Option<&'a mut [S]>, + input: Option<&'a AudioBuffer>, + output: Option<&'a AudioBuffer>, ) -> Option> { match (input, output) { (None, None) => None, @@ -412,30 +422,20 @@ impl<'a, S> ChannelPair<'a, S> { /// Attempts to retrieve the input channel's buffer data, if the input channel is present. #[inline] - pub fn input(&'a self) -> Option<&'a [S]> { + pub fn input(&self) -> Option<&'a AudioBuffer> { match self { - InputOnly(i) | InputOutput(i, _) => Some(i), + InputOnly(i) | InputOutput(i, _) => Some(*i), OutputOnly(_) => None, - InPlace(io) => Some(io), + 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]> { + pub fn output(&'a self) -> Option<&'a AudioBuffer> { match self { - OutputOnly(o) | InputOutput(_, o) | InPlace(o) => Some(o), + OutputOnly(o) | InputOutput(_, o) | InPlace(o) => Some(*o), InputOnly(_) => None, } } diff --git a/plugin/src/process/audio/input.rs b/plugin/src/process/audio/port.rs similarity index 55% rename from plugin/src/process/audio/input.rs rename to plugin/src/process/audio/port.rs index db590e1c..e018144e 100644 --- a/plugin/src/process/audio/input.rs +++ b/plugin/src/process/audio/port.rs @@ -1,28 +1,29 @@ -use crate::internal_utils::slice_from_external_parts; -use crate::prelude::Audio; -use crate::process::audio::{BufferError, SampleType}; +use crate::process::audio::{AudioBuffer, BufferError, CelledClapAudioBuffer, SampleType}; use clack_common::process::ConstantMask; -use clap_sys::audio_buffer::clap_audio_buffer; +use std::ops::Index; use std::slice::Iter; -/// An iterator of all the available [`InputPort`]s from an [`Audio`] struct. -pub struct InputPortsIter<'a> { - inputs: Iter<'a, clap_audio_buffer>, +/// An iterator of all the available [`Port`]s from an [`Audio`] struct. +/// +/// [`Audio`]: crate::process::Audio +#[derive(Clone)] +pub struct PortsIter<'a> { + inputs: Iter<'a, CelledClapAudioBuffer>, frames_count: u32, } -impl<'a> InputPortsIter<'a> { +impl<'a> PortsIter<'a> { #[inline] - pub(crate) fn new(audio: &Audio<'a>) -> Self { + pub(crate) fn new(ports: &'a [CelledClapAudioBuffer], frames_count: u32) -> Self { Self { - inputs: audio.inputs.iter(), - frames_count: audio.frames_count, + inputs: ports.iter(), + frames_count, } } } -impl<'a> Iterator for InputPortsIter<'a> { - type Item = InputPort<'a>; +impl<'a> Iterator for PortsIter<'a> { + type Item = Port<'a>; #[inline] fn next(&mut self) -> Option { @@ -30,7 +31,7 @@ impl<'a> Iterator for InputPortsIter<'a> { .next() // SAFETY: The Audio type this is built from ensures each buffer is valid // and is of length frames_count. - .map(|buf| unsafe { InputPort::from_raw(buf, self.frames_count) }) + .map(|buf| unsafe { Port::from_raw(buf, self.frames_count) }) } #[inline] @@ -39,27 +40,27 @@ impl<'a> Iterator for InputPortsIter<'a> { } } -impl ExactSizeIterator for InputPortsIter<'_> { +impl ExactSizeIterator for PortsIter<'_> { #[inline] fn len(&self) -> usize { self.inputs.len() } } -/// An input audio port. +/// An audio port. #[derive(Copy, Clone)] -pub struct InputPort<'a> { - inner: &'a clap_audio_buffer, +pub struct Port<'a> { + inner: &'a CelledClapAudioBuffer, frames_count: u32, } -impl<'a> InputPort<'a> { +impl<'a> Port<'a> { /// # Safety /// /// * The provided buffer must be valid; /// * `frames_count` *must* match the size of the buffers. #[inline] - pub(crate) unsafe fn from_raw(inner: &'a clap_audio_buffer, frames_count: u32) -> Self { + pub(crate) unsafe fn from_raw(inner: &'a CelledClapAudioBuffer, frames_count: u32) -> Self { Self { inner, frames_count, @@ -79,35 +80,35 @@ impl<'a> InputPort<'a> { /// # Example /// /// ``` - /// use clack_plugin::process::audio::{InputChannels, InputPort, SampleType}; + /// use clack_plugin::process::audio::{Channels, Port, SampleType}; /// - /// # fn foo(port: InputPort) { - /// let port: InputPort = /* ... */ + /// # fn foo(port: Port) { + /// let port: Port = /* ... */ /// # port; /// /// // Decide what to do using by matching against every possible configuration /// match port.channels().unwrap() { - /// SampleType::F32(buf) => { /* Process the 32-bit buffer */ }, - /// SampleType::F64(buf) => { /* Process the 64-bit buffer */ }, + /// SampleType::F32(buf) => { /* Process the 32-bit buffer */ } + /// SampleType::F64(buf) => { /* Process the 64-bit buffer */ } /// SampleType::Both(buf32, buf64) => { /* We have both buffers available */ } /// } /// /// // If we're only interested in a single buffer type, /// // we can use SampleType's helper methods: - /// let channels: InputChannels = port.channels().unwrap().into_f32().unwrap(); + /// let channels: Channels = port.channels().unwrap().to_f32().unwrap(); /// # } /// ``` #[inline] pub fn channels( &self, - ) -> Result, InputChannels<'a, f64>>, BufferError> { + ) -> Result, Channels<'a, f64>>, BufferError> { // SAFETY: this type ensures the provided buffer is valid Ok(unsafe { SampleType::from_raw_buffer(self.inner) }?.map( - |data| InputChannels { + |data| Channels { data, frames_count: self.frames_count, }, - |data| InputChannels { + |data| Channels { data, frames_count: self.frames_count, }, @@ -137,26 +138,39 @@ impl<'a> InputPort<'a> { /// The constant mask for this port. #[inline] pub fn constant_mask(&self) -> ConstantMask { - ConstantMask::from_bits(self.inner.constant_mask) + ConstantMask::from_bits(self.inner.constant_mask.get()) + } + + /// Sets the constant mask for this port. + #[inline] + pub fn set_constant_mask(&self, new_mask: ConstantMask) { + self.inner.constant_mask.set(new_mask.to_bits()) } } -/// An [`InputPort`]'s channels' data buffers, which contains samples of a given type `S`. +/// An [`Port`]'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 -/// [`InputPort::channels`]. -#[derive(Copy, Clone)] -pub struct InputChannels<'a, S> { +/// [`Port::channels`]. +pub struct Channels<'a, S> { frames_count: u32, data: &'a [*mut S], } -impl<'a, S> InputChannels<'a, S> { +impl<'a, S> Channels<'a, S> { + /// # Safety + /// Both data and the pointers in it must be valid for 'a. + /// Every pointer in data must point to a slice of size `frames_count`, and be valid for both reads and writes. + #[inline] + pub(crate) const unsafe fn from_raw(data: &'a [*mut S], frames_count: u32) -> Self { + Channels { data, frames_count } + } + /// Returns the number of frames to process in this block. /// /// This will always match the number of samples of every audio channel buffer. #[inline] - pub fn frames_count(&self) -> u32 { + pub const fn frames_count(&self) -> u32 { self.frames_count } @@ -165,13 +179,13 @@ impl<'a, S> InputChannels<'a, S> { /// In CLAP's API, hosts provide a port's audio data as an array of raw pointers, each of which points /// to the start of a sample array of type `S` and of [`frames_count`](Self::frames_count) length. #[inline] - pub fn raw_data(&self) -> &'a [*mut S] { + pub const fn raw_data(&self) -> &'a [*mut S] { self.data } /// The number of channels. #[inline] - pub fn channel_count(&self) -> u32 { + pub const fn channel_count(&self) -> u32 { self.data.len() as u32 } @@ -180,28 +194,46 @@ impl<'a, S> InputChannels<'a, S> { /// If there is no channel at the given index (i.e. `channel_index` is greater or equal than /// [`channel_count`](Self::channel_count)), this returns [`None`]. #[inline] - pub fn channel(&self, channel_index: u32) -> Option<&'a [S]> { + pub fn channel(&self, channel_index: u32) -> Option<&'a AudioBuffer> { // SAFETY: this type guarantees the buffer pointer is valid and of size frames_count unsafe { self.data .get(channel_index as usize) - .map(|data| slice_from_external_parts(*data, self.frames_count as usize)) + .map(|data| AudioBuffer::from_raw_parts(*data, self.frames_count as usize)) } } /// Gets an iterator over all the port's channels' sample buffers. #[inline] - pub fn iter(&self) -> InputChannelsIter<'a, S> { - InputChannelsIter { + pub fn iter(&self) -> ChannelsIter<'a, S> { + ChannelsIter { data: self.data.iter(), frames_count: self.frames_count, } } } -impl<'a, T> IntoIterator for InputChannels<'a, T> { - type Item = &'a [T]; - type IntoIter = InputChannelsIter<'a, T>; +impl<'a, S> Clone for Channels<'a, S> { + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl<'a, S> Copy for Channels<'a, S> {} + +impl<'a, S> Index for Channels<'a, S> { + type Output = AudioBuffer; + + fn index(&self, index: usize) -> &Self::Output { + // SAFETY: this type guarantees the buffer pointer is valid and of size frames_count + unsafe { AudioBuffer::from_raw_parts(self.data[index], self.frames_count as usize) } + } +} + +impl<'a, T> IntoIterator for Channels<'a, T> { + type Item = &'a AudioBuffer; + type IntoIter = ChannelsIter<'a, T>; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -209,9 +241,9 @@ impl<'a, T> IntoIterator for InputChannels<'a, T> { } } -impl<'a, T> IntoIterator for &'a InputChannels<'a, T> { - type Item = &'a [T]; - type IntoIter = InputChannelsIter<'a, T>; +impl<'a, T> IntoIterator for &'a Channels<'a, T> { + type Item = &'a AudioBuffer; + type IntoIter = ChannelsIter<'a, T>; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -219,22 +251,22 @@ impl<'a, T> IntoIterator for &'a InputChannels<'a, T> { } } -/// An iterator over all of an [`InputPort`]'s channels' sample buffers. -pub struct InputChannelsIter<'a, T> { - pub(crate) data: Iter<'a, *mut T>, - pub(crate) frames_count: u32, +/// An iterator over all of an [`Port`]'s channels' sample buffers. +pub struct ChannelsIter<'a, T> { + data: Iter<'a, *mut T>, + frames_count: u32, } -impl<'a, T> Iterator for InputChannelsIter<'a, T> { - type Item = &'a [T]; +impl<'a, T> Iterator for ChannelsIter<'a, T> { + type Item = &'a AudioBuffer; #[inline] fn next(&mut self) -> Option { self.data .next() - // SAFETY: iterator can only get created from an InputChannels, which guarantees + // SAFETY: iterator can only get created from a PortChannels, which guarantees // the buffer is both valid and of length frames_count - .map(|ptr| unsafe { slice_from_external_parts(*ptr, self.frames_count as usize) }) + .map(|ptr| unsafe { AudioBuffer::from_raw_parts(*ptr, self.frames_count as usize) }) } #[inline] @@ -243,9 +275,19 @@ impl<'a, T> Iterator for InputChannelsIter<'a, T> { } } -impl ExactSizeIterator for InputChannelsIter<'_, S> { +impl ExactSizeIterator for ChannelsIter<'_, S> { #[inline] fn len(&self) -> usize { self.data.len() } } + +impl<'a, T> Clone for ChannelsIter<'a, T> { + #[inline] + fn clone(&self) -> Self { + Self { + data: self.data.clone(), + frames_count: self.frames_count, + } + } +} diff --git a/plugin/src/process/audio/sample_type.rs b/plugin/src/process/audio/sample_type.rs index 3289e6f9..30aacdd1 100644 --- a/plugin/src/process/audio/sample_type.rs +++ b/plugin/src/process/audio/sample_type.rs @@ -1,6 +1,5 @@ -use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut}; -use crate::process::audio::BufferError; -use clap_sys::audio_buffer::clap_audio_buffer; +use crate::internal_utils::slice_from_external_parts; +use crate::process::audio::{BufferError, CelledClapAudioBuffer}; /// A generic enum to discriminate between buffers containing [`f32`] and [`f64`] sample types. /// @@ -9,12 +8,10 @@ use clap_sys::audio_buffer::clap_audio_buffer; /// /// This type is used by methods that detect which types of sample buffers are available: /// -/// * [`InputPort::channels`](super::InputPort::channels) returns a [`SampleType`] of -/// [`InputChannels`](super::InputChannels); -/// * [`OutputPort::channels`](super::OutputPort::channels) returns a [`SampleType`] of -/// [`OutputChannels`](super::OutputChannels); +/// * [`Port::channels`](super::Port::channels) returns a [`SampleType`] of +/// [`Channels`](super::Channels); /// * [`PortPair::channels`](super::PortPair::channels) returns a [`SampleType`] of -/// [`PairedChannels`](super::PairedChannels); +/// [`ChannelsPairs`](super::ChannelsPairs); #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] pub enum SampleType { /// Only [`f32`] sample buffers are available. @@ -42,60 +39,17 @@ impl SampleType { /// ``` /// use clack_plugin::process::audio::SampleType; /// - /// assert_eq!(SampleType::::F32(1.0).as_f32(), Some(&1.0f32)); - /// assert_eq!(SampleType::::F64(1.0).as_f32(), None); - /// assert_eq!(SampleType::::Both(1.0, 1.0).as_f32(), Some(&1.0f32)); + /// assert_eq!(SampleType::::F32(1.0).to_f32(), Some(1.0f32)); + /// assert_eq!(SampleType::::F64(1.0).to_f32(), None); + /// assert_eq!(SampleType::::Both(1.0, 1.0).to_f32(), Some(1.0f32)); /// ``` #[inline] - pub fn as_f32(&self) -> Option<&F32> { - match self { - SampleType::F32(c) | SampleType::Both(c, _) => Some(c), - _ => None, - } - } - - /// Returns a mutable reference to the `F32` sample buffer type if it is available, - /// or [`None`] otherwise. - /// - /// If both buffer types are available, the `F64` buffer type is ignored. - /// - /// # Example - /// - /// ``` - /// use clack_plugin::process::audio::SampleType; - /// - /// assert_eq!(SampleType::::F32(1.0).as_f32_mut(), Some(&mut 1.0f32)); - /// assert_eq!(SampleType::::F64(1.0).as_f32_mut(), None); - /// assert_eq!(SampleType::::Both(1.0, 1.0).as_f32_mut(), Some(&mut 1.0f32)); - /// ``` - #[inline] - pub fn as_f32_mut(&mut self) -> Option<&mut F32> { - match self { - SampleType::F32(c) | SampleType::Both(c, _) => Some(c), - _ => None, - } - } - - /// Returns the `F32` sample buffer type if it is available, or [`None`] otherwise. - /// - /// If both buffer types are available, the `F64` buffer type is discarded. - /// - /// This method consumes the enum. For a non-consuming operation, see the - /// [`as_f32`](SampleType::as_f32) and [`as_f32_mut`](SampleType::as_f32_mut) methods. - /// - /// # Example - /// - /// ``` - /// use clack_plugin::process::audio::SampleType; - /// - /// assert_eq!(SampleType::::F32(1.0).into_f32(), Some(1.0f32)); - /// assert_eq!(SampleType::::F64(1.0).into_f32(), None); - /// assert_eq!(SampleType::::Both(1.0, 1.0).into_f32(), Some(1.0f32)); - /// ``` - #[inline] - pub fn into_f32(self) -> Option { + pub fn to_f32(&self) -> Option + where + F32: Copy, + { match self { - SampleType::F32(c) | SampleType::Both(c, _) => Some(c), + SampleType::F32(c) | SampleType::Both(c, _) => Some(*c), _ => None, } } @@ -110,60 +64,17 @@ impl SampleType { /// ``` /// use clack_plugin::process::audio::SampleType; /// - /// assert_eq!(SampleType::::F32(1.0).as_f64(), None); - /// assert_eq!(SampleType::::F64(1.0).as_f64(), Some(&1.0f64)); - /// assert_eq!(SampleType::::Both(1.0, 1.0).as_f64(), Some(&1.0f64)); - /// ``` - #[inline] - pub fn as_f64(&self) -> Option<&F64> { - match self { - SampleType::F64(c) | SampleType::Both(_, c) => Some(c), - _ => None, - } - } - - /// Returns a mutable reference to the `F64` sample buffer type if it is available, - /// or [`None`] otherwise. - /// - /// If both buffer types are available, the `F32` buffer type is ignored. - /// - /// # Example - /// - /// ``` - /// use clack_plugin::process::audio::SampleType; - /// - /// assert_eq!(SampleType::::F32(1.0).as_f64_mut(), None); - /// assert_eq!(SampleType::::F64(1.0).as_f64_mut(), Some(&mut 1.0f64)); - /// assert_eq!(SampleType::::Both(1.0, 1.0).as_f64_mut(), Some(&mut 1.0f64)); - /// ``` - #[inline] - pub fn as_f64_mut(&mut self) -> Option<&mut F64> { - match self { - SampleType::F64(c) | SampleType::Both(_, c) => Some(c), - _ => None, - } - } - - /// Returns the `F64` sample buffer type if it is available, or [`None`] otherwise. - /// - /// If both buffer types are available, the `F32` buffer type is discarded. - /// - /// This method consumes the enum. For a non-consuming operation, see the - /// [`as_f32`](SampleType::as_f64) and [`as_f64_mut`](SampleType::as_f64_mut) methods. - /// - /// # Example - /// - /// ``` - /// use clack_plugin::process::audio::SampleType; - /// - /// assert_eq!(SampleType::::F32(1.0).into_f64(), None); - /// assert_eq!(SampleType::::F64(1.0).into_f64(), Some(1.0f64)); - /// assert_eq!(SampleType::::Both(1.0, 1.0).into_f64(), Some(1.0f64)); + /// assert_eq!(SampleType::::F32(1.0).to_f64(), None); + /// assert_eq!(SampleType::::F64(1.0).to_f64(), Some(1.0f64)); + /// assert_eq!(SampleType::::Both(1.0, 1.0).to_f64(), Some(1.0f64)); /// ``` #[inline] - pub fn into_f64(self) -> Option { + pub fn to_f64(&self) -> Option + where + F64: Copy, + { match self { - SampleType::F64(c) | SampleType::Both(_, c) => Some(c), + SampleType::F64(c) | SampleType::Both(_, c) => Some(*c), _ => None, } } @@ -273,7 +184,7 @@ impl<'a> SampleType<&'a [*mut f32], &'a [*mut f64]> { /// /// The caller must ensure the provided buffer is valid. #[inline] - pub(crate) unsafe fn from_raw_buffer(raw: &clap_audio_buffer) -> Result { + pub(crate) unsafe fn from_raw_buffer(raw: &CelledClapAudioBuffer) -> Result { match (raw.data32.is_null(), raw.data64.is_null()) { (true, true) => { if raw.channel_count == 0 { @@ -304,39 +215,6 @@ impl<'a> SampleType<&'a [*mut f32], &'a [*mut f64]> { } } -impl<'a> SampleType<&'a mut [*mut f32], &'a mut [*mut f64]> { - /// # Safety - /// - /// The caller must ensure the provided buffer is valid, and that we have exclusive `&mut` - /// access to its contents. - #[inline] - pub(crate) unsafe fn from_raw_buffer_mut( - raw: &mut clap_audio_buffer, - ) -> Result { - match (raw.data32.is_null(), raw.data64.is_null()) { - (true, true) => { - if raw.channel_count == 0 { - Ok(SampleType::Both([].as_mut_slice(), [].as_mut_slice())) - } else { - Err(BufferError::InvalidChannelBuffer) - } - } - (false, true) => Ok(SampleType::F32(slice_from_external_parts_mut( - raw.data32 as *mut _, - raw.channel_count as usize, - ))), - (true, false) => Ok(SampleType::F64(slice_from_external_parts_mut( - raw.data64 as *mut _, - raw.channel_count as usize, - ))), - (false, false) => Ok(SampleType::Both( - slice_from_external_parts_mut(raw.data32 as *mut _, raw.channel_count as usize), - slice_from_external_parts_mut(raw.data64 as *mut _, raw.channel_count as usize), - )), - } - } -} - #[cfg(test)] mod test { use super::*;