parent
591f3710bd
commit
910a90d47a
1 changed files with 81 additions and 74 deletions
|
@ -386,85 +386,92 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
) {
|
) {
|
||||||
self.clone().backend.borrow_mut().run(
|
self.clone().backend.borrow_mut().run(
|
||||||
move |buffer, transport, input_events, output_events| {
|
move |buffer, transport, input_events, output_events| {
|
||||||
if should_terminate.load(Ordering::SeqCst) {
|
// TODO: This process wrapper should actually be in the backends (since the backends
|
||||||
return false;
|
// should also not allocate in their audio callbacks), but that's a bit more
|
||||||
}
|
// erorr prone
|
||||||
|
process_wrapper(|| {
|
||||||
let sample_rate = self.buffer_config.sample_rate;
|
if should_terminate.load(Ordering::SeqCst) {
|
||||||
if let ProcessStatus::Error(err) = self.plugin.write().process(
|
return false;
|
||||||
buffer,
|
|
||||||
// TODO: Provide extra inputs and outputs in the JACk backend
|
|
||||||
&mut AuxiliaryBuffers {
|
|
||||||
inputs: &mut [],
|
|
||||||
outputs: &mut [],
|
|
||||||
},
|
|
||||||
&mut self.make_process_context(transport, input_events, output_events),
|
|
||||||
) {
|
|
||||||
nih_error!("The plugin returned an error while processing:");
|
|
||||||
nih_error!("{}", err);
|
|
||||||
|
|
||||||
let push_successful = gui_task_sender.send(GuiTask::Close).is_ok();
|
|
||||||
nih_debug_assert!(
|
|
||||||
push_successful,
|
|
||||||
"Could not queue window close, the editor will remain open"
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any output note events are now in a vector that can be processed by the
|
|
||||||
// audio/MIDI backend
|
|
||||||
|
|
||||||
// We'll always write these events to the first sample, so even when we add note output we
|
|
||||||
// shouldn't have to think about interleaving events here
|
|
||||||
let mut parameter_values_changed = false;
|
|
||||||
while let Some((param_ptr, normalized_value)) = self.unprocessed_param_changes.pop()
|
|
||||||
{
|
|
||||||
unsafe { param_ptr.set_normalized_value(normalized_value) };
|
|
||||||
unsafe { param_ptr.update_smoother(sample_rate, false) };
|
|
||||||
parameter_values_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the editor to react to the new parameter values if the editor uses a reactive data
|
|
||||||
// binding model
|
|
||||||
if parameter_values_changed {
|
|
||||||
self.notify_param_values_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// After processing audio, we'll check if the editor has sent us updated plugin state.
|
|
||||||
// We'll restore that here on the audio thread to prevent changing the values during the
|
|
||||||
// process call and also to prevent inconsistent state when the host also wants to load
|
|
||||||
// plugin state.
|
|
||||||
// FIXME: Zero capacity channels allocate on receiving, find a better alternative that
|
|
||||||
// doesn't do that
|
|
||||||
let updated_state = permit_alloc(|| self.updated_state_receiver.try_recv());
|
|
||||||
if let Ok(state) = updated_state {
|
|
||||||
unsafe {
|
|
||||||
state::deserialize_object(
|
|
||||||
&state,
|
|
||||||
self.params.clone(),
|
|
||||||
|param_id| self.param_map.get(param_id).copied(),
|
|
||||||
Some(&self.buffer_config),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.notify_param_values_changed();
|
let sample_rate = self.buffer_config.sample_rate;
|
||||||
|
let mut plugin = self.plugin.write();
|
||||||
|
if let ProcessStatus::Error(err) = plugin.process(
|
||||||
|
buffer,
|
||||||
|
// TODO: Provide extra inputs and outputs in the JACk backend
|
||||||
|
&mut AuxiliaryBuffers {
|
||||||
|
inputs: &mut [],
|
||||||
|
outputs: &mut [],
|
||||||
|
},
|
||||||
|
&mut self.make_process_context(transport, input_events, output_events),
|
||||||
|
) {
|
||||||
|
nih_error!("The plugin returned an error while processing:");
|
||||||
|
nih_error!("{}", err);
|
||||||
|
|
||||||
// TODO: Normally we'd also call initialize after deserializing state, but that's
|
let push_successful = gui_task_sender.send(GuiTask::Close).is_ok();
|
||||||
// not guaranteed to be realtime safe. Should we do it anyways?
|
nih_debug_assert!(
|
||||||
self.plugin.write().reset();
|
push_successful,
|
||||||
|
"Could not queue window close, the editor will remain open"
|
||||||
// We'll pass the state object back to the GUI thread so deallocation can happen
|
|
||||||
// there without potentially blocking the audio thread
|
|
||||||
if let Err(err) = self.updated_state_sender.send(state) {
|
|
||||||
nih_debug_assert_failure!(
|
|
||||||
"Failed to send state object back to GUI thread: {}",
|
|
||||||
err
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any output note events are now in a vector that can be processed by the
|
||||||
|
// audio/MIDI backend
|
||||||
|
|
||||||
|
// We'll always write these events to the first sample, so even when we add note
|
||||||
|
// output we shouldn't have to think about interleaving events here
|
||||||
|
let mut parameter_values_changed = false;
|
||||||
|
while let Some((param_ptr, normalized_value)) =
|
||||||
|
self.unprocessed_param_changes.pop()
|
||||||
|
{
|
||||||
|
unsafe { param_ptr.set_normalized_value(normalized_value) };
|
||||||
|
unsafe { param_ptr.update_smoother(sample_rate, false) };
|
||||||
|
parameter_values_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow the editor to react to the new parameter values if the editor uses a
|
||||||
|
// reactive data binding model
|
||||||
|
if parameter_values_changed {
|
||||||
|
self.notify_param_values_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// After processing audio, we'll check if the editor has sent us updated plugin
|
||||||
|
// state. We'll restore that here on the audio thread to prevent changing the
|
||||||
|
// values during the process call and also to prevent inconsistent state when
|
||||||
|
// the host also wants to load plugin state.
|
||||||
|
// FIXME: Zero capacity channels allocate on receiving, find a better
|
||||||
|
// alternative that doesn't do that
|
||||||
|
let updated_state = permit_alloc(|| self.updated_state_receiver.try_recv());
|
||||||
|
if let Ok(state) = updated_state {
|
||||||
|
unsafe {
|
||||||
|
state::deserialize_object(
|
||||||
|
&state,
|
||||||
|
self.params.clone(),
|
||||||
|
|param_id| self.param_map.get(param_id).copied(),
|
||||||
|
Some(&self.buffer_config),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.notify_param_values_changed();
|
||||||
|
|
||||||
|
// TODO: Normally we'd also call initialize after deserializing state, but
|
||||||
|
// that's not guaranteed to be realtime safe. Should we do it anyways?
|
||||||
|
self.plugin.write().reset();
|
||||||
|
|
||||||
|
// We'll pass the state object back to the GUI thread so deallocation can
|
||||||
|
// happen there without potentially blocking the audio thread
|
||||||
|
if let Err(err) = self.updated_state_sender.send(state) {
|
||||||
|
nih_debug_assert_failure!(
|
||||||
|
"Failed to send state object back to GUI thread: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
})
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue