diff --git a/src/buffer.rs b/src/buffer.rs index 5cd3dc4e..1e086fd5 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -17,6 +17,10 @@ pub use samples::{ChannelSamples, ChannelSamplesIter, SamplesIter}; /// this either way. Maybe just get rid of it in favor for raw pointers. #[derive(Default)] pub struct Buffer<'a> { + /// The number of samples contained within `output_slices`. This needs to be stored separately + /// to be able to handle 0 channel IO for MIDI-only plugins. + num_samples: usize, + /// Contains slices for the plugin's outputs. You can't directly create a nested slice from a /// pointer to pointers, so this needs to be preallocated in the setup call and kept around /// between process calls. And because storing a reference to this means a) that you need a lot @@ -31,11 +35,7 @@ impl<'a> Buffer<'a> { /// Returns the number of samples per channel in this buffer. #[inline] pub fn len(&self) -> usize { - if self.output_slices.is_empty() { - 0 - } else { - self.output_slices[0].len() - } + self.num_samples } /// Returns the number of channels in this buffer. @@ -47,7 +47,7 @@ impl<'a> Buffer<'a> { /// Returns true if this buffer does not contain any samples. #[inline] pub fn is_empty(&self) -> bool { - self.output_slices.is_empty() || self.output_slices[0].is_empty() + self.num_samples == 0 } /// Obtain the raw audio buffers. @@ -108,17 +108,28 @@ impl<'a> Buffer<'a> { } } - /// Access the raw output slice vector. This needs to be resized to match the number of output - /// channels during the plugin's initialization. Then during audio processing, these slices - /// should be updated to point to the plugin's audio buffers. + /// Set the slices in the raw output slice vector. This vector needs to be resized to match the + /// number of output channels during the plugin's initialization. Then during audio processing, + /// these slices should be updated to point to the plugin's audio buffers. The `num_samples` + /// argument should match the length of the inner slices. /// /// # Safety /// /// The stored slices must point to live data when this object is passed to the plugins' process /// function. The rest of this object also assumes all channel lengths are equal. Panics will /// likely occur if this is not the case. - pub unsafe fn set_slices(&mut self, update: impl FnOnce(&mut Vec<&'a mut [f32]>)) { + pub unsafe fn set_slices( + &mut self, + num_samples: usize, + update: impl FnOnce(&mut Vec<&'a mut [f32]>), + ) { + self.num_samples = num_samples; update(&mut self.output_slices); + + #[cfg(debug_assertions)] + for slice in &self.output_slices { + nih_debug_assert_eq!(slice.len(), num_samples); + } } } @@ -131,7 +142,7 @@ mod miri { let mut real_buffers = vec![vec![0.0; 512]; 2]; let mut buffer = Buffer::default(); unsafe { - buffer.set_slices(|output_slices| { + buffer.set_slices(512, |output_slices| { let (first_channel, other_channels) = real_buffers.split_at_mut(1); *output_slices = vec![&mut first_channel[0], &mut other_channels[0]]; }) @@ -159,7 +170,7 @@ mod miri { let mut real_buffers = vec![vec![0.0; 512]; 2]; let mut buffer = Buffer::default(); unsafe { - buffer.set_slices(|output_slices| { + buffer.set_slices(512, |output_slices| { let (first_channel, other_channels) = real_buffers.split_at_mut(1); *output_slices = vec![&mut first_channel[0], &mut other_channels[0]]; }) diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 23e3a50d..d30003b0 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -1788,7 +1788,7 @@ impl Wrapper

{ wrapper .output_buffer .borrow_mut() - .set_slices(|output_slices| { + .set_slices(0, |output_slices| { output_slices.resize_with(bus_config.num_output_channels as usize, || &mut []) }); @@ -1812,7 +1812,7 @@ impl Wrapper

{ Buffer::default, ); for buffer in aux_input_buffers.iter_mut() { - buffer.set_slices(|channel_slices| { + buffer.set_slices(0, |channel_slices| { channel_slices .resize_with(bus_config.aux_input_busses.num_channels as usize, || { &mut [] @@ -1827,7 +1827,7 @@ impl Wrapper

{ Buffer::default, ); for buffer in aux_output_buffers.iter_mut() { - buffer.set_slices(|channel_slices| { + buffer.set_slices(0, |channel_slices| { channel_slices .resize_with(bus_config.aux_output_busses.num_channels as usize, || { &mut [] @@ -1987,9 +1987,10 @@ impl Wrapper

{ // TODO: The audio buffers have a latency field, should we use those? // TODO: Like with VST3, should we expose some way to access or set the silence/constant // flags? + let block_len = block_end - block_start; let mut output_buffer = wrapper.output_buffer.borrow_mut(); let mut buffer_is_valid = false; - output_buffer.set_slices(|output_slices| { + output_buffer.set_slices(block_len, |output_slices| { // Buffers for zero-channel plugins like note effects should always be allowed buffer_is_valid = output_slices.is_empty(); @@ -2024,7 +2025,7 @@ impl Wrapper

{ *(audio_outputs.data32 as *mut *mut f32).add(output_channel_idx); *output_channel_slice = std::slice::from_raw_parts_mut( channel_ptr.add(block_start), - block_end - block_start, + block_len, ); } } @@ -2056,7 +2057,7 @@ impl Wrapper

{ ptr::copy_nonoverlapping( input_channel_ptr.add(block_start), output_channel_ptr.add(block_start), - block_end - block_start, + block_len, ); } } @@ -2095,13 +2096,12 @@ impl Wrapper

{ // If the host passes weird data then we need to be very sure that there are // no dangling references to previous data - buffer.set_slices(|slices| slices.fill_with(|| &mut [])); + buffer.set_slices(0, |slices| slices.fill_with(|| &mut [])); continue; } // We'll always reuse the start of the buffer even of the current block is // shorter for cache locality reasons - let block_len = block_end - block_start; for (channel_idx, channel_storage) in storage.iter_mut().enumerate() { // The `set_len()` avoids having to unnecessarily fill the buffer with // zeroes when sizing up @@ -2113,7 +2113,7 @@ impl Wrapper

{ )); } - buffer.set_slices(|slices| { + buffer.set_slices(block_len, |slices| { for (channel_slice, channel_storage) in slices.iter_mut().zip(storage.iter_mut()) { @@ -2148,12 +2148,11 @@ impl Wrapper

{ // If the host passes weird data then we need to be very sure that there are // no dangling references to previous data - buffer.set_slices(|slices| slices.fill_with(|| &mut [])); + buffer.set_slices(0, |slices| slices.fill_with(|| &mut [])); continue; } - let block_len = block_end - block_start; - buffer.set_slices(|slices| { + buffer.set_slices(block_len, |slices| { for (channel_idx, channel_slice) in slices.iter_mut().enumerate() { *channel_slice = std::slice::from_raw_parts_mut( (*(*host_output).data32.add(channel_idx)).add(block_start) diff --git a/src/wrapper/standalone/backend/cpal.rs b/src/wrapper/standalone/backend/cpal.rs index 4d75d4ae..02cd3179 100644 --- a/src/wrapper/standalone/backend/cpal.rs +++ b/src/wrapper/standalone/backend/cpal.rs @@ -325,7 +325,7 @@ impl Cpal { ]; let mut buffer = Buffer::default(); unsafe { - buffer.set_slices(|output_slices| { + buffer.set_slices(0, |output_slices| { // Pre-allocate enough storage, the pointers are set in the data callback because // `channels` will have been moved between now and the next callback output_slices.resize_with(channels.len(), || &mut []); @@ -339,12 +339,11 @@ impl Cpal { // Can't borrow from `self` in the callback let config = self.config.clone(); let mut num_processed_samples = 0; - move |data, _info| { // Things may have been moved in between callbacks, so these pointers need to be set up // again on each invocation unsafe { - buffer.set_slices(|output_slices| { + buffer.set_slices(config.period_size as usize, |output_slices| { for (output_slice, channel) in output_slices.iter_mut().zip(channels.iter_mut()) { // SAFETY: `channels` is no longer used directly after this, and it outlives diff --git a/src/wrapper/standalone/backend/dummy.rs b/src/wrapper/standalone/backend/dummy.rs index 4b429d9e..9baa7fb8 100644 --- a/src/wrapper/standalone/backend/dummy.rs +++ b/src/wrapper/standalone/backend/dummy.rs @@ -33,7 +33,7 @@ impl Backend for Dummy { ]; let mut buffer = Buffer::default(); unsafe { - buffer.set_slices(|output_slices| { + buffer.set_slices(self.config.period_size as usize, |output_slices| { // SAFETY: `channels` is no longer used directly after this *output_slices = channels .iter_mut() diff --git a/src/wrapper/standalone/backend/jack.rs b/src/wrapper/standalone/backend/jack.rs index 3cd66cb0..aa5ef11e 100644 --- a/src/wrapper/standalone/backend/jack.rs +++ b/src/wrapper/standalone/backend/jack.rs @@ -40,7 +40,7 @@ impl Backend for Jack { let mut buffer = Buffer::default(); unsafe { - buffer.set_slices(|output_slices| { + buffer.set_slices(0, |output_slices| { output_slices.resize_with(self.outputs.lock().len(), || &mut []); }) } @@ -104,7 +104,7 @@ impl Backend for Jack { // And the buffer's slices need to point to the JACK output ports unsafe { - buffer.set_slices(|output_slices| { + buffer.set_slices(num_frames as usize, |output_slices| { for (output_slice, output) in output_slices.iter_mut().zip(outputs.iter_mut()) { // SAFETY: This buffer is only read from after in this callback, and the // reference passed to `cb` cannot outlive that function call diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index 1b509f51..fca87729 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -402,7 +402,7 @@ impl IComponent for Wrapper

{ self.inner .output_buffer .borrow_mut() - .set_slices(|output_slices| { + .set_slices(0, |output_slices| { output_slices .resize_with(bus_config.num_output_channels as usize, || &mut []) }); @@ -429,7 +429,7 @@ impl IComponent for Wrapper

{ Buffer::default, ); for buffer in aux_input_buffers.iter_mut() { - buffer.set_slices(|channel_slices| { + buffer.set_slices(0, |channel_slices| { channel_slices.resize_with( bus_config.aux_input_busses.num_channels as usize, || &mut [], @@ -444,7 +444,7 @@ impl IComponent for Wrapper

{ Buffer::default, ); for buffer in aux_output_buffers.iter_mut() { - buffer.set_slices(|channel_slices| { + buffer.set_slices(0, |channel_slices| { channel_slices.resize_with( bus_config.aux_output_busses.num_channels as usize, || &mut [], @@ -1322,9 +1322,10 @@ impl IAudioProcessor for Wrapper

{ // channels. In case the does does not provide an output or if they don't provide // all of the channels (this should not happen, but Ableton Live might do it) then // we'll skip the process function. + let block_len = block_end - block_start; let mut output_buffer = self.inner.output_buffer.borrow_mut(); let mut buffer_is_valid = false; - output_buffer.set_slices(|output_slices| { + output_buffer.set_slices(block_len, |output_slices| { // Buffers for zero-channel plugins like note effects should always be allowed buffer_is_valid = output_slices.is_empty(); @@ -1352,7 +1353,7 @@ impl IAudioProcessor for Wrapper

{ *((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx); *output_channel_slice = std::slice::from_raw_parts_mut( channel_ptr.add(block_start), - block_end - block_start, + block_len, ); } } @@ -1377,7 +1378,7 @@ impl IAudioProcessor for Wrapper

{ ptr::copy_nonoverlapping( input_channel_ptr.add(block_start), output_channel_ptr.add(block_start), - block_end - block_start, + block_len, ); } } @@ -1416,13 +1417,12 @@ impl IAudioProcessor for Wrapper

{ // If the host passes weird data then we need to be very sure that there are // no dangling references to previous data - buffer.set_slices(|slices| slices.fill_with(|| &mut [])); + buffer.set_slices(0, |slices| slices.fill_with(|| &mut [])); continue; } // We'll always reuse the start of the buffer even of the current block is // shorter for cache locality reasons - let block_len = block_end - block_start; for (channel_idx, channel_storage) in storage.iter_mut().enumerate() { // The `set_len()` avoids having to unnecessarily fill the buffer with // zeroes when sizing up @@ -1435,7 +1435,7 @@ impl IAudioProcessor for Wrapper

{ )); } - buffer.set_slices(|slices| { + buffer.set_slices(block_len, |slices| { for (channel_slice, channel_storage) in slices.iter_mut().zip(storage.iter_mut()) { @@ -1470,12 +1470,11 @@ impl IAudioProcessor for Wrapper

{ // If the host passes weird data then we need to be very sure that there are // no dangling references to previous data - buffer.set_slices(|slices| slices.fill_with(|| &mut [])); + buffer.set_slices(0, |slices| slices.fill_with(|| &mut [])); continue; } - let block_len = block_end - block_start; - buffer.set_slices(|slices| { + buffer.set_slices(block_len, |slices| { for (channel_idx, channel_slice) in slices.iter_mut().enumerate() { *channel_slice = std::slice::from_raw_parts_mut( (*(*host_output).buffers.add(channel_idx)).add(block_start)