Explicitly handle 0 channel IO
Instead of treating it as a flush. This is needed for note effect plugins.
This commit is contained in:
parent
7ce86cc788
commit
5e486ab3d9
2 changed files with 268 additions and 282 deletions
|
@ -1547,14 +1547,6 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
// we'll process every incoming event.
|
||||
let process = &*process;
|
||||
|
||||
// I don't think this is a thing for CLAP since there's a dedicated flush function, but
|
||||
// might as well protect against this
|
||||
// TOOD: Send the output events when doing a flush
|
||||
if process.audio_outputs_count == 0 || process.frames_count == 0 {
|
||||
nih_log!("CLAP process call event flush");
|
||||
return CLAP_PROCESS_CONTINUE;
|
||||
}
|
||||
|
||||
// If `P::SAMPLE_ACCURATE_AUTOMATION` is set, then we'll split up the audio buffer into
|
||||
// chunks whenever a parameter change occurs
|
||||
let mut block_start = 0;
|
||||
|
@ -1588,21 +1580,16 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
// - 1 input bus
|
||||
// - 1 output bus
|
||||
// - 1 input bus and 1 output bus
|
||||
// - 1 input bus and 1 output bus
|
||||
//
|
||||
// Depending on the host either of these may also be missing if the number of
|
||||
// channels is set to 0.
|
||||
nih_debug_assert!(
|
||||
process.audio_inputs_count <= 1 && process.audio_outputs_count <= 1,
|
||||
"The host provides more than one input or output bus"
|
||||
);
|
||||
|
||||
// Right now we don't handle any auxiliary outputs
|
||||
check_null_ptr_msg!(
|
||||
"Null pointers passed for audio outputs in process function",
|
||||
CLAP_PROCESS_ERROR,
|
||||
process.audio_outputs,
|
||||
(*process.audio_outputs).data32
|
||||
);
|
||||
let audio_outputs = &*process.audio_outputs;
|
||||
let num_output_channels = audio_outputs.channel_count as usize;
|
||||
|
||||
// This vector has been preallocated to contain enough slices as there are output
|
||||
// channels
|
||||
// TODO: The audio buffers have a latency field, should we use those?
|
||||
|
@ -1610,7 +1597,13 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
// flags?
|
||||
let mut output_buffer = wrapper.output_buffer.borrow_mut();
|
||||
output_buffer.with_raw_vec(|output_slices| {
|
||||
if !process.audio_outputs.is_null()
|
||||
&& !(*process.audio_outputs).data32.is_null()
|
||||
{
|
||||
let audio_outputs = &*process.audio_outputs;
|
||||
let num_output_channels = audio_outputs.channel_count as usize;
|
||||
nih_debug_assert_eq!(num_output_channels, output_slices.len());
|
||||
|
||||
for (output_channel_idx, output_channel_slice) in
|
||||
output_slices.iter_mut().enumerate()
|
||||
{
|
||||
|
@ -1626,14 +1619,21 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
block_end - block_start,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Some hosts process data in place, in which case we don't need to do any copying
|
||||
// ourselves. If the pointers do not alias, then we'll do the copy here and then the
|
||||
// plugin can just do normal in place processing.
|
||||
if !process.audio_inputs.is_null() {
|
||||
if !process.audio_outputs.is_null()
|
||||
&& !(*process.audio_outputs).data32.is_null()
|
||||
&& !process.audio_inputs.is_null()
|
||||
&& !(*process.audio_inputs).data32.is_null()
|
||||
{
|
||||
// We currently don't support sidechain inputs
|
||||
let audio_outputs = &*process.audio_outputs;
|
||||
let audio_inputs = &*process.audio_inputs;
|
||||
let num_output_channels = audio_outputs.channel_count as usize;
|
||||
let num_input_channels = audio_inputs.channel_count as usize;
|
||||
nih_debug_assert!(
|
||||
num_input_channels <= num_output_channels,
|
||||
|
|
|
@ -697,22 +697,13 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
.expect("Process call without prior setup call")
|
||||
.sample_rate;
|
||||
|
||||
// It's possible the host only wanted to send new parameter values
|
||||
let is_parameter_flush = data.num_outputs == 0;
|
||||
if is_parameter_flush {
|
||||
nih_log!("VST3 parameter flush");
|
||||
} else {
|
||||
check_null_ptr_msg!(
|
||||
"Process output pointer is null",
|
||||
data.outputs,
|
||||
(*data.outputs).buffers,
|
||||
);
|
||||
}
|
||||
|
||||
// The setups we suppport are:
|
||||
// - 1 input bus
|
||||
// - 1 output bus
|
||||
// - 1 input bus and 1 output bus
|
||||
//
|
||||
// Depending on the host either of these may also be missing if the number of channels
|
||||
// is set to 0.
|
||||
nih_debug_assert!(
|
||||
data.num_inputs >= 0
|
||||
&& data.num_inputs <= 1
|
||||
|
@ -956,24 +947,23 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
parameter_values_changed = false;
|
||||
}
|
||||
|
||||
let result = if is_parameter_flush {
|
||||
kResultOk
|
||||
} else {
|
||||
let num_output_channels = (*data.outputs).num_channels as usize;
|
||||
|
||||
// This vector has been preallocated to contain enough slices as there are
|
||||
// output channels
|
||||
let mut output_buffer = self.inner.output_buffer.borrow_mut();
|
||||
output_buffer.with_raw_vec(|output_slices| {
|
||||
if !data.outputs.is_null() {
|
||||
let num_output_channels = (*data.outputs).num_channels as usize;
|
||||
nih_debug_assert_eq!(num_output_channels, output_slices.len());
|
||||
|
||||
for (output_channel_idx, output_channel_slice) in
|
||||
output_slices.iter_mut().enumerate()
|
||||
{
|
||||
// If `P::SAMPLE_ACCURATE_AUTOMATION` is set, then we may be iterating over
|
||||
// the buffer in smaller sections.
|
||||
// SAFETY: These pointers may not be valid outside of this function even though
|
||||
// their lifetime is equal to this structs. This is still safe because they are
|
||||
// only dereferenced here later as part of this process function.
|
||||
// If `P::SAMPLE_ACCURATE_AUTOMATION` is set, then we may be iterating
|
||||
// over the buffer in smaller sections.
|
||||
// SAFETY: These pointers may not be valid outside of this function even
|
||||
// though their lifetime is equal to this structs. This is still safe
|
||||
// because they are only dereferenced here later as part of this process
|
||||
// function.
|
||||
let channel_ptr =
|
||||
*((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx);
|
||||
*output_channel_slice = std::slice::from_raw_parts_mut(
|
||||
|
@ -981,24 +971,24 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
block_end - block_start,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Some hosts process data in place, in which case we don't need to do any
|
||||
// copying ourselves. If the pointers do not alias, then we'll do the copy here
|
||||
// and then the plugin can just do normal in place processing.
|
||||
if !data.inputs.is_null() {
|
||||
// Some hosts process data in place, in which case we don't need to do any copying
|
||||
// ourselves. If the pointers do not alias, then we'll do the copy here and then the
|
||||
// plugin can just do normal in place processing.
|
||||
if !data.outputs.is_null() && !data.inputs.is_null() {
|
||||
let num_output_channels = (*data.outputs).num_channels as usize;
|
||||
let num_input_channels = (*data.inputs).num_channels as usize;
|
||||
nih_debug_assert!(
|
||||
num_input_channels <= num_output_channels,
|
||||
"Stereo to mono and similar configurations are not supported"
|
||||
);
|
||||
for input_channel_idx in
|
||||
0..cmp::min(num_input_channels, num_output_channels)
|
||||
{
|
||||
for input_channel_idx in 0..cmp::min(num_input_channels, num_output_channels) {
|
||||
let output_channel_ptr =
|
||||
*((*data.outputs).buffers as *mut *mut f32).add(input_channel_idx);
|
||||
let input_channel_ptr = *((*data.inputs).buffers as *const *const f32)
|
||||
.add(input_channel_idx);
|
||||
let input_channel_ptr =
|
||||
*((*data.inputs).buffers as *const *const f32).add(input_channel_idx);
|
||||
if input_channel_ptr != output_channel_ptr {
|
||||
ptr::copy_nonoverlapping(
|
||||
input_channel_ptr.add(block_start),
|
||||
|
@ -1031,8 +1021,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
}
|
||||
|
||||
// We need to compensate for the block splitting here
|
||||
transport.pos_samples =
|
||||
Some(context.project_time_samples + block_start as i64);
|
||||
transport.pos_samples = Some(context.project_time_samples + block_start as i64);
|
||||
if context.state & (1 << 9) != 0 {
|
||||
// kProjectTimeMusicValid
|
||||
if P::SAMPLE_ACCURATE_AUTOMATION
|
||||
|
@ -1054,8 +1043,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
// kBarPositionValid
|
||||
if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
|
||||
// The transport object knows how to recompute this from the other information
|
||||
transport.bar_start_pos_beats =
|
||||
match transport.bar_start_pos_beats() {
|
||||
transport.bar_start_pos_beats = match transport.bar_start_pos_beats() {
|
||||
Some(updated) => Some(updated),
|
||||
None => Some(context.bar_position_music),
|
||||
};
|
||||
|
@ -1151,8 +1139,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
Some(translated_event) => {
|
||||
vst3_event.type_ =
|
||||
EventTypes::kNoteExpressionValueEvent as u16;
|
||||
vst3_event.event.note_expression_value =
|
||||
translated_event;
|
||||
vst3_event.event.note_expression_value = translated_event;
|
||||
}
|
||||
None => {
|
||||
nih_debug_assert_failure!(
|
||||
|
@ -1216,14 +1203,13 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
|||
}
|
||||
}
|
||||
|
||||
match result {
|
||||
let result = match result {
|
||||
ProcessStatus::Error(err) => {
|
||||
nih_debug_assert_failure!("Process error: {}", err);
|
||||
|
||||
return kResultFalse;
|
||||
}
|
||||
_ => kResultOk,
|
||||
}
|
||||
};
|
||||
|
||||
// If our block ends at the end of the buffer then that means there are no more
|
||||
|
|
Loading…
Add table
Reference in a new issue