Add an AsyncExecutor for editor GUIs
This is decoupled form `GuiContext` as that would require invasive changes all over the place.
This commit is contained in:
parent
f3bb816cb5
commit
c980576102
|
@ -29,6 +29,8 @@ code then it will not be listed here.
|
||||||
changed to `&mut impl InitContext<Self>`.
|
changed to `&mut impl InitContext<Self>`.
|
||||||
- The `&mut impl ProcessContext` argument to `Plugin::process()` needs to be
|
- The `&mut impl ProcessContext` argument to `Plugin::process()` needs to be
|
||||||
changed to `&mut impl ProcessContext<Self>`.
|
changed to `&mut impl ProcessContext<Self>`.
|
||||||
|
- The `Plugin::editor()` method now also takes a
|
||||||
|
`_async_executor: AsyncExecutor<Self>` parameter.
|
||||||
|
|
||||||
## [2022-10-20]
|
## [2022-10-20]
|
||||||
|
|
||||||
|
|
|
@ -313,7 +313,7 @@ impl Plugin for Crisp {
|
||||||
self.params.clone()
|
self.params.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&self) -> Option<Box<dyn Editor>> {
|
fn editor(&self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
editor::create(self.params.clone(), self.params.editor_state.clone())
|
editor::create(self.params.clone(), self.params.editor_state.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,7 @@ impl Plugin for Diopser {
|
||||||
self.params.clone()
|
self.params.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&self) -> Option<Box<dyn Editor>> {
|
fn editor(&self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
editor::create(self.params.clone(), self.params.editor_state.clone())
|
editor::create(self.params.clone(), self.params.editor_state.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl Plugin for Gain {
|
||||||
self.params.clone()
|
self.params.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&self) -> Option<Box<dyn Editor>> {
|
fn editor(&self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
let params = self.params.clone();
|
let params = self.params.clone();
|
||||||
let peak_meter = self.peak_meter.clone();
|
let peak_meter = self.peak_meter.clone();
|
||||||
create_egui_editor(
|
create_egui_editor(
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl Plugin for Gain {
|
||||||
self.params.clone()
|
self.params.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&self) -> Option<Box<dyn Editor>> {
|
fn editor(&self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
editor::create(
|
editor::create(
|
||||||
self.params.clone(),
|
self.params.clone(),
|
||||||
self.peak_meter.clone(),
|
self.peak_meter.clone(),
|
||||||
|
|
|
@ -85,7 +85,7 @@ impl Plugin for Gain {
|
||||||
self.params.clone()
|
self.params.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&self) -> Option<Box<dyn Editor>> {
|
fn editor(&self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
editor::create(
|
editor::create(
|
||||||
self.params.clone(),
|
self.params.clone(),
|
||||||
self.peak_meter.clone(),
|
self.peak_meter.clone(),
|
||||||
|
|
|
@ -278,7 +278,7 @@ impl Plugin for SpectralCompressor {
|
||||||
self.params.clone()
|
self.params.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&self) -> Option<Box<dyn Editor>> {
|
fn editor(&self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
editor::create(self.params.clone(), self.editor_state.clone())
|
editor::create(self.params.clone(), self.editor_state.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::context::process::ProcessContext;
|
||||||
use crate::editor::Editor;
|
use crate::editor::Editor;
|
||||||
use crate::midi::MidiConfig;
|
use crate::midi::MidiConfig;
|
||||||
use crate::params::Params;
|
use crate::params::Params;
|
||||||
|
use crate::prelude::AsyncExecutor;
|
||||||
use crate::wrapper::clap::features::ClapFeature;
|
use crate::wrapper::clap::features::ClapFeature;
|
||||||
use crate::wrapper::state::PluginState;
|
use crate::wrapper::state::PluginState;
|
||||||
|
|
||||||
|
@ -122,7 +123,7 @@ pub trait Plugin: Default + Send + 'static {
|
||||||
/// access into the editor. You can later modify the parameters through the
|
/// access into the editor. You can later modify the parameters through the
|
||||||
/// [`GuiContext`][crate::prelude::GuiContext] and [`ParamSetter`][crate::prelude::ParamSetter] after the editor
|
/// [`GuiContext`][crate::prelude::GuiContext] and [`ParamSetter`][crate::prelude::ParamSetter] after the editor
|
||||||
/// GUI has been created.
|
/// GUI has been created.
|
||||||
fn editor(&self) -> Option<Box<dyn Editor>> {
|
fn editor(&self, async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContex
|
||||||
use super::descriptor::PluginDescriptor;
|
use super::descriptor::PluginDescriptor;
|
||||||
use super::util::ClapPtr;
|
use super::util::ClapPtr;
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
|
use crate::context::gui::AsyncExecutor;
|
||||||
use crate::context::process::Transport;
|
use crate::context::process::Transport;
|
||||||
use crate::editor::{Editor, ParentWindowHandle};
|
use crate::editor::{Editor, ParentWindowHandle};
|
||||||
use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
|
use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
|
||||||
|
@ -114,8 +115,8 @@ pub struct Wrapper<P: ClapPlugin> {
|
||||||
params: Arc<dyn Params>,
|
params: Arc<dyn Params>,
|
||||||
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
|
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
|
||||||
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
|
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
|
||||||
/// creating an editor.
|
/// creating an editor. Wrapped in an `AtomicRefCell` because it needs to be initialized late.
|
||||||
editor: Option<Mutex<Box<dyn Editor>>>,
|
editor: AtomicRefCell<Option<Mutex<Box<dyn Editor>>>>,
|
||||||
/// A handle for the currently active editor instance. The plugin should implement `Drop` on
|
/// A handle for the currently active editor instance. The plugin should implement `Drop` on
|
||||||
/// this handle for its closing behavior.
|
/// this handle for its closing behavior.
|
||||||
editor_handle: Mutex<Option<Box<dyn Any + Send>>>,
|
editor_handle: Mutex<Option<Box<dyn Any + Send>>>,
|
||||||
|
@ -388,7 +389,6 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
pub fn new(host_callback: *const clap_host) -> Arc<Self> {
|
pub fn new(host_callback: *const clap_host) -> Arc<Self> {
|
||||||
let plugin = P::default();
|
let plugin = P::default();
|
||||||
let task_executor = Mutex::new(plugin.task_executor());
|
let task_executor = Mutex::new(plugin.task_executor());
|
||||||
let editor = plugin.editor().map(Mutex::new);
|
|
||||||
|
|
||||||
// This is used to allow the plugin to restore preset data from its editor, see the comment
|
// This is used to allow the plugin to restore preset data from its editor, see the comment
|
||||||
// on `Self::updated_state_sender`
|
// on `Self::updated_state_sender`
|
||||||
|
@ -551,7 +551,8 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
plugin: Mutex::new(plugin),
|
plugin: Mutex::new(plugin),
|
||||||
task_executor,
|
task_executor,
|
||||||
params,
|
params,
|
||||||
editor,
|
// Initialized later as it needs a reference to the wrapper for the async executor
|
||||||
|
editor: AtomicRefCell::new(None),
|
||||||
editor_handle: Mutex::new(None),
|
editor_handle: Mutex::new(None),
|
||||||
editor_scaling_factor: AtomicF32::new(1.0),
|
editor_scaling_factor: AtomicF32::new(1.0),
|
||||||
|
|
||||||
|
@ -678,6 +679,22 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
let wrapper = Arc::new(wrapper);
|
let wrapper = Arc::new(wrapper);
|
||||||
*wrapper.this.borrow_mut() = Arc::downgrade(&wrapper);
|
*wrapper.this.borrow_mut() = Arc::downgrade(&wrapper);
|
||||||
|
|
||||||
|
// The editor also needs to be initialized later so the Async executor can work.
|
||||||
|
*wrapper.editor.borrow_mut() = wrapper
|
||||||
|
.plugin
|
||||||
|
.lock()
|
||||||
|
.editor(AsyncExecutor {
|
||||||
|
inner: Arc::new({
|
||||||
|
let wrapper = wrapper.clone();
|
||||||
|
|
||||||
|
move |task| {
|
||||||
|
let task_posted = wrapper.do_maybe_async(Task::PluginTask(task));
|
||||||
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.map(Mutex::new);
|
||||||
|
|
||||||
wrapper
|
wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,7 +742,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
/// the editor instance is currently locked then nothing will happen, and the request can safely
|
/// the editor instance is currently locked then nothing will happen, and the request can safely
|
||||||
/// be ignored.
|
/// be ignored.
|
||||||
pub fn notify_param_values_changed(&self) {
|
pub fn notify_param_values_changed(&self) {
|
||||||
if let Some(editor) = &self.editor {
|
if let Some(editor) = self.editor.borrow().as_ref() {
|
||||||
match editor.try_lock() {
|
match editor.try_lock() {
|
||||||
Some(editor) => editor.param_values_changed(),
|
Some(editor) => editor.param_values_changed(),
|
||||||
None => nih_debug_assert_failure!(
|
None => nih_debug_assert_failure!(
|
||||||
|
@ -740,7 +757,10 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
/// safely be called from any thread. If this returns `false`, then the plugin should reset its
|
/// safely be called from any thread. If this returns `false`, then the plugin should reset its
|
||||||
/// size back to the previous value.
|
/// size back to the previous value.
|
||||||
pub fn request_resize(&self) -> bool {
|
pub fn request_resize(&self) -> bool {
|
||||||
match (&*self.host_gui.borrow(), &self.editor) {
|
match (
|
||||||
|
self.host_gui.borrow().as_ref(),
|
||||||
|
self.editor.borrow().as_ref(),
|
||||||
|
) {
|
||||||
(Some(host_gui), Some(editor)) => {
|
(Some(host_gui), Some(editor)) => {
|
||||||
let (unscaled_width, unscaled_height) = editor.lock().size();
|
let (unscaled_width, unscaled_height) = editor.lock().size();
|
||||||
let scaling_factor = self.editor_scaling_factor.load(Ordering::Relaxed);
|
let scaling_factor = self.editor_scaling_factor.load(Ordering::Relaxed);
|
||||||
|
@ -2300,7 +2320,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
&wrapper.clap_plugin_audio_ports_config as *const _ as *const c_void
|
&wrapper.clap_plugin_audio_ports_config as *const _ as *const c_void
|
||||||
} else if id == CLAP_EXT_AUDIO_PORTS {
|
} else if id == CLAP_EXT_AUDIO_PORTS {
|
||||||
&wrapper.clap_plugin_audio_ports as *const _ as *const c_void
|
&wrapper.clap_plugin_audio_ports as *const _ as *const c_void
|
||||||
} else if id == CLAP_EXT_GUI && wrapper.editor.is_some() {
|
} else if id == CLAP_EXT_GUI && wrapper.editor.borrow().is_some() {
|
||||||
// Only report that we support this extension if the plugin has an editor
|
// Only report that we support this extension if the plugin has an editor
|
||||||
&wrapper.clap_plugin_gui as *const _ as *const c_void
|
&wrapper.clap_plugin_gui as *const _ as *const c_void
|
||||||
} else if id == CLAP_EXT_LATENCY {
|
} else if id == CLAP_EXT_LATENCY {
|
||||||
|
@ -2686,6 +2706,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
|
|
||||||
if wrapper
|
if wrapper
|
||||||
.editor
|
.editor
|
||||||
|
.borrow()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -2709,7 +2730,8 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
// For macOS the scaling factor is always 1
|
// For macOS the scaling factor is always 1
|
||||||
let (unscaled_width, unscaled_height) = wrapper.editor.as_ref().unwrap().lock().size();
|
let (unscaled_width, unscaled_height) =
|
||||||
|
wrapper.editor.borrow().as_ref().unwrap().lock().size();
|
||||||
let scaling_factor = wrapper.editor_scaling_factor.load(Ordering::Relaxed);
|
let scaling_factor = wrapper.editor_scaling_factor.load(Ordering::Relaxed);
|
||||||
(*width, *height) = (
|
(*width, *height) = (
|
||||||
(unscaled_width as f32 * scaling_factor).round() as u32,
|
(unscaled_width as f32 * scaling_factor).round() as u32,
|
||||||
|
@ -2751,7 +2773,8 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
check_null_ptr!(false, plugin);
|
check_null_ptr!(false, plugin);
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
let (unscaled_width, unscaled_height) = wrapper.editor.as_ref().unwrap().lock().size();
|
let (unscaled_width, unscaled_height) =
|
||||||
|
wrapper.editor.borrow().as_ref().unwrap().lock().size();
|
||||||
let scaling_factor = wrapper.editor_scaling_factor.load(Ordering::Relaxed);
|
let scaling_factor = wrapper.editor_scaling_factor.load(Ordering::Relaxed);
|
||||||
let (editor_width, editor_height) = (
|
let (editor_width, editor_height) = (
|
||||||
(unscaled_width as f32 * scaling_factor).round() as u32,
|
(unscaled_width as f32 * scaling_factor).round() as u32,
|
||||||
|
@ -2793,7 +2816,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// This extension is only exposed when we have an editor
|
// This extension is only exposed when we have an editor
|
||||||
*editor_handle = Some(wrapper.editor.as_ref().unwrap().lock().spawn(
|
*editor_handle = Some(wrapper.editor.borrow().as_ref().unwrap().lock().spawn(
|
||||||
ParentWindowHandle { handle },
|
ParentWindowHandle { handle },
|
||||||
wrapper.clone().make_gui_context(),
|
wrapper.clone().make_gui_context(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -108,7 +108,8 @@ impl<P: Plugin, B: Backend> GuiContext for WrapperGuiContext<P, B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_resize(&self) -> bool {
|
fn request_resize(&self) -> bool {
|
||||||
let (unscaled_width, unscaled_height) = self.wrapper.editor.as_ref().unwrap().lock().size();
|
let (unscaled_width, unscaled_height) =
|
||||||
|
self.wrapper.editor.borrow().as_ref().unwrap().lock().size();
|
||||||
|
|
||||||
// This will cause the editor to be resized at the start of the next frame
|
// This will cause the editor to be resized at the start of the next frame
|
||||||
let push_successful = self
|
let push_successful = self
|
||||||
|
|
|
@ -13,6 +13,7 @@ use std::thread;
|
||||||
use super::backend::Backend;
|
use super::backend::Backend;
|
||||||
use super::config::WrapperConfig;
|
use super::config::WrapperConfig;
|
||||||
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
|
||||||
|
use crate::context::gui::AsyncExecutor;
|
||||||
use crate::context::process::Transport;
|
use crate::context::process::Transport;
|
||||||
use crate::editor::{Editor, ParentWindowHandle};
|
use crate::editor::{Editor, ParentWindowHandle};
|
||||||
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
|
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
|
||||||
|
@ -51,8 +52,8 @@ pub struct Wrapper<P: Plugin, B: Backend> {
|
||||||
param_map: HashMap<String, ParamPtr>,
|
param_map: HashMap<String, ParamPtr>,
|
||||||
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
|
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
|
||||||
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
|
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
|
||||||
/// creating an editor.
|
/// creating an editor. Wrapped in an `AtomicRefCell` because it needs to be initialized late.
|
||||||
pub editor: Option<Arc<Mutex<Box<dyn Editor>>>>,
|
pub editor: AtomicRefCell<Option<Arc<Mutex<Box<dyn Editor>>>>>,
|
||||||
|
|
||||||
/// A realtime-safe task queue so the plugin can schedule tasks that need to be run later on the
|
/// A realtime-safe task queue so the plugin can schedule tasks that need to be run later on the
|
||||||
/// GUI thread. See the same field in the VST3 wrapper for more information on why this looks
|
/// GUI thread. See the same field in the VST3 wrapper for more information on why this looks
|
||||||
|
@ -159,7 +160,6 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
task_executor: Mutex::new(plugin.task_executor()),
|
task_executor: Mutex::new(plugin.task_executor()),
|
||||||
});
|
});
|
||||||
let params = plugin.params();
|
let params = plugin.params();
|
||||||
let editor = plugin.editor().map(|editor| Arc::new(Mutex::new(editor)));
|
|
||||||
|
|
||||||
// This is used to allow the plugin to restore preset data from its editor, see the comment
|
// This is used to allow the plugin to restore preset data from its editor, see the comment
|
||||||
// on `Self::updated_state_sender`
|
// on `Self::updated_state_sender`
|
||||||
|
@ -211,7 +211,8 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(param_id, param_ptr, _)| (param_id, param_ptr))
|
.map(|(param_id, param_ptr, _)| (param_id, param_ptr))
|
||||||
.collect(),
|
.collect(),
|
||||||
editor,
|
// Initialized later as it needs a reference to the wrapper for the async executor
|
||||||
|
editor: AtomicRefCell::new(None),
|
||||||
|
|
||||||
event_loop: OsEventLoop::new_and_spawn(Arc::downgrade(&task_executor_wrapper)),
|
event_loop: OsEventLoop::new_and_spawn(Arc::downgrade(&task_executor_wrapper)),
|
||||||
|
|
||||||
|
@ -236,6 +237,22 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
updated_state_receiver,
|
updated_state_receiver,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The editor needs to be initialized later so the Async executor can work.
|
||||||
|
*wrapper.editor.borrow_mut() = wrapper
|
||||||
|
.plugin
|
||||||
|
.lock()
|
||||||
|
.editor(AsyncExecutor {
|
||||||
|
inner: Arc::new({
|
||||||
|
let wrapper = wrapper.clone();
|
||||||
|
|
||||||
|
move |task| {
|
||||||
|
let task_posted = wrapper.event_loop.do_maybe_async(task);
|
||||||
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.map(|editor| Arc::new(Mutex::new(editor)));
|
||||||
|
|
||||||
// Right now the IO configuration is fixed in the standalone target, so if the plugin cannot
|
// Right now the IO configuration is fixed in the standalone target, so if the plugin cannot
|
||||||
// work with this then we cannot initialize the plugin at all.
|
// work with this then we cannot initialize the plugin at all.
|
||||||
{
|
{
|
||||||
|
@ -283,7 +300,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
thread::spawn(move || this.run_audio_thread(terminate_audio_thread, gui_task_sender))
|
thread::spawn(move || this.run_audio_thread(terminate_audio_thread, gui_task_sender))
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.editor.clone() {
|
match self.editor.borrow().clone() {
|
||||||
Some(editor) => {
|
Some(editor) => {
|
||||||
let context = self.clone().make_gui_context(gui_task_sender);
|
let context = self.clone().make_gui_context(gui_task_sender);
|
||||||
|
|
||||||
|
@ -511,7 +528,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
|
||||||
/// off-chance that the editor instance is currently locked then nothing will happen, and the
|
/// off-chance that the editor instance is currently locked then nothing will happen, and the
|
||||||
/// request can safely be ignored.
|
/// request can safely be ignored.
|
||||||
fn notify_param_values_changed(&self) {
|
fn notify_param_values_changed(&self) {
|
||||||
if let Some(editor) = &self.editor {
|
if let Some(editor) = self.editor.borrow().as_ref() {
|
||||||
match editor.try_lock() {
|
match editor.try_lock() {
|
||||||
Some(editor) => editor.param_values_changed(),
|
Some(editor) => editor.param_values_changed(),
|
||||||
None => nih_debug_assert_failure!(
|
None => nih_debug_assert_failure!(
|
||||||
|
|
|
@ -15,6 +15,7 @@ use super::param_units::ParamUnits;
|
||||||
use super::util::{ObjectPtr, VstPtr, VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_START};
|
use super::util::{ObjectPtr, VstPtr, VST3_MIDI_PARAMS_END, VST3_MIDI_PARAMS_START};
|
||||||
use super::view::WrapperView;
|
use super::view::WrapperView;
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
|
use crate::context::gui::AsyncExecutor;
|
||||||
use crate::context::process::Transport;
|
use crate::context::process::Transport;
|
||||||
use crate::editor::Editor;
|
use crate::editor::Editor;
|
||||||
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
|
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
|
||||||
|
@ -41,8 +42,8 @@ pub(crate) struct WrapperInner<P: Vst3Plugin> {
|
||||||
pub params: Arc<dyn Params>,
|
pub params: Arc<dyn Params>,
|
||||||
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
|
/// The plugin's editor, if it has one. This object does not do anything on its own, but we need
|
||||||
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
|
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
|
||||||
/// creating an editor.
|
/// creating an editor. Wrapped in an `AtomicRefCell` because it needs to be initialized late.
|
||||||
pub editor: Option<Arc<Mutex<Box<dyn Editor>>>>,
|
pub editor: AtomicRefCell<Option<Arc<Mutex<Box<dyn Editor>>>>>,
|
||||||
|
|
||||||
/// The host's [`IComponentHandler`] instance, if passed through
|
/// The host's [`IComponentHandler`] instance, if passed through
|
||||||
/// [`IEditController::set_component_handler`].
|
/// [`IEditController::set_component_handler`].
|
||||||
|
@ -198,7 +199,6 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
pub fn new() -> Arc<Self> {
|
pub fn new() -> Arc<Self> {
|
||||||
let plugin = P::default();
|
let plugin = P::default();
|
||||||
let task_executor = Mutex::new(plugin.task_executor());
|
let task_executor = Mutex::new(plugin.task_executor());
|
||||||
let editor = plugin.editor().map(|editor| Arc::new(Mutex::new(editor)));
|
|
||||||
|
|
||||||
// This is used to allow the plugin to restore preset data from its editor, see the comment
|
// This is used to allow the plugin to restore preset data from its editor, see the comment
|
||||||
// on `Self::updated_state_sender`
|
// on `Self::updated_state_sender`
|
||||||
|
@ -283,7 +283,8 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
plugin: Mutex::new(plugin),
|
plugin: Mutex::new(plugin),
|
||||||
task_executor,
|
task_executor,
|
||||||
params,
|
params,
|
||||||
editor,
|
// Initialized later as it needs a reference to the wrapper for the async executor
|
||||||
|
editor: AtomicRefCell::new(None),
|
||||||
|
|
||||||
component_handler: AtomicRefCell::new(None),
|
component_handler: AtomicRefCell::new(None),
|
||||||
|
|
||||||
|
@ -331,6 +332,22 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
*wrapper.event_loop.borrow_mut() =
|
*wrapper.event_loop.borrow_mut() =
|
||||||
Some(OsEventLoop::new_and_spawn(Arc::downgrade(&wrapper)));
|
Some(OsEventLoop::new_and_spawn(Arc::downgrade(&wrapper)));
|
||||||
|
|
||||||
|
// The editor also needs to be initialized later so the Async executor can work.
|
||||||
|
*wrapper.editor.borrow_mut() = wrapper
|
||||||
|
.plugin
|
||||||
|
.lock()
|
||||||
|
.editor(AsyncExecutor {
|
||||||
|
inner: Arc::new({
|
||||||
|
let wrapper = wrapper.clone();
|
||||||
|
|
||||||
|
move |task| {
|
||||||
|
let task_posted = wrapper.do_maybe_async(Task::PluginTask(task));
|
||||||
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.map(|editor| Arc::new(Mutex::new(editor)));
|
||||||
|
|
||||||
wrapper
|
wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +402,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
|
||||||
/// that the editor instance is currently locked then nothing will happen, and the request can
|
/// that the editor instance is currently locked then nothing will happen, and the request can
|
||||||
/// safely be ignored.
|
/// safely be ignored.
|
||||||
pub fn notify_param_values_changed(&self) {
|
pub fn notify_param_values_changed(&self) {
|
||||||
if let Some(editor) = &self.editor {
|
if let Some(editor) = self.editor.borrow().as_ref() {
|
||||||
match editor.try_lock() {
|
match editor.try_lock() {
|
||||||
Some(editor) => editor.param_values_changed(),
|
Some(editor) => editor.param_values_changed(),
|
||||||
None => nih_debug_assert_failure!(
|
None => nih_debug_assert_failure!(
|
||||||
|
|
|
@ -771,7 +771,7 @@ impl<P: Vst3Plugin> IEditController for Wrapper<P> {
|
||||||
unsafe fn create_view(&self, _name: vst3_sys::base::FIDString) -> *mut c_void {
|
unsafe fn create_view(&self, _name: vst3_sys::base::FIDString) -> *mut c_void {
|
||||||
// Without specialization this is the least redundant way to check if the plugin has an
|
// Without specialization this is the least redundant way to check if the plugin has an
|
||||||
// editor. The default implementation returns a None here.
|
// editor. The default implementation returns a None here.
|
||||||
match &self.inner.editor {
|
match self.inner.editor.borrow().as_ref() {
|
||||||
Some(editor) => Box::into_raw(WrapperView::new(self.inner.clone(), editor.clone()))
|
Some(editor) => Box::into_raw(WrapperView::new(self.inner.clone(), editor.clone()))
|
||||||
as *mut vst3_sys::c_void,
|
as *mut vst3_sys::c_void,
|
||||||
None => ptr::null_mut(),
|
None => ptr::null_mut(),
|
||||||
|
|
Loading…
Reference in a new issue