diff --git a/CHANGELOG.md b/CHANGELOG.md index dba9bed4..712f4f38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,11 @@ state is to list breaking changes. structure and to more explicitly mention that the non-lifecycle methods are called once immediately after creating the plugin object. +### Fixed + +- The buffer changes from March 31st broke the sample accurate automation + feature. This has now been fixed. + ## [2023-04-22] ### Added diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index a05837da..2eda36d5 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -2020,84 +2020,85 @@ impl Wrapper

{ // TODO: Like with VST3, should we expose some way to access or set the silence/constant // flags? let mut buffer_manager = wrapper.buffer_manager.borrow_mut(); - let buffers = buffer_manager.create_buffers(block_len, |buffer_source| { - // Explicitly take plugins with no main output that does have auxiliary outputs - // into account. Shouldn't happen, but if we just start copying audio here then - // that would result in unsoundness. - if process.audio_outputs_count > 0 - && !process.audio_outputs.is_null() - && !(*process.audio_outputs).data32.is_null() - && has_main_output - { - let audio_output = &*process.audio_outputs; - let ptrs = NonNull::new(audio_output.data32 as *mut *mut f32).unwrap(); - let num_channels = audio_output.channel_count as usize; - - *buffer_source.main_output_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); - } - - if process.audio_inputs_count > 0 - && !process.audio_inputs.is_null() - && !(*process.audio_inputs).data32.is_null() - && has_main_input - { - let audio_input = &*process.audio_inputs; - let ptrs = NonNull::new(audio_input.data32 as *mut *mut f32).unwrap(); - let num_channels = audio_input.channel_count as usize; - - *buffer_source.main_input_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); - } - - if !process.audio_inputs.is_null() { - for (aux_input_no, aux_input_channel_pointers) in buffer_source - .aux_input_channel_pointers - .iter_mut() - .enumerate() + let buffers = + buffer_manager.create_buffers(block_start, block_len, |buffer_source| { + // Explicitly take plugins with no main output that does have auxiliary + // outputs into account. Shouldn't happen, but if we just start copying + // audio here then that would result in unsoundness. + if process.audio_outputs_count > 0 + && !process.audio_outputs.is_null() + && !(*process.audio_outputs).data32.is_null() + && has_main_output { - let aux_input_idx = aux_input_no + aux_input_start_idx; - if aux_input_idx > process.audio_inputs_count as usize { - break; - } + let audio_output = &*process.audio_outputs; + let ptrs = NonNull::new(audio_output.data32 as *mut *mut f32).unwrap(); + let num_channels = audio_output.channel_count as usize; - let audio_input = &*process.audio_inputs.add(aux_input_idx); - match NonNull::new(audio_input.data32 as *mut *mut f32) { - Some(ptrs) => { - let num_channels = audio_input.channel_count as usize; + *buffer_source.main_output_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } - *aux_input_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); + if process.audio_inputs_count > 0 + && !process.audio_inputs.is_null() + && !(*process.audio_inputs).data32.is_null() + && has_main_input + { + let audio_input = &*process.audio_inputs; + let ptrs = NonNull::new(audio_input.data32 as *mut *mut f32).unwrap(); + let num_channels = audio_input.channel_count as usize; + + *buffer_source.main_input_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } + + if !process.audio_inputs.is_null() { + for (aux_input_no, aux_input_channel_pointers) in buffer_source + .aux_input_channel_pointers + .iter_mut() + .enumerate() + { + let aux_input_idx = aux_input_no + aux_input_start_idx; + if aux_input_idx > process.audio_inputs_count as usize { + break; + } + + let audio_input = &*process.audio_inputs.add(aux_input_idx); + match NonNull::new(audio_input.data32 as *mut *mut f32) { + Some(ptrs) => { + let num_channels = audio_input.channel_count as usize; + + *aux_input_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } + None => continue, } - None => continue, } } - } - if !process.audio_outputs.is_null() { - for (aux_output_no, aux_output_channel_pointers) in buffer_source - .aux_output_channel_pointers - .iter_mut() - .enumerate() - { - let aux_output_idx = aux_output_no + aux_output_start_idx; - if aux_output_idx > process.audio_outputs_count as usize { - break; - } - - let audio_output = &*process.audio_outputs.add(aux_output_idx); - match NonNull::new(audio_output.data32 as *mut *mut f32) { - Some(ptrs) => { - let num_channels = audio_output.channel_count as usize; - - *aux_output_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); + if !process.audio_outputs.is_null() { + for (aux_output_no, aux_output_channel_pointers) in buffer_source + .aux_output_channel_pointers + .iter_mut() + .enumerate() + { + let aux_output_idx = aux_output_no + aux_output_start_idx; + if aux_output_idx > process.audio_outputs_count as usize { + break; + } + + let audio_output = &*process.audio_outputs.add(aux_output_idx); + match NonNull::new(audio_output.data32 as *mut *mut f32) { + Some(ptrs) => { + let num_channels = audio_output.channel_count as usize; + + *aux_output_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } + None => continue, } - None => continue, } } - } - }); + }); // If the host does not provide outputs or if it does not provide the required // number of channels (should not happen, but Ableton Live does this for bypassed diff --git a/src/wrapper/standalone/backend/cpal.rs b/src/wrapper/standalone/backend/cpal.rs index 79823b64..9ddf6d2d 100644 --- a/src/wrapper/standalone/backend/cpal.rs +++ b/src/wrapper/standalone/backend/cpal.rs @@ -825,7 +825,7 @@ impl CpalMidir { { let buffers = unsafe { - buffer_manager.create_buffers(buffer_size, |buffer_sources| { + buffer_manager.create_buffers(0, buffer_size, |buffer_sources| { *buffer_sources.main_output_channel_pointers = Some(ChannelPointers { ptrs: NonNull::new(main_io_channel_pointers.get().as_mut_ptr()) .unwrap(), diff --git a/src/wrapper/standalone/backend/dummy.rs b/src/wrapper/standalone/backend/dummy.rs index f99154cd..b18d7376 100644 --- a/src/wrapper/standalone/backend/dummy.rs +++ b/src/wrapper/standalone/backend/dummy.rs @@ -121,7 +121,7 @@ impl Backend

for Dummy { } let buffers = unsafe { - buffer_manager.create_buffers(num_samples, |buffer_sources| { + buffer_manager.create_buffers(0, num_samples, |buffer_sources| { *buffer_sources.main_output_channel_pointers = Some(ChannelPointers { ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(), num_channels: main_io_channel_pointers.len(), diff --git a/src/wrapper/standalone/backend/jack.rs b/src/wrapper/standalone/backend/jack.rs index 036671ec..39dafdb2 100644 --- a/src/wrapper/standalone/backend/jack.rs +++ b/src/wrapper/standalone/backend/jack.rs @@ -204,7 +204,7 @@ impl Backend

for Jack { } let buffers = unsafe { - buffer_manager.create_buffers(num_frames as usize, |buffer_sources| { + buffer_manager.create_buffers(0, num_frames as usize, |buffer_sources| { *buffer_sources.main_output_channel_pointers = Some(ChannelPointers { ptrs: NonNull::new(main_output_channel_pointers.get().as_mut_ptr()) .unwrap(), diff --git a/src/wrapper/util/buffer_management.rs b/src/wrapper/util/buffer_management.rs index d3814dc0..98777d7a 100644 --- a/src/wrapper/util/buffer_management.rs +++ b/src/wrapper/util/buffer_management.rs @@ -151,7 +151,9 @@ impl BufferManager { /// uninitialized buffer data (aux outputs, and main output channels with no matching input /// channel) are filled with zeroes. /// - /// If any of the output + /// `sample_offset` and `num_samples` can be used to slice a set of host channel pointers for + /// sample accurate automation. If any of the outputs are missing because the host hasn't + /// provided enough channels or outputs, then they will be replaced by empty slices. /// /// # Panics /// @@ -163,6 +165,7 @@ impl BufferManager { /// or write to for the lifetime of the returned [`Buffers`]. pub unsafe fn create_buffers<'a, 'buffer: 'a>( &'a mut self, + sample_offset: usize, num_samples: usize, set_buffer_sources: impl FnOnce(&mut BufferSource), ) -> Buffers<'a, 'buffer> { @@ -192,8 +195,10 @@ impl BufferManager { output_channel_pointers.ptrs.as_ptr().add(channel_idx); assert!(!output_channel_pointer.is_null()); - *output_slice = - std::slice::from_raw_parts_mut(*output_channel_pointer, num_samples); + *output_slice = std::slice::from_raw_parts_mut( + (*output_channel_pointer).add(sample_offset), + num_samples, + ); } // If the caller/host should have provided buffer pointers but didn't then we @@ -229,7 +234,7 @@ impl BufferManager { assert!(!input_channel_pointer.is_null()); output_slice.copy_from_slice(std::slice::from_raw_parts_mut( - *input_channel_pointer, + (*input_channel_pointer).add(sample_offset), num_samples, )) } @@ -275,7 +280,7 @@ impl BufferManager { nih_debug_assert!(num_samples <= channel.capacity()); channel.resize(num_samples, 0.0); channel.copy_from_slice(std::slice::from_raw_parts_mut( - *input_channel_pointer, + (*input_channel_pointer).add(sample_offset), num_samples, )) } @@ -334,7 +339,7 @@ impl BufferManager { assert!(!output_channel_pointer.is_null()); *output_slice = std::slice::from_raw_parts_mut( - *output_channel_pointer, + (*output_channel_pointer).add(sample_offset), num_samples, ); @@ -424,7 +429,7 @@ mod miri { // implementation for more information. let mut buffer_manager = BufferManager::for_audio_io_layout(BUFFER_SIZE, AUDIO_IO_LAYOUT); let buffers = unsafe { - buffer_manager.create_buffers(BUFFER_SIZE, |buffer_sources| { + buffer_manager.create_buffers(0, BUFFER_SIZE, |buffer_sources| { *buffer_sources.main_output_channel_pointers = Some(ChannelPointers { ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(), num_channels: main_io_channel_pointers.len(), diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index 322355f7..090e3df8 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -1229,81 +1229,84 @@ impl IAudioProcessor for Wrapper

{ // The buffer manager preallocated buffer slices for all the IO and storage for // any axuiliary inputs. let mut buffer_manager = self.inner.buffer_manager.borrow_mut(); - let buffers = buffer_manager.create_buffers(block_len, |buffer_source| { - if data.num_outputs > 0 - && !data.outputs.is_null() - && !(*data.outputs).buffers.is_null() - && has_main_output - { - let audio_output = &*data.outputs; - let ptrs = NonNull::new(audio_output.buffers as *mut *mut f32).unwrap(); - let num_channels = audio_output.num_channels as usize; - - *buffer_source.main_output_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); - } - - if data.num_inputs > 0 - && !data.inputs.is_null() - && !(*data.inputs).buffers.is_null() - && has_main_input - { - let audio_input = &*data.inputs; - let ptrs = NonNull::new(audio_input.buffers as *mut *mut f32).unwrap(); - let num_channels = audio_input.num_channels as usize; - - *buffer_source.main_input_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); - } - - if !data.inputs.is_null() { - for (aux_input_no, aux_input_channel_pointers) in buffer_source - .aux_input_channel_pointers - .iter_mut() - .enumerate() + let buffers = + buffer_manager.create_buffers(block_start, block_len, |buffer_source| { + if data.num_outputs > 0 + && !data.outputs.is_null() + && !(*data.outputs).buffers.is_null() + && has_main_output { - let aux_input_idx = aux_input_no + aux_input_start_idx; - if aux_input_idx > data.num_outputs as usize { - break; - } + let audio_output = &*data.outputs; + let ptrs = + NonNull::new(audio_output.buffers as *mut *mut f32).unwrap(); + let num_channels = audio_output.num_channels as usize; - let audio_input = &*data.inputs.add(aux_input_idx); - match NonNull::new(audio_input.buffers as *mut *mut f32) { - Some(ptrs) => { - let num_channels = audio_input.num_channels as usize; + *buffer_source.main_output_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } - *aux_input_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); + if data.num_inputs > 0 + && !data.inputs.is_null() + && !(*data.inputs).buffers.is_null() + && has_main_input + { + let audio_input = &*data.inputs; + let ptrs = + NonNull::new(audio_input.buffers as *mut *mut f32).unwrap(); + let num_channels = audio_input.num_channels as usize; + + *buffer_source.main_input_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } + + if !data.inputs.is_null() { + for (aux_input_no, aux_input_channel_pointers) in buffer_source + .aux_input_channel_pointers + .iter_mut() + .enumerate() + { + let aux_input_idx = aux_input_no + aux_input_start_idx; + if aux_input_idx > data.num_outputs as usize { + break; + } + + let audio_input = &*data.inputs.add(aux_input_idx); + match NonNull::new(audio_input.buffers as *mut *mut f32) { + Some(ptrs) => { + let num_channels = audio_input.num_channels as usize; + + *aux_input_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } + None => continue, } - None => continue, } } - } - if !data.outputs.is_null() { - for (aux_output_no, aux_output_channel_pointers) in buffer_source - .aux_output_channel_pointers - .iter_mut() - .enumerate() - { - let aux_output_idx = aux_output_no + aux_output_start_idx; - if aux_output_idx > data.num_outputs as usize { - break; - } - - let audio_output = &*data.outputs.add(aux_output_idx); - match NonNull::new(audio_output.buffers as *mut *mut f32) { - Some(ptrs) => { - let num_channels = audio_output.num_channels as usize; - - *aux_output_channel_pointers = - Some(ChannelPointers { ptrs, num_channels }); + if !data.outputs.is_null() { + for (aux_output_no, aux_output_channel_pointers) in buffer_source + .aux_output_channel_pointers + .iter_mut() + .enumerate() + { + let aux_output_idx = aux_output_no + aux_output_start_idx; + if aux_output_idx > data.num_outputs as usize { + break; + } + + let audio_output = &*data.outputs.add(aux_output_idx); + match NonNull::new(audio_output.buffers as *mut *mut f32) { + Some(ptrs) => { + let num_channels = audio_output.num_channels as usize; + + *aux_output_channel_pointers = + Some(ChannelPointers { ptrs, num_channels }); + } + None => continue, } - None => continue, } } - } - }); + }); // We already checked whether the host has initiated a parameter flush, but in // case it still did something unexpected that we did not catch we'll still try