Consider block start with buffer management
This broke sample accurate automation.
This commit is contained in:
parent
4912962551
commit
5e69910616
7 changed files with 157 additions and 143 deletions
|
@ -24,6 +24,11 @@ state is to list breaking changes.
|
||||||
structure and to more explicitly mention that the non-lifecycle methods are
|
structure and to more explicitly mention that the non-lifecycle methods are
|
||||||
called once immediately after creating the plugin object.
|
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]
|
## [2023-04-22]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -2020,84 +2020,85 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
// TODO: Like with VST3, should we expose some way to access or set the silence/constant
|
// TODO: Like with VST3, should we expose some way to access or set the silence/constant
|
||||||
// flags?
|
// flags?
|
||||||
let mut buffer_manager = wrapper.buffer_manager.borrow_mut();
|
let mut buffer_manager = wrapper.buffer_manager.borrow_mut();
|
||||||
let buffers = buffer_manager.create_buffers(block_len, |buffer_source| {
|
let buffers =
|
||||||
// Explicitly take plugins with no main output that does have auxiliary outputs
|
buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
|
||||||
// into account. Shouldn't happen, but if we just start copying audio here then
|
// Explicitly take plugins with no main output that does have auxiliary
|
||||||
// that would result in unsoundness.
|
// outputs into account. Shouldn't happen, but if we just start copying
|
||||||
if process.audio_outputs_count > 0
|
// audio here then that would result in unsoundness.
|
||||||
&& !process.audio_outputs.is_null()
|
if process.audio_outputs_count > 0
|
||||||
&& !(*process.audio_outputs).data32.is_null()
|
&& !process.audio_outputs.is_null()
|
||||||
&& has_main_output
|
&& !(*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 aux_input_idx = aux_input_no + aux_input_start_idx;
|
let audio_output = &*process.audio_outputs;
|
||||||
if aux_input_idx > process.audio_inputs_count as usize {
|
let ptrs = NonNull::new(audio_output.data32 as *mut *mut f32).unwrap();
|
||||||
break;
|
let num_channels = audio_output.channel_count as usize;
|
||||||
}
|
|
||||||
|
|
||||||
let audio_input = &*process.audio_inputs.add(aux_input_idx);
|
*buffer_source.main_output_channel_pointers =
|
||||||
match NonNull::new(audio_input.data32 as *mut *mut f32) {
|
Some(ChannelPointers { ptrs, num_channels });
|
||||||
Some(ptrs) => {
|
}
|
||||||
let num_channels = audio_input.channel_count as usize;
|
|
||||||
|
|
||||||
*aux_input_channel_pointers =
|
if process.audio_inputs_count > 0
|
||||||
Some(ChannelPointers { ptrs, num_channels });
|
&& !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() {
|
if !process.audio_outputs.is_null() {
|
||||||
for (aux_output_no, aux_output_channel_pointers) in buffer_source
|
for (aux_output_no, aux_output_channel_pointers) in buffer_source
|
||||||
.aux_output_channel_pointers
|
.aux_output_channel_pointers
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
let aux_output_idx = aux_output_no + aux_output_start_idx;
|
let aux_output_idx = aux_output_no + aux_output_start_idx;
|
||||||
if aux_output_idx > process.audio_outputs_count as usize {
|
if aux_output_idx > process.audio_outputs_count as usize {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let audio_output = &*process.audio_outputs.add(aux_output_idx);
|
let audio_output = &*process.audio_outputs.add(aux_output_idx);
|
||||||
match NonNull::new(audio_output.data32 as *mut *mut f32) {
|
match NonNull::new(audio_output.data32 as *mut *mut f32) {
|
||||||
Some(ptrs) => {
|
Some(ptrs) => {
|
||||||
let num_channels = audio_output.channel_count as usize;
|
let num_channels = audio_output.channel_count as usize;
|
||||||
|
|
||||||
*aux_output_channel_pointers =
|
*aux_output_channel_pointers =
|
||||||
Some(ChannelPointers { ptrs, num_channels });
|
Some(ChannelPointers { ptrs, num_channels });
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
}
|
}
|
||||||
None => continue,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// If the host does not provide outputs or if it does not provide the required
|
// 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
|
// number of channels (should not happen, but Ableton Live does this for bypassed
|
||||||
|
|
|
@ -825,7 +825,7 @@ impl CpalMidir {
|
||||||
|
|
||||||
{
|
{
|
||||||
let buffers = unsafe {
|
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 {
|
*buffer_sources.main_output_channel_pointers = Some(ChannelPointers {
|
||||||
ptrs: NonNull::new(main_io_channel_pointers.get().as_mut_ptr())
|
ptrs: NonNull::new(main_io_channel_pointers.get().as_mut_ptr())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|
|
@ -121,7 +121,7 @@ impl<P: Plugin> Backend<P> for Dummy {
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffers = unsafe {
|
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 {
|
*buffer_sources.main_output_channel_pointers = Some(ChannelPointers {
|
||||||
ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(),
|
ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(),
|
||||||
num_channels: main_io_channel_pointers.len(),
|
num_channels: main_io_channel_pointers.len(),
|
||||||
|
|
|
@ -204,7 +204,7 @@ impl<P: Plugin> Backend<P> for Jack {
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffers = unsafe {
|
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 {
|
*buffer_sources.main_output_channel_pointers = Some(ChannelPointers {
|
||||||
ptrs: NonNull::new(main_output_channel_pointers.get().as_mut_ptr())
|
ptrs: NonNull::new(main_output_channel_pointers.get().as_mut_ptr())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|
|
@ -151,7 +151,9 @@ impl BufferManager {
|
||||||
/// uninitialized buffer data (aux outputs, and main output channels with no matching input
|
/// uninitialized buffer data (aux outputs, and main output channels with no matching input
|
||||||
/// channel) are filled with zeroes.
|
/// 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
|
/// # Panics
|
||||||
///
|
///
|
||||||
|
@ -163,6 +165,7 @@ impl BufferManager {
|
||||||
/// or write to for the lifetime of the returned [`Buffers`].
|
/// or write to for the lifetime of the returned [`Buffers`].
|
||||||
pub unsafe fn create_buffers<'a, 'buffer: 'a>(
|
pub unsafe fn create_buffers<'a, 'buffer: 'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
|
sample_offset: usize,
|
||||||
num_samples: usize,
|
num_samples: usize,
|
||||||
set_buffer_sources: impl FnOnce(&mut BufferSource),
|
set_buffer_sources: impl FnOnce(&mut BufferSource),
|
||||||
) -> Buffers<'a, 'buffer> {
|
) -> Buffers<'a, 'buffer> {
|
||||||
|
@ -192,8 +195,10 @@ impl BufferManager {
|
||||||
output_channel_pointers.ptrs.as_ptr().add(channel_idx);
|
output_channel_pointers.ptrs.as_ptr().add(channel_idx);
|
||||||
assert!(!output_channel_pointer.is_null());
|
assert!(!output_channel_pointer.is_null());
|
||||||
|
|
||||||
*output_slice =
|
*output_slice = std::slice::from_raw_parts_mut(
|
||||||
std::slice::from_raw_parts_mut(*output_channel_pointer, num_samples);
|
(*output_channel_pointer).add(sample_offset),
|
||||||
|
num_samples,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the caller/host should have provided buffer pointers but didn't then we
|
// 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());
|
assert!(!input_channel_pointer.is_null());
|
||||||
|
|
||||||
output_slice.copy_from_slice(std::slice::from_raw_parts_mut(
|
output_slice.copy_from_slice(std::slice::from_raw_parts_mut(
|
||||||
*input_channel_pointer,
|
(*input_channel_pointer).add(sample_offset),
|
||||||
num_samples,
|
num_samples,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -275,7 +280,7 @@ impl BufferManager {
|
||||||
nih_debug_assert!(num_samples <= channel.capacity());
|
nih_debug_assert!(num_samples <= channel.capacity());
|
||||||
channel.resize(num_samples, 0.0);
|
channel.resize(num_samples, 0.0);
|
||||||
channel.copy_from_slice(std::slice::from_raw_parts_mut(
|
channel.copy_from_slice(std::slice::from_raw_parts_mut(
|
||||||
*input_channel_pointer,
|
(*input_channel_pointer).add(sample_offset),
|
||||||
num_samples,
|
num_samples,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -334,7 +339,7 @@ impl BufferManager {
|
||||||
assert!(!output_channel_pointer.is_null());
|
assert!(!output_channel_pointer.is_null());
|
||||||
|
|
||||||
*output_slice = std::slice::from_raw_parts_mut(
|
*output_slice = std::slice::from_raw_parts_mut(
|
||||||
*output_channel_pointer,
|
(*output_channel_pointer).add(sample_offset),
|
||||||
num_samples,
|
num_samples,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -424,7 +429,7 @@ mod miri {
|
||||||
// implementation for more information.
|
// implementation for more information.
|
||||||
let mut buffer_manager = BufferManager::for_audio_io_layout(BUFFER_SIZE, AUDIO_IO_LAYOUT);
|
let mut buffer_manager = BufferManager::for_audio_io_layout(BUFFER_SIZE, AUDIO_IO_LAYOUT);
|
||||||
let buffers = unsafe {
|
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 {
|
*buffer_sources.main_output_channel_pointers = Some(ChannelPointers {
|
||||||
ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(),
|
ptrs: NonNull::new(main_io_channel_pointers.as_mut_ptr()).unwrap(),
|
||||||
num_channels: main_io_channel_pointers.len(),
|
num_channels: main_io_channel_pointers.len(),
|
||||||
|
|
|
@ -1229,81 +1229,84 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
// The buffer manager preallocated buffer slices for all the IO and storage for
|
// The buffer manager preallocated buffer slices for all the IO and storage for
|
||||||
// any axuiliary inputs.
|
// any axuiliary inputs.
|
||||||
let mut buffer_manager = self.inner.buffer_manager.borrow_mut();
|
let mut buffer_manager = self.inner.buffer_manager.borrow_mut();
|
||||||
let buffers = buffer_manager.create_buffers(block_len, |buffer_source| {
|
let buffers =
|
||||||
if data.num_outputs > 0
|
buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
|
||||||
&& !data.outputs.is_null()
|
if data.num_outputs > 0
|
||||||
&& !(*data.outputs).buffers.is_null()
|
&& !data.outputs.is_null()
|
||||||
&& has_main_output
|
&& !(*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 aux_input_idx = aux_input_no + aux_input_start_idx;
|
let audio_output = &*data.outputs;
|
||||||
if aux_input_idx > data.num_outputs as usize {
|
let ptrs =
|
||||||
break;
|
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);
|
*buffer_source.main_output_channel_pointers =
|
||||||
match NonNull::new(audio_input.buffers as *mut *mut f32) {
|
Some(ChannelPointers { ptrs, num_channels });
|
||||||
Some(ptrs) => {
|
}
|
||||||
let num_channels = audio_input.num_channels as usize;
|
|
||||||
|
|
||||||
*aux_input_channel_pointers =
|
if data.num_inputs > 0
|
||||||
Some(ChannelPointers { ptrs, num_channels });
|
&& !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() {
|
if !data.outputs.is_null() {
|
||||||
for (aux_output_no, aux_output_channel_pointers) in buffer_source
|
for (aux_output_no, aux_output_channel_pointers) in buffer_source
|
||||||
.aux_output_channel_pointers
|
.aux_output_channel_pointers
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
let aux_output_idx = aux_output_no + aux_output_start_idx;
|
let aux_output_idx = aux_output_no + aux_output_start_idx;
|
||||||
if aux_output_idx > data.num_outputs as usize {
|
if aux_output_idx > data.num_outputs as usize {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let audio_output = &*data.outputs.add(aux_output_idx);
|
let audio_output = &*data.outputs.add(aux_output_idx);
|
||||||
match NonNull::new(audio_output.buffers as *mut *mut f32) {
|
match NonNull::new(audio_output.buffers as *mut *mut f32) {
|
||||||
Some(ptrs) => {
|
Some(ptrs) => {
|
||||||
let num_channels = audio_output.num_channels as usize;
|
let num_channels = audio_output.num_channels as usize;
|
||||||
|
|
||||||
*aux_output_channel_pointers =
|
*aux_output_channel_pointers =
|
||||||
Some(ChannelPointers { ptrs, num_channels });
|
Some(ChannelPointers { ptrs, num_channels });
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
}
|
}
|
||||||
None => continue,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// We already checked whether the host has initiated a parameter flush, but in
|
// 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
|
// case it still did something unexpected that we did not catch we'll still try
|
||||||
|
|
Loading…
Add table
Reference in a new issue