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/common/src/process/constant_mask.rs b/common/src/process/constant_mask.rs index 3f825f77..e689cc1c 100644 --- a/common/src/process/constant_mask.rs +++ b/common/src/process/constant_mask.rs @@ -105,14 +105,6 @@ impl Debug for ConstantMask { } } -impl Default for ConstantMask { - /// Returns an empty constant mask, i.e. one where every channel is considered dynamic. - #[inline] - fn default() -> Self { - ConstantMask::FULLY_DYNAMIC - } -} - impl IntoIterator for ConstantMask { type Item = bool; type IntoIter = ConstantMaskIter; diff --git a/host/examples/cpal/src/host/audio.rs b/host/examples/cpal/src/host/audio.rs index e8b515a8..34aa04c3 100644 --- a/host/examples/cpal/src/host/audio.rs +++ b/host/examples/cpal/src/host/audio.rs @@ -140,7 +140,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) @@ -150,7 +150,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 8ac5f5ae..89305502 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}; @@ -122,7 +121,7 @@ impl HostAudioBuffers { pub fn prepare_plugin_buffers( &mut self, cpal_buf_len: usize, - ) -> (InputAudioBuffers<'_>, OutputAudioBuffers<'_>) { + ) -> (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/lib.rs b/host/src/lib.rs index ae038fb7..e38e9e15 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -86,11 +86,9 @@ //! //! 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. -//! [`AudioPorts::with_output_buffers`](process::audio_buffers::AudioPorts::with_output_buffers) -//! respectively). +//! [`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)). //! //! 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 @@ -292,8 +290,7 @@ pub mod prelude { AudioPortProcessingInfo, PluginAudioConfiguration, PluginAudioProcessor, ProcessStatus, StartedPluginAudioProcessor, StoppedPluginAudioProcessor, audio_buffers::{ - AudioPortBuffer, AudioPortBufferType, AudioPorts, InputAudioBuffers, InputChannel, - OutputAudioBuffers, + AudioBuffers, AudioPortBuffer, AudioPortBufferType, AudioPorts, InputChannel, }, }, utils::ClapId, diff --git a/host/src/process.rs b/host/src/process.rs index 07653208..39ba8605 100644 --- a/host/src/process.rs +++ b/host/src/process.rs @@ -13,10 +13,10 @@ #![deny(missing_docs)] -use self::audio_buffers::InputAudioBuffers; +use self::audio_buffers::AudioBuffers; use crate::host::HostHandlers; use crate::plugin::{PluginAudioProcessorHandle, PluginInstanceError, PluginSharedHandle}; -use crate::prelude::{OutputAudioBuffers, PluginInstance}; +use crate::prelude::PluginInstance; use crate::process::PluginAudioProcessor::*; use clack_common::events::event_types::TransportEvent; use clack_common::events::io::{InputEvents, OutputEvents}; @@ -33,6 +33,8 @@ pub use clack_common::process::*; #[allow(missing_docs)] // TODO: doc this pub mod audio_buffers; +pub(crate) mod celled_audio_buffers; + /// A handle to a plugin's audio processor that can be in either its `started` or `stopped` state. /// /// This is a convenience type that can be used where the type-states [`StartedPluginAudioProcessor`] and @@ -417,10 +419,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 +437,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 +465,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, @@ -478,8 +480,8 @@ impl StartedPluginAudioProcessor { in_events: input_events.as_raw(), out_events: output_events.as_raw_mut(), - audio_inputs: audio_inputs.as_raw_buffers().as_ptr(), - audio_outputs: audio_outputs.as_raw_buffers().as_mut_ptr(), + audio_inputs: audio_inputs.as_raw_buffers().cast(), + audio_outputs: audio_outputs.as_raw_buffers().cast(), audio_inputs_count: audio_inputs.port_count(), audio_outputs_count: audio_outputs.port_count(), diff --git a/host/src/process/audio_buffers.rs b/host/src/process/audio_buffers.rs index ade4dc6b..9abe4166 100644 --- a/host/src/process/audio_buffers.rs +++ b/host/src/process/audio_buffers.rs @@ -1,7 +1,8 @@ //! Types to manipulate input and output audio buffers for processing. +use crate::process::celled_audio_buffers::CelledClapAudioBuffer; use crate::util::check_collection_clap_size_overflow; -use clack_common::process::AudioPortProcessingInfo; +use clack_common::process::{AudioPortProcessingInfo, ConstantMask}; use clap_sys::audio_buffer::clap_audio_buffer; use core::array::IntoIter; @@ -68,7 +69,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, @@ -80,38 +81,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), @@ -145,7 +114,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>, @@ -165,15 +134,13 @@ impl AudioPorts { let last = self.buffer_lists.len(); - let mut constant_mask = 0u64; + let mut constant_mask = ConstantMask::FULLY_DYNAMIC; let is_f64 = match port.channels { AudioPortBufferType::F32(channels) => { for channel in channels { min_channel_buffer_length = min_channel_buffer_length .min(saturate_usize_to_clap_size(channel.buffer.len())); - if channel.is_constant { - constant_mask |= 1 << i as u64 - } + constant_mask.set_channel_constant(i as u64, channel.is_constant); check_collection_clap_size_overflow(&self.buffer_lists); @@ -189,9 +156,7 @@ impl AudioPorts { for channel in channels { min_channel_buffer_length = min_channel_buffer_length .min(saturate_usize_to_clap_size(channel.buffer.len())); - if channel.is_constant { - constant_mask |= 1 << i as u64 - } + constant_mask.set_channel_constant(i as u64, channel.is_constant); check_collection_clap_size_overflow(&self.buffer_lists); @@ -215,7 +180,7 @@ impl AudioPorts { descriptor.channel_count = buffers.len() as u32; } descriptor.latency = port.latency; - descriptor.constant_mask = constant_mask; + descriptor.constant_mask = constant_mask.to_bits(); if is_f64 { descriptor.data64 = buffers.as_mut_ptr().cast(); @@ -247,8 +212,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 == u32::MAX { None } else { @@ -260,7 +225,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>, @@ -355,8 +320,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 == u32::MAX { None } else { @@ -383,12 +348,13 @@ const fn saturate_usize_to_clap_size(value: usize) -> u32 { } } -pub struct InputAudioBuffers<'a> { - buffers: &'a [clap_audio_buffer], +#[derive(Copy, Clone)] +pub struct AudioBuffers<'a> { + buffers: &'a [CelledClapAudioBuffer], frames_count: Option, } -impl<'a> InputAudioBuffers<'a> { +impl<'a> AudioBuffers<'a> { #[inline] pub const fn empty() -> Self { Self { @@ -413,48 +379,27 @@ impl<'a> InputAudioBuffers<'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 [clap_audio_buffer], frames_count: u32) -> Self { + pub unsafe fn from_raw_buffers(buffers: *mut [clap_audio_buffer], frames_count: u32) -> Self { + let buffers = *(buffers as *const _ as *const _); check_collection_clap_size_overflow(buffers); - 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), - ) - } - } - #[inline] - pub fn as_raw_buffers(&self) -> &'a [clap_audio_buffer] { - self.buffers + pub fn as_raw_buffers(&self) -> *mut [clap_audio_buffer] { + CelledClapAudioBuffer::slice_as_raw_ptr(self.buffers) } - /// The number of port buffers this [`InputAudioBuffers`] has been given. + /// The number of port buffers this [`AudioBuffers`] has been given. #[inline] pub fn port_count(&self) -> u32 { #[allow(clippy::cast_possible_truncation)] @@ -476,22 +421,25 @@ impl<'a> InputAudioBuffers<'a> { #[inline] pub fn port_info(&self, port_index: u32) -> Option { - self.buffers - .get(port_index as usize) - .map(AudioPortProcessingInfo::from_raw) + let info_ptr = self.buffers.get(port_index as usize)?; + + Some(info_ptr.processing_info()) } #[inline] pub fn port_infos(&self) -> impl Iterator + '_ { - self.buffers.iter().map(AudioPortProcessingInfo::from_raw) + self.buffers + .iter() + .map(CelledClapAudioBuffer::processing_info) } - /// Returns the minimum number of frames available both in this [`InputAudioBuffers`] and - /// the given [`OutputAudioBuffers`]. + /// 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: &OutputAudioBuffers) -> u32 { + #[inline] + 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, @@ -500,173 +448,46 @@ impl<'a> InputAudioBuffers<'a> { } } -pub struct OutputAudioBuffers<'a> { - buffers: &'a mut [clap_audio_buffer], - frames_count: Option, -} - -impl<'a> OutputAudioBuffers<'a> { - #[inline] - pub fn empty() -> Self { - Self { - buffers: &mut [], - frames_count: None, - } - } - - /// Shortens the [`frames_count`] of these output 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`. - #[inline] - pub unsafe fn from_raw_buffers( - buffers: &'a mut [clap_audio_buffer], - frames_count: u32, - ) -> Self { - check_collection_clap_size_overflow(buffers); - - Self { - buffers, - 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) } - } - - #[cfg(feature = "clack-plugin")] +#[cfg(feature = "clack-plugin")] +impl<'a> AudioBuffers<'a> { pub fn from_plugin_audio_mut( audio: &'a mut clack_plugin::prelude::Audio, - ) -> OutputAudioBuffers<'a> { + ) -> (AudioBuffers<'a>, AudioBuffers<'a>) { 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) } - } - - #[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), + ( + AudioBuffers::from_raw_buffers(audio.raw_inputs(), frames_count), + AudioBuffers::from_raw_buffers(audio.raw_outputs(), frames_count), ) } } - #[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(audio: clack_plugin::prelude::Audio) -> (AudioBuffers, AudioBuffers) { + let frames_count = audio.frames_count(); - // SAFETY: the validity of the buffers is guaranteed by this type + // SAFETY: the validity of the buffers is guaranteed by the Audio type unsafe { - clack_plugin::prelude::Audio::from_raw_buffers( - inputs.buffers, - self.buffers, - frames_count, + ( + AudioBuffers::from_raw_buffers(audio.raw_inputs(), frames_count), + 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. - #[inline] - pub fn port_count(&self) -> u32 { - #[allow(clippy::cast_possible_truncation)] - // We checked above that buffers' len cannot overflow u32 - { - self.buffers.len() as u32 - } - } - - /// The number of frames in these output 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) - } } #[cfg(test)] @@ -771,7 +592,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( @@ -784,11 +605,9 @@ 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: input_buffers.as_raw_buffers().cast(), + audio_outputs: output_buffers.as_raw_buffers().cast(), audio_inputs_count: input_buffers.port_count(), audio_outputs_count: output_buffers.port_count(), @@ -807,7 +626,7 @@ mod test { assert_eq!(channels.channel_count(), 128); for (channel, buf) in channels.iter().zip(bufs.iter()) { - assert_eq!(buf, channel) + assert_eq!(channel, buf) } } @@ -816,7 +635,7 @@ mod test { 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/host/src/process/celled_audio_buffers.rs b/host/src/process/celled_audio_buffers.rs new file mode 100644 index 00000000..eec2364e --- /dev/null +++ b/host/src/process/celled_audio_buffers.rs @@ -0,0 +1,34 @@ +use clack_common::process::AudioPortProcessingInfo; +use clap_sys::audio_buffer::clap_audio_buffer; +use core::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 +} + +impl CelledClapAudioBuffer { + #[inline] + pub(crate) fn slice_as_raw_ptr(slice: &[CelledClapAudioBuffer]) -> *mut [clap_audio_buffer] { + slice as *const _ as *const _ as *mut _ + } + + #[inline] + pub(crate) fn from_raw_slice(slice: &mut [clap_audio_buffer]) -> &[Self] { + // SAFETY: This type has the same memory layout as clap_audio_buffer, + // and transmuting between &mut T and &Cell is safe. + unsafe { &*(slice as *mut [clap_audio_buffer] as *mut [CelledClapAudioBuffer]) } + } + + #[inline] + pub(crate) fn processing_info(&self) -> AudioPortProcessingInfo { + // SAFETY: The shared reference "self" here guarantees the pointer is well-aligned and initialized. + // This type also has the exact same memory layout as clap_audio_buffer, so it is safe to pass + // a pointer to it to from_raw_ptr here. + unsafe { AudioPortProcessingInfo::from_raw_ptr(self as *const _ as *const _) } + } +} diff --git a/plugin/examples/gain-gui/tests/test_gain.rs b/plugin/examples/gain-gui/tests/test_gain.rs index c58b2976..f583fd50 100644 --- a/plugin/examples/gain-gui/tests/test_gain.rs +++ b/plugin/examples/gain-gui/tests/test_gain.rs @@ -103,7 +103,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()), ), @@ -113,7 +113,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/gain-presets/tests/test_gain.rs b/plugin/examples/gain-presets/tests/test_gain.rs index a26d47d1..15254d99 100644 --- a/plugin/examples/gain-presets/tests/test_gain.rs +++ b/plugin/examples/gain-presets/tests/test_gain.rs @@ -106,7 +106,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()), ), @@ -116,7 +116,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/gain/tests/test_gain.rs b/plugin/examples/gain/tests/test_gain.rs index f6a74b71..4502fe07 100644 --- a/plugin/examples/gain/tests/test_gain.rs +++ b/plugin/examples/gain/tests/test_gain.rs @@ -104,7 +104,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()), ), @@ -114,7 +114,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/src/process.rs b/plugin/src/process.rs index ba28738f..3cac1d11 100644 --- a/plugin/src/process.rs +++ b/plugin/src/process.rs @@ -11,7 +11,11 @@ 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}; +mod celled_audio_buffers; + +pub(crate) use celled_audio_buffers::CelledClapAudioBuffer; + +use crate::internal_utils::slice_from_external_parts; use audio::*; /// Metadata about the current process call. @@ -197,8 +201,8 @@ impl Events<'_> { /// } /// ``` pub struct Audio<'a> { - inputs: &'a [clap_audio_buffer], - outputs: &'a mut [clap_audio_buffer], + inputs: &'a [CelledClapAudioBuffer], + outputs: &'a [CelledClapAudioBuffer], frames_count: u32, } @@ -215,11 +219,11 @@ impl<'a> 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, ), } @@ -236,45 +240,71 @@ impl<'a> Audio<'a> { /// 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. @@ -302,7 +332,7 @@ impl<'a> Audio<'a> { pub fn input_port_info(&self, index: usize) -> Option { self.inputs .get(index) - .map(AudioPortProcessingInfo::from_raw) + .map(CelledClapAudioBuffer::processing_info) } /// Retrieves the number of available [`InputPort`]s. @@ -326,7 +356,9 @@ impl<'a> Audio<'a> { /// output port by its index. #[inline] pub fn input_ports_infos(&self) -> impl ExactSizeIterator + '_ { - self.inputs.iter().map(AudioPortProcessingInfo::from_raw) + self.inputs + .iter() + .map(CelledClapAudioBuffer::processing_info) } /// Retrieves the [`OutputPort`] at a given index. @@ -338,7 +370,7 @@ impl<'a> Audio<'a> { #[inline] pub fn output_port(&mut self, index: usize) -> Option> { self.outputs - .get_mut(index) + .get(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) }) @@ -355,7 +387,7 @@ impl<'a> Audio<'a> { pub fn output_port_info(&self, index: usize) -> Option { self.outputs .get(index) - .map(AudioPortProcessingInfo::from_raw) + .map(CelledClapAudioBuffer::processing_info) } /// Retrieves the number of available [`OutputPort`]s. @@ -381,7 +413,9 @@ impl<'a> Audio<'a> { pub fn output_ports_infos( &self, ) -> impl ExactSizeIterator + '_ { - self.outputs.iter().map(AudioPortProcessingInfo::from_raw) + self.outputs + .iter() + .map(CelledClapAudioBuffer::processing_info) } /// Retrieves the [`PortPair`] at a given index. @@ -396,7 +430,7 @@ impl<'a> Audio<'a> { unsafe { PortPair::from_raw( self.inputs.get(index), - self.outputs.get_mut(index), + self.outputs.get(index), self.frames_count, ) } @@ -431,8 +465,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, diff --git a/plugin/src/process/audio.rs b/plugin/src/process/audio.rs index 4bc1b3f5..f2622541 100644 --- a/plugin/src/process/audio.rs +++ b/plugin/src/process/audio.rs @@ -42,10 +42,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, + ) } } diff --git a/plugin/src/process/audio/input.rs b/plugin/src/process/audio/input.rs index b25960f4..d32b6e78 100644 --- a/plugin/src/process/audio/input.rs +++ b/plugin/src/process/audio/input.rs @@ -1,13 +1,13 @@ use crate::internal_utils::slice_from_external_parts; use crate::prelude::Audio; +use crate::process::CelledClapAudioBuffer; use crate::process::audio::{BufferError, SampleType}; use clack_common::process::ConstantMask; -use clap_sys::audio_buffer::clap_audio_buffer; 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>, + inputs: Iter<'a, CelledClapAudioBuffer>, frames_count: u32, } @@ -49,7 +49,7 @@ impl ExactSizeIterator for InputPortsIter<'_> { /// An input audio port. #[derive(Copy, Clone)] pub struct InputPort<'a> { - inner: &'a clap_audio_buffer, + inner: &'a CelledClapAudioBuffer, frames_count: u32, } @@ -59,7 +59,7 @@ impl<'a> InputPort<'a> { /// * 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, @@ -137,7 +137,7 @@ 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()) } } diff --git a/plugin/src/process/audio/output.rs b/plugin/src/process/audio/output.rs index a155f920..7acecc57 100644 --- a/plugin/src/process/audio/output.rs +++ b/plugin/src/process/audio/output.rs @@ -1,14 +1,13 @@ use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut}; use crate::prelude::Audio; -use crate::process::InputChannelsIter; use crate::process::audio::{BufferError, SampleType}; +use crate::process::{CelledClapAudioBuffer, InputChannelsIter}; use clack_common::process::ConstantMask; -use clap_sys::audio_buffer::clap_audio_buffer; -use std::slice::IterMut; +use std::slice::{Iter, IterMut}; /// An iterator of all the available [`OutputPort`]s from an [`Audio`] struct. pub struct OutputPortsIter<'a> { - outputs: IterMut<'a, clap_audio_buffer>, + outputs: Iter<'a, CelledClapAudioBuffer>, frames_count: u32, } @@ -16,7 +15,7 @@ impl<'a> OutputPortsIter<'a> { #[inline] pub(crate) fn new(audio: &'a mut Audio<'_>) -> Self { Self { - outputs: audio.outputs.iter_mut(), + outputs: audio.outputs.iter(), frames_count: audio.frames_count, } } @@ -49,7 +48,7 @@ impl ExactSizeIterator for OutputPortsIter<'_> { /// An output audio port. pub struct OutputPort<'a> { - inner: &'a mut clap_audio_buffer, + inner: &'a CelledClapAudioBuffer, frames_count: u32, } @@ -59,7 +58,7 @@ impl<'a> OutputPort<'a> { /// * 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 { + pub(crate) unsafe fn from_raw(inner: &'a CelledClapAudioBuffer, frames_count: u32) -> Self { Self { inner, frames_count, @@ -137,13 +136,13 @@ impl<'a> OutputPort<'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(&mut self, new_mask: ConstantMask) { - self.inner.constant_mask = new_mask.to_bits() + self.inner.constant_mask.set(new_mask.to_bits()) } } diff --git a/plugin/src/process/audio/pair.rs b/plugin/src/process/audio/pair.rs index 10af3d82..9a40055c 100644 --- a/plugin/src/process/audio/pair.rs +++ b/plugin/src/process/audio/pair.rs @@ -1,9 +1,8 @@ use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut}; -use crate::process::Audio; use crate::process::audio::pair::ChannelPair::*; use crate::process::audio::{BufferError, InputPort, OutputPort, SampleType}; +use crate::process::{Audio, CelledClapAudioBuffer}; use clack_common::process::{AudioPortProcessingInfo, ConstantMask}; -use clap_sys::audio_buffer::clap_audio_buffer; use std::slice::{Iter, IterMut}; /// A pair of Input and Output ports. @@ -15,8 +14,8 @@ 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. 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 +26,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) { @@ -67,9 +66,7 @@ impl<'a> PortPair<'a> { /// 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)) + self.input.as_ref().map(|buf| buf.processing_info()) } /// Retrieves the port info for the output of this pair. @@ -77,9 +74,7 @@ impl<'a> PortPair<'a> { /// 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)) + self.output.as_ref().map(|buf| buf.processing_info()) } /// Retrieves the port pair's channels. @@ -192,11 +187,11 @@ impl<'a> PortPair<'a> { pub fn constant_masks(&self) -> (ConstantMask, ConstantMask) { ( self.input - .map(|i| ConstantMask::from_bits(i.constant_mask)) + .map(|i| ConstantMask::from_bits(i.constant_mask.get())) .unwrap_or(ConstantMask::FULLY_CONSTANT), self.output .as_ref() - .map(|o| ConstantMask::from_bits(o.constant_mask)) + .map(|o| ConstantMask::from_bits(o.constant_mask.get())) .unwrap_or(ConstantMask::FULLY_CONSTANT), ) } @@ -333,8 +328,8 @@ impl ExactSizeIterator for PairedChannelsIter<'_, S> { /// An iterator of all of the available [`PortPair`]s from an [`Audio`] struct. pub struct PortPairsIter<'a> { - inputs: Iter<'a, clap_audio_buffer>, - outputs: IterMut<'a, clap_audio_buffer>, + inputs: Iter<'a, CelledClapAudioBuffer>, + outputs: Iter<'a, CelledClapAudioBuffer>, frames_count: u32, } @@ -343,7 +338,7 @@ impl<'a> PortPairsIter<'a> { pub(crate) fn new(audio: &'a mut Audio<'_>) -> Self { Self { inputs: audio.inputs.iter(), - outputs: audio.outputs.iter_mut(), + outputs: audio.outputs.iter(), frames_count: audio.frames_count, } } diff --git a/plugin/src/process/audio/sample_type.rs b/plugin/src/process/audio/sample_type.rs index 3289e6f9..484f8d17 100644 --- a/plugin/src/process/audio/sample_type.rs +++ b/plugin/src/process/audio/sample_type.rs @@ -1,6 +1,6 @@ use crate::internal_utils::{slice_from_external_parts, slice_from_external_parts_mut}; +use crate::process::CelledClapAudioBuffer; use crate::process::audio::BufferError; -use clap_sys::audio_buffer::clap_audio_buffer; /// A generic enum to discriminate between buffers containing [`f32`] and [`f64`] sample types. /// @@ -273,7 +273,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 { @@ -311,7 +311,7 @@ impl<'a> SampleType<&'a mut [*mut f32], &'a mut [*mut f64]> { /// access to its contents. #[inline] pub(crate) unsafe fn from_raw_buffer_mut( - raw: &mut clap_audio_buffer, + raw: &CelledClapAudioBuffer, ) -> Result { match (raw.data32.is_null(), raw.data64.is_null()) { (true, true) => { diff --git a/plugin/src/process/celled_audio_buffers.rs b/plugin/src/process/celled_audio_buffers.rs new file mode 100644 index 00000000..d64942d2 --- /dev/null +++ b/plugin/src/process/celled_audio_buffers.rs @@ -0,0 +1,40 @@ +use clack_common::process::AudioPortProcessingInfo; +use core::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 +} + +// Statically assert that the two structs have the same memory representation +const _: () = { + use clap_sys::audio_buffer::clap_audio_buffer; + use core::mem::offset_of; + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + assert!(offset_of!(CelledClapAudioBuffer, data32) == offset_of!(clap_audio_buffer, data32)); + assert!(offset_of!(CelledClapAudioBuffer, data64) == offset_of!(clap_audio_buffer, data64)); + assert!( + offset_of!(CelledClapAudioBuffer, channel_count) + == offset_of!(clap_audio_buffer, channel_count) + ); + assert!(offset_of!(CelledClapAudioBuffer, latency) == offset_of!(clap_audio_buffer, latency)); + assert!( + offset_of!(CelledClapAudioBuffer, constant_mask) + == offset_of!(clap_audio_buffer, constant_mask) + ); +}; + +impl CelledClapAudioBuffer { + #[inline] + pub(crate) fn processing_info(&self) -> AudioPortProcessingInfo { + // SAFETY: The shared reference "self" here guarantees the pointer is well-aligned and initialized. + // This type also has the exact same memory layout as clap_audio_buffer, so it is safe to pass + // a pointer to it to from_raw_ptr here. + unsafe { AudioPortProcessingInfo::from_raw_ptr(self as *const _ as *const _) } + } +}