1
0
Fork 0

Delay the process_wrapper() use

The other parts also don't allocate, but parking_lot doesn't play nicely
when spawning an editor thread. In that case its hashmap may get resized
on the audio thread which is not what we want, but it won't cause any
real problems.
This commit is contained in:
Robbert van der Helm 2022-02-06 00:51:18 +01:00
parent 5c9ac31408
commit 29a9c41389

View file

@ -1124,167 +1124,161 @@ impl<P: Plugin> IAudioProcessor for Wrapper<P> {
unsafe fn process(&self, data: *mut vst3_sys::vst::ProcessData) -> tresult { unsafe fn process(&self, data: *mut vst3_sys::vst::ProcessData) -> tresult {
check_null_ptr!(data); check_null_ptr!(data);
// Panic on allocations if the `assert_process_allocs` feature has been enabled, and make // We need to handle incoming automation first
// sure that FTZ is set up correctly let data = &*data;
process_wrapper(|| { let sample_rate = self
// We need to handle incoming automation first .inner
let data = &*data; .current_buffer_config
let sample_rate = self .load()
.inner .map(|c| c.sample_rate);
.current_buffer_config if let Some(param_changes) = data.input_param_changes.upgrade() {
.load() let num_param_queues = param_changes.get_parameter_count();
.map(|c| c.sample_rate); for change_queue_idx in 0..num_param_queues {
if let Some(param_changes) = data.input_param_changes.upgrade() { if let Some(param_change_queue) =
let num_param_queues = param_changes.get_parameter_count(); param_changes.get_parameter_data(change_queue_idx).upgrade()
for change_queue_idx in 0..num_param_queues {
if let Some(param_change_queue) =
param_changes.get_parameter_data(change_queue_idx).upgrade()
{
let param_hash = param_change_queue.get_parameter_id();
let num_changes = param_change_queue.get_point_count();
// TODO: Handle sample accurate parameter changes, possibly in a similar way to
// the smoothing
let mut sample_offset = 0i32;
let mut value = 0.0f64;
if num_changes > 0
&& param_change_queue.get_point(
num_changes - 1,
&mut sample_offset,
&mut value,
) == kResultOk
{
self.inner.set_normalized_value_by_hash(
param_hash,
value as f32,
sample_rate,
);
}
}
}
}
// And also incoming note events if the plugin accepts MDII
if P::ACCEPTS_MIDI {
let mut input_events = self.inner.input_events.write();
if let Some(events) = data.input_events.upgrade() {
let num_events = events.get_event_count();
input_events.clear();
let mut event: MaybeUninit<_> = MaybeUninit::uninit();
for i in 0..num_events {
nih_debug_assert_eq!(events.get_event(i, event.as_mut_ptr()), kResultOk);
let event = event.assume_init();
let timing = event.sample_offset as u32;
if event.type_ == vst3_sys::vst::EventTypes::kNoteOnEvent as u16 {
let event = event.event.note_on;
input_events.push_back(NoteEvent::NoteOn {
timing,
channel: event.channel as u8,
note: event.pitch as u8,
velocity: (event.velocity * 127.0).round() as u8,
});
} else if event.type_ == vst3_sys::vst::EventTypes::kNoteOffEvent as u16 {
let event = event.event.note_off;
input_events.push_back(NoteEvent::NoteOff {
timing,
channel: event.channel as u8,
note: event.pitch as u8,
velocity: (event.velocity * 127.0).round() as u8,
});
}
}
}
}
// It's possible the host only wanted to send new parameter values
if data.num_outputs == 0 {
nih_log!("VST3 parameter flush");
return kResultOk;
}
// The setups we suppport are:
// - 1 input bus
// - 1 output bus
// - 1 input bus and 1 output bus
nih_debug_assert!(
data.num_inputs >= 0
&& data.num_inputs <= 1
&& data.num_outputs >= 0
&& data.num_outputs <= 1,
"The host provides more than one input or output bus"
);
nih_debug_assert_eq!(
data.symbolic_sample_size,
vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32
);
nih_debug_assert!(data.num_samples >= 0);
let num_output_channels = (*data.outputs).num_channels as usize;
check_null_ptr_msg!(
"Process output pointer is null",
data.outputs,
(*data.outputs).buffers,
);
// This vector has been reallocated to contain enough slices as there are output
// channels
let mut output_buffer = self.inner.output_buffer.write();
{
let output_slices = output_buffer.as_raw_vec();
nih_debug_assert_eq!(num_output_channels, output_slices.len());
for (output_channel_idx, output_channel_slice) in
output_slices.iter_mut().enumerate()
{ {
// SAFETY: These pointers may not be valid outside of this function even though let param_hash = param_change_queue.get_parameter_id();
// their lifetime is equal to this structs. This is still safe because they are let num_changes = param_change_queue.get_point_count();
// only dereferenced here later as part of this process function.
*output_channel_slice = std::slice::from_raw_parts_mut(
*((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx),
data.num_samples as usize,
);
}
}
// Most hosts process data in place, in which case we don't need to do any copying // TODO: Handle sample accurate parameter changes, possibly in a similar way to
// ourselves. If the pointers do not alias, then we'll do the copy here and then the // the smoothing
// plugin can just do normal in place processing. let mut sample_offset = 0i32;
if !data.inputs.is_null() { let mut value = 0.0f64;
let num_input_channels = (*data.inputs).num_channels as usize; if num_changes > 0
nih_debug_assert!( && param_change_queue.get_point(
num_input_channels <= num_output_channels, num_changes - 1,
"Stereo to mono and similar configurations are not supported" &mut sample_offset,
); &mut value,
for input_channel_idx in 0..cmp::min(num_input_channels, num_output_channels) { ) == kResultOk
let output_channel_ptr = {
*((*data.outputs).buffers as *mut *mut f32).add(input_channel_idx); self.inner.set_normalized_value_by_hash(
let input_channel_ptr = param_hash,
*((*data.inputs).buffers as *const *const f32).add(input_channel_idx); value as f32,
if input_channel_ptr != output_channel_ptr { sample_rate,
ptr::copy_nonoverlapping(
input_channel_ptr,
output_channel_ptr,
data.num_samples as usize,
); );
} }
} }
} }
}
match self // And also incoming note events if the plugin accepts MDII
.inner if P::ACCEPTS_MIDI {
.plugin let mut input_events = self.inner.input_events.write();
.write() if let Some(events) = data.input_events.upgrade() {
// SAFETY: Same here let num_events = events.get_event_count();
.process(&mut output_buffer, &mut self.inner.make_process_context())
{ input_events.clear();
let mut event: MaybeUninit<_> = MaybeUninit::uninit();
for i in 0..num_events {
nih_debug_assert_eq!(events.get_event(i, event.as_mut_ptr()), kResultOk);
let event = event.assume_init();
let timing = event.sample_offset as u32;
if event.type_ == vst3_sys::vst::EventTypes::kNoteOnEvent as u16 {
let event = event.event.note_on;
input_events.push_back(NoteEvent::NoteOn {
timing,
channel: event.channel as u8,
note: event.pitch as u8,
velocity: (event.velocity * 127.0).round() as u8,
});
} else if event.type_ == vst3_sys::vst::EventTypes::kNoteOffEvent as u16 {
let event = event.event.note_off;
input_events.push_back(NoteEvent::NoteOff {
timing,
channel: event.channel as u8,
note: event.pitch as u8,
velocity: (event.velocity * 127.0).round() as u8,
});
}
}
}
}
// It's possible the host only wanted to send new parameter values
if data.num_outputs == 0 {
nih_log!("VST3 parameter flush");
return kResultOk;
}
// The setups we suppport are:
// - 1 input bus
// - 1 output bus
// - 1 input bus and 1 output bus
nih_debug_assert!(
data.num_inputs >= 0
&& data.num_inputs <= 1
&& data.num_outputs >= 0
&& data.num_outputs <= 1,
"The host provides more than one input or output bus"
);
nih_debug_assert_eq!(
data.symbolic_sample_size,
vst3_sys::vst::SymbolicSampleSizes::kSample32 as i32
);
nih_debug_assert!(data.num_samples >= 0);
let num_output_channels = (*data.outputs).num_channels as usize;
check_null_ptr_msg!(
"Process output pointer is null",
data.outputs,
(*data.outputs).buffers,
);
// This vector has been reallocated to contain enough slices as there are output
// channels
let mut output_buffer = self.inner.output_buffer.write();
{
let output_slices = output_buffer.as_raw_vec();
nih_debug_assert_eq!(num_output_channels, output_slices.len());
for (output_channel_idx, output_channel_slice) in output_slices.iter_mut().enumerate() {
// 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.
*output_channel_slice = std::slice::from_raw_parts_mut(
*((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx),
data.num_samples as usize,
);
}
}
// Most 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() {
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) {
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);
if input_channel_ptr != output_channel_ptr {
ptr::copy_nonoverlapping(
input_channel_ptr,
output_channel_ptr,
data.num_samples as usize,
);
}
}
}
let mut plugin = self.inner.plugin.write();
let mut context = self.inner.make_process_context();
// Panic on allocations if the `assert_process_allocs` feature has been enabled, and make
// sure that FTZ is set up correctly
process_wrapper(
move || match plugin.process(&mut output_buffer, &mut context) {
ProcessStatus::Error(err) => { ProcessStatus::Error(err) => {
nih_debug_assert_failure!("Process error: {}", err); nih_debug_assert_failure!("Process error: {}", err);
kResultFalse kResultFalse
} }
_ => kResultOk, _ => kResultOk,
} },
}) )
} }
unsafe fn get_tail_samples(&self) -> u32 { unsafe fn get_tail_samples(&self) -> u32 {