1
0
Fork 0

Replace AsyncExecutor with simple closure

This makes everything much simpler. The task type is now defined
directly on `Plugin`.
This commit is contained in:
Robbert van der Helm 2022-10-22 01:59:29 +02:00
parent 84f834abb6
commit 5ea2377c18
28 changed files with 85 additions and 125 deletions

View file

@ -14,13 +14,12 @@ code then it will not be listed here.
changed to `&mut impl InitContext<Self>`. changed to `&mut impl InitContext<Self>`.
- NIH-plug has gained support for asynchronously running background tasks in a - NIH-plug has gained support for asynchronously running background tasks in a
simple, type-safe, and realtime-safe way. This sadly does mean that every simple, type-safe, and realtime-safe way. This sadly does mean that every
`Plugin` instance now needs to define an `AsyncExecutor` type definition and `Plugin` instance now needs to define a `BackgroundTask` type definition and
constructor function as Rust does not yet support defaults for associated constructor function as Rust does not yet support defaults for associated
types (Rust issue [#29661](https://github.com/rust-lang/rust/issues/29661)): types (Rust issue [#29661](https://github.com/rust-lang/rust/issues/29661)):
```rust ```rust
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
``` ```
- The `Editor` trait and the `ParentWindowHandle` struct have been moved from - The `Editor` trait and the `ParentWindowHandle` struct have been moved from

View file

@ -307,8 +307,7 @@ impl Plugin for Crisp {
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -183,8 +183,7 @@ impl Plugin for Crossover {
aux_outputs: Some(&["Band 1", "Band 2", "Band 3", "Band 4", "Band 5"]), aux_outputs: Some(&["Band 1", "Band 2", "Band 3", "Band 4", "Band 5"]),
}; };
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -252,8 +252,7 @@ impl Plugin for Diopser {
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -132,11 +132,10 @@ impl Plugin for Gain {
// splits. // splits.
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
// More advanced plugins can use this to run expensive background tasks. See the `AsyncExecutor` // More advanced plugins can use this to run expensive background tasks. See the field's
// trait's documentation for more information. `()` means that the plugin does not have any // documentation for more information. `()` means that the plugin does not have any background
// background tasks. // tasks.
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -83,8 +83,7 @@ impl Plugin for Gain {
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -80,8 +80,7 @@ impl Plugin for Gain {
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -79,8 +79,7 @@ impl Plugin for Gain {
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -33,8 +33,7 @@ impl Plugin for MidiInverter {
const MIDI_OUTPUT: MidiConfig = MidiConfig::MidiCCs; const MIDI_OUTPUT: MidiConfig = MidiConfig::MidiCCs;
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -155,8 +155,7 @@ impl Plugin for PolyModSynth {
const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const MIDI_INPUT: MidiConfig = MidiConfig::Basic;
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -111,8 +111,7 @@ impl Plugin for Sine {
const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const MIDI_INPUT: MidiConfig = MidiConfig::Basic;
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -95,8 +95,7 @@ impl Plugin for Stft {
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -123,8 +123,7 @@ impl Plugin for LoudnessWarWinner {
const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_INPUT_CHANNELS: u32 = 2;
const DEFAULT_OUTPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -168,8 +168,7 @@ impl Plugin for PubertySimulator {
const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_INPUT_CHANNELS: u32 = 2;
const DEFAULT_OUTPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -157,8 +157,7 @@ impl Plugin for SafetyLimiter {
const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_INPUT_CHANNELS: u32 = 2;
const DEFAULT_OUTPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -272,8 +272,7 @@ impl Plugin for SpectralCompressor {
const SAMPLE_ACCURATE_AUTOMATION: bool = true; const SAMPLE_ACCURATE_AUTOMATION: bool = true;
type AsyncExecutor = (); type BackgroundTask = ();
fn async_executor(&self) -> Self::AsyncExecutor {}
fn params(&self) -> Arc<dyn Params> { fn params(&self) -> Arc<dyn Params> {
self.params.clone() self.params.clone()

View file

@ -1,23 +0,0 @@
//! Traits for running background tasks from a [`Plugin`][crate::prelude::Plugin].
//!
//! This should not be confused with the `async` language features and ecosystem in Rust.
/// Something that can run tasks of type [`Task`][Self::Task]. This can be used to defer expensive
/// computations to a background thread. Tasks can be spawned through the methods on the various
/// [`*Context`][crate::context] types.
pub trait AsyncExecutor: Send + Sync {
/// The type of task this executor can execute. This is usually an enum type. The task type
/// should not contain any heap allocated data like [`Vec`]s and [`Box`]es.
type Task: Send;
/// Run `task` on the current thread. This is usually called from the operating system's main
/// thread or a similar thread.
fn execute(&self, task: Self::Task);
}
/// A default implementation for plugins that don't need asynchronous background tasks.
impl AsyncExecutor for () {
type Task = ();
fn execute(&self, _task: Self::Task) {}
}

View file

@ -5,7 +5,7 @@ use std::fmt::Display;
use crate::midi::NoteEvent; use crate::midi::NoteEvent;
use crate::params::internals::ParamPtr; use crate::params::internals::ParamPtr;
use crate::params::Param; use crate::params::Param;
use crate::prelude::{AsyncExecutor, Plugin}; use crate::plugin::Plugin;
use crate::wrapper::state::PluginState; use crate::wrapper::state::PluginState;
/// Callbacks the plugin can make while it is being initialized. This is passed to the plugin during /// Callbacks the plugin can make while it is being initialized. This is passed to the plugin during
@ -23,7 +23,7 @@ pub trait InitContext<P: Plugin> {
/// ///
/// There is no asynchronous alternative for this function as that may result in incorrect /// There is no asynchronous alternative for this function as that may result in incorrect
/// behavior when doing offline rendering. /// behavior when doing offline rendering.
fn execute(&self, task: <P::AsyncExecutor as AsyncExecutor>::Task); fn execute(&self, task: P::BackgroundTask);
/// Get the current plugin API. /// Get the current plugin API.
fn plugin_api(&self) -> PluginApi; fn plugin_api(&self) -> PluginApi;
@ -53,7 +53,7 @@ pub trait InitContext<P: Plugin> {
pub trait ProcessContext<P: Plugin> { pub trait ProcessContext<P: Plugin> {
/// Run a task on a background thread. This allows defering expensive background tasks for /// Run a task on a background thread. This allows defering expensive background tasks for
/// alter. As long as creating the `task` is realtime-safe, this operation is too. /// alter. As long as creating the `task` is realtime-safe, this operation is too.
fn execute_async(&self, task: <P::AsyncExecutor as AsyncExecutor>::Task); fn execute_async(&self, task: P::BackgroundTask);
/// Get the current plugin API. /// Get the current plugin API.
fn plugin_api(&self) -> PluginApi; fn plugin_api(&self) -> PluginApi;

View file

@ -10,8 +10,6 @@ mod linux;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod windows; mod windows;
use crate::prelude::AsyncExecutor;
#[cfg(all(target_family = "unix", not(target_os = "macos")))] #[cfg(all(target_family = "unix", not(target_os = "macos")))]
pub(crate) use self::linux::LinuxEventLoop as OsEventLoop; pub(crate) use self::linux::LinuxEventLoop as OsEventLoop;
// For now, also use the Linux event loop on macOS so it at least compiles // For now, also use the Linux event loop on macOS so it at least compiles
@ -68,11 +66,3 @@ pub(crate) trait MainThreadExecutor<T>: Send + Sync {
/// assume (and can only assume) that this is called from the main thread. /// assume (and can only assume) that this is called from the main thread.
unsafe fn execute(&self, task: T); unsafe fn execute(&self, task: T);
} }
/// An adapter implementation to allow any [`AsyncExecutor`] to function as a
/// [`MainThreadExecutor`]. Used only in the standalone wrapper.
impl<T: Send, E: AsyncExecutor<Task = T>> MainThreadExecutor<T> for E {
unsafe fn execute(&self, task: T) {
AsyncExecutor::execute(self, task);
}
}

View file

@ -101,7 +101,6 @@ pub mod prelude;
pub mod formatters; pub mod formatters;
pub mod util; pub mod util;
pub mod async_executor;
pub mod buffer; pub mod buffer;
pub mod context; pub mod context;
pub mod editor; pub mod editor;

View file

@ -2,7 +2,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::async_executor::AsyncExecutor;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::context::{InitContext, ProcessContext}; use crate::context::{InitContext, ProcessContext};
use crate::editor::Editor; use crate::editor::Editor;
@ -11,6 +10,8 @@ use crate::params::Params;
use crate::wrapper::clap::features::ClapFeature; use crate::wrapper::clap::features::ClapFeature;
use crate::wrapper::state::PluginState; use crate::wrapper::state::PluginState;
pub type TaskExecutor<P> = Box<dyn Fn(<P as Plugin>::BackgroundTask) + Send + Sync>;
/// Basic functionality that needs to be implemented by a plugin. The wrappers will use this to /// Basic functionality that needs to be implemented by a plugin. The wrappers will use this to
/// expose the plugin in a particular plugin format. /// expose the plugin in a particular plugin format.
/// ///
@ -90,19 +91,21 @@ pub trait Plugin: Default + Send + 'static {
/// to do offline processing. /// to do offline processing.
const HARD_REALTIME_ONLY: bool = false; const HARD_REALTIME_ONLY: bool = false;
/// The plugin's [`AsyncExecutor`] type. Use `()` if the plugin does not need to perform /// A type encoding this plugin's different background tasks, if it needs those. This is usually
/// expensive background tasks. /// an enum type. The task type should not contain any heap allocated data like [`Vec`]s and
// /// [`Box`]es. Tasks can be send using the methods on the various [`*Context`][crate::context]
// This needs to be an associated type so we can have a nice type safe interface in the /// objects.
// `*Context` traits. ///
/// Use `()` if the plugin does not need any background tasks.
// //
// NOTE: Sadly it's not yet possible to default this and the `async_executor()` function to // NOTE: Sadly it's not yet possible to default this and the `async_executor()` function to
// `()`: https://github.com/rust-lang/rust/issues/29661 // `()`: https://github.com/rust-lang/rust/issues/29661
type AsyncExecutor: AsyncExecutor; type BackgroundTask: Send;
/// The plugin's background task executor. See [`BackgroundTask`][Self::BackgroundTask].
/// The plugin's background task executor. Use `()` if the plugin does not need this fn task_executor(&self) -> TaskExecutor<Self> {
/// functinlality. // In the default implementation we can simply ignore the value
fn async_executor(&self) -> Self::AsyncExecutor; Box::new(|_| ())
}
/// The plugin's parameters. The host will update the parameter values before calling /// The plugin's parameters. The host will update the parameter values before calling
/// `process()`. These parameters are identified by strings that should never change when the /// `process()`. These parameters are identified by strings that should never change when the

View file

@ -10,7 +10,6 @@ pub use crate::wrapper::standalone::{nih_export_standalone, nih_export_standalon
pub use crate::formatters; pub use crate::formatters;
pub use crate::util; pub use crate::util;
pub use crate::async_executor::AsyncExecutor;
pub use crate::buffer::Buffer; pub use crate::buffer::Buffer;
pub use crate::context::{GuiContext, InitContext, ParamSetter, PluginApi, ProcessContext}; pub use crate::context::{GuiContext, InitContext, ParamSetter, PluginApi, ProcessContext};
// This also includes the derive macro // This also includes the derive macro
@ -24,7 +23,7 @@ pub use crate::params::Params;
pub use crate::params::{BoolParam, FloatParam, IntParam, Param, ParamFlags}; pub use crate::params::{BoolParam, FloatParam, IntParam, Param, ParamFlags};
pub use crate::plugin::{ pub use crate::plugin::{
AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, ClapPlugin, Plugin, AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, ClapPlugin, Plugin,
PolyModulationConfig, PortNames, ProcessMode, ProcessStatus, Vst3Plugin, PolyModulationConfig, PortNames, ProcessMode, ProcessStatus, TaskExecutor, Vst3Plugin,
}; };
pub use crate::wrapper::clap::features::ClapFeature; pub use crate::wrapper::clap::features::ClapFeature;
pub use crate::wrapper::state::PluginState; pub use crate::wrapper::state::PluginState;

View file

@ -3,7 +3,6 @@ use std::collections::VecDeque;
use std::sync::Arc; use std::sync::Arc;
use super::wrapper::{OutputParamEvent, Task, Wrapper}; use super::wrapper::{OutputParamEvent, Task, Wrapper};
use crate::async_executor::AsyncExecutor;
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport}; use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
use crate::event_loop::EventLoop; use crate::event_loop::EventLoop;
use crate::midi::NoteEvent; use crate::midi::NoteEvent;
@ -115,8 +114,8 @@ impl<P: ClapPlugin> GuiContext for WrapperGuiContext<P> {
} }
impl<P: ClapPlugin> InitContext<P> for WrapperInitContext<'_, P> { impl<P: ClapPlugin> InitContext<P> for WrapperInitContext<'_, P> {
fn execute(&self, task: <P::AsyncExecutor as crate::prelude::AsyncExecutor>::Task) { fn execute(&self, task: P::BackgroundTask) {
self.wrapper.async_executor.execute(task); (self.wrapper.task_executor)(task);
} }
fn plugin_api(&self) -> PluginApi { fn plugin_api(&self) -> PluginApi {
@ -133,10 +132,7 @@ impl<P: ClapPlugin> InitContext<P> for WrapperInitContext<'_, P> {
} }
impl<P: ClapPlugin> ProcessContext<P> for WrapperProcessContext<'_, P> { impl<P: ClapPlugin> ProcessContext<P> for WrapperProcessContext<'_, P> {
fn execute_async( fn execute_async(&self, task: P::BackgroundTask) {
&self,
task: <<P as crate::prelude::Plugin>::AsyncExecutor as AsyncExecutor>::Task,
) {
let task_posted = self.wrapper.do_maybe_async(Task::PluginTask(task)); let task_posted = self.wrapper.do_maybe_async(Task::PluginTask(task));
nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
} }

View file

@ -76,7 +76,6 @@ use std::time::Duration;
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext}; use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
use super::descriptor::PluginDescriptor; use super::descriptor::PluginDescriptor;
use super::util::ClapPtr; use super::util::ClapPtr;
use crate::async_executor::AsyncExecutor;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::context::Transport; use crate::context::Transport;
use crate::editor::{Editor, ParentWindowHandle}; use crate::editor::{Editor, ParentWindowHandle};
@ -86,6 +85,7 @@ use crate::params::internals::ParamPtr;
use crate::params::{ParamFlags, Params}; use crate::params::{ParamFlags, Params};
use crate::plugin::{ use crate::plugin::{
AuxiliaryBuffers, BufferConfig, BusConfig, ClapPlugin, Plugin, ProcessMode, ProcessStatus, AuxiliaryBuffers, BufferConfig, BusConfig, ClapPlugin, Plugin, ProcessMode, ProcessStatus,
TaskExecutor,
}; };
use crate::util::permit_alloc; use crate::util::permit_alloc;
use crate::wrapper::clap::util::{read_stream, write_stream}; use crate::wrapper::clap::util::{read_stream, write_stream};
@ -106,8 +106,8 @@ pub struct Wrapper<P: ClapPlugin> {
/// The wrapped plugin instance. /// The wrapped plugin instance.
plugin: Mutex<P>, plugin: Mutex<P>,
/// The plugin's background task executor. /// The plugin's background task executor closure.
pub async_executor: P::AsyncExecutor, pub task_executor: TaskExecutor<P>,
/// The plugin's parameters. These are fetched once during initialization. That way the /// The plugin's parameters. These are fetched once during initialization. That way the
/// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with /// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with
/// the `Params` object without having to acquire a lock on `plugin`. /// the `Params` object without having to acquire a lock on `plugin`.
@ -272,7 +272,7 @@ pub struct Wrapper<P: ClapPlugin> {
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
pub enum Task<P: Plugin> { pub enum Task<P: Plugin> {
/// Execute one of the plugin's background tasks. /// Execute one of the plugin's background tasks.
PluginTask(<P::AsyncExecutor as AsyncExecutor>::Task), PluginTask(P::BackgroundTask),
/// Inform the host that the latency has changed. /// Inform the host that the latency has changed.
LatencyChanged, LatencyChanged,
/// Inform the host that the voice info has changed. /// Inform the host that the voice info has changed.
@ -353,7 +353,7 @@ impl<P: ClapPlugin> MainThreadExecutor<Task<P>> for Wrapper<P> {
unsafe fn execute(&self, task: Task<P>) { unsafe fn execute(&self, task: Task<P>) {
// This function is always called from the main thread, from [Self::on_main_thread]. // This function is always called from the main thread, from [Self::on_main_thread].
match task { match task {
Task::PluginTask(task) => AsyncExecutor::execute(&self.async_executor, task), Task::PluginTask(task) => (self.task_executor)(task),
Task::LatencyChanged => match &*self.host_latency.borrow() { Task::LatencyChanged => match &*self.host_latency.borrow() {
Some(host_latency) => { Some(host_latency) => {
// XXX: The CLAP docs mention that you should request a restart if this happens // XXX: The CLAP docs mention that you should request a restart if this happens
@ -387,7 +387,7 @@ impl<P: ClapPlugin> MainThreadExecutor<Task<P>> for Wrapper<P> {
impl<P: ClapPlugin> Wrapper<P> { 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 async_executor = plugin.async_executor(); let task_executor = plugin.task_executor();
let editor = plugin.editor().map(Mutex::new); 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
@ -549,7 +549,7 @@ impl<P: ClapPlugin> Wrapper<P> {
this: AtomicRefCell::new(Weak::new()), this: AtomicRefCell::new(Weak::new()),
plugin: Mutex::new(plugin), plugin: Mutex::new(plugin),
async_executor, task_executor,
params, params,
editor, editor,
editor_handle: Mutex::new(None), editor_handle: Mutex::new(None),

View file

@ -3,7 +3,6 @@ use std::sync::Arc;
use super::backend::Backend; use super::backend::Backend;
use super::wrapper::{GuiTask, Wrapper}; use super::wrapper::{GuiTask, Wrapper};
use crate::async_executor::AsyncExecutor;
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport}; use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
use crate::event_loop::EventLoop; use crate::event_loop::EventLoop;
use crate::midi::NoteEvent; use crate::midi::NoteEvent;
@ -80,8 +79,8 @@ impl<P: Plugin, B: Backend> GuiContext for WrapperGuiContext<P, B> {
} }
impl<P: Plugin, B: Backend> InitContext<P> for WrapperInitContext<'_, P, B> { impl<P: Plugin, B: Backend> InitContext<P> for WrapperInitContext<'_, P, B> {
fn execute(&self, task: <<P as Plugin>::AsyncExecutor as crate::prelude::AsyncExecutor>::Task) { fn execute(&self, task: P::BackgroundTask) {
self.wrapper.async_executor.execute(task); (self.wrapper.task_executor_wrapper.task_executor)(task);
} }
fn plugin_api(&self) -> PluginApi { fn plugin_api(&self) -> PluginApi {
@ -98,7 +97,7 @@ impl<P: Plugin, B: Backend> InitContext<P> for WrapperInitContext<'_, P, B> {
} }
impl<P: Plugin, B: Backend> ProcessContext<P> for WrapperProcessContext<'_, P, B> { impl<P: Plugin, B: Backend> ProcessContext<P> for WrapperProcessContext<'_, P, B> {
fn execute_async(&self, task: <P::AsyncExecutor as AsyncExecutor>::Task) { fn execute_async(&self, task: P::BackgroundTask) {
let task_posted = self.wrapper.event_loop.do_maybe_async(task); let task_posted = self.wrapper.event_loop.do_maybe_async(task);
nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
} }

View file

@ -13,16 +13,15 @@ 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::async_executor::AsyncExecutor;
use crate::context::Transport; use crate::context::Transport;
use crate::editor::{Editor, ParentWindowHandle}; use crate::editor::{Editor, ParentWindowHandle};
use crate::event_loop::{EventLoop, OsEventLoop}; use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
use crate::midi::NoteEvent; use crate::midi::NoteEvent;
use crate::params::internals::ParamPtr; use crate::params::internals::ParamPtr;
use crate::params::{ParamFlags, Params}; use crate::params::{ParamFlags, Params};
use crate::plugin::{ use crate::plugin::{
AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, Plugin, ProcessMode, AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, Plugin, ProcessMode,
ProcessStatus, ProcessStatus, TaskExecutor,
}; };
use crate::util::permit_alloc; use crate::util::permit_alloc;
use crate::wrapper::state::{self, PluginState}; use crate::wrapper::state::{self, PluginState};
@ -37,8 +36,9 @@ pub struct Wrapper<P: Plugin, B: Backend> {
/// The wrapped plugin instance. /// The wrapped plugin instance.
plugin: Mutex<P>, plugin: Mutex<P>,
/// The plugin's background task executor. /// The plugin's background task executor closure. Wrapped in another struct so it can be used
pub async_executor: Arc<P::AsyncExecutor>, /// as a [`MainContext`] with [`EventLoop`].
pub task_executor_wrapper: Arc<TaskExecutorWrapper<P>>,
/// The plugin's parameters. These are fetched once during initialization. That way the /// The plugin's parameters. These are fetched once during initialization. That way the
/// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with /// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with
/// the `Params` object without having to acquire a lock on `plugin`. /// the `Params` object without having to acquire a lock on `plugin`.
@ -60,7 +60,7 @@ pub struct Wrapper<P: Plugin, B: Backend> {
/// ///
/// This is only used for executing [`AsyncExecutor`] tasks, so it's parameterized directly over /// This is only used for executing [`AsyncExecutor`] tasks, so it's parameterized directly over
/// that using a special `MainThreadExecutor` wrapper around `AsyncExecutor`. /// that using a special `MainThreadExecutor` wrapper around `AsyncExecutor`.
pub(crate) event_loop: OsEventLoop<<P::AsyncExecutor as AsyncExecutor>::Task, P::AsyncExecutor>, pub(crate) event_loop: OsEventLoop<P::BackgroundTask, TaskExecutorWrapper<P>>,
config: WrapperConfig, config: WrapperConfig,
@ -139,12 +139,25 @@ impl WindowHandler for WrapperWindowHandler {
} }
} }
/// Adapter to make `TaskExecutor<P>` work as a `MainThreadExecutor`.
pub struct TaskExecutorWrapper<P: Plugin> {
pub task_executor: TaskExecutor<P>,
}
impl<P: Plugin> MainThreadExecutor<P::BackgroundTask> for TaskExecutorWrapper<P> {
unsafe fn execute(&self, task: P::BackgroundTask) {
(self.task_executor)(task)
}
}
impl<P: Plugin, B: Backend> Wrapper<P, B> { impl<P: Plugin, B: Backend> Wrapper<P, B> {
/// Instantiate a new instance of the standalone wrapper. Returns an error if the plugin does /// Instantiate a new instance of the standalone wrapper. Returns an error if the plugin does
/// not accept the IO configuration from the wrapper config. /// not accept the IO configuration from the wrapper config.
pub fn new(backend: B, config: WrapperConfig) -> Result<Arc<Self>, WrapperError> { pub fn new(backend: B, config: WrapperConfig) -> Result<Arc<Self>, WrapperError> {
let plugin = P::default(); let plugin = P::default();
let async_executor = Arc::new(plugin.async_executor()); let task_executor_wrapper = Arc::new(TaskExecutorWrapper {
task_executor: plugin.task_executor(),
});
let params = plugin.params(); let params = plugin.params();
let editor = plugin.editor().map(|editor| Arc::new(Mutex::new(editor))); let editor = plugin.editor().map(|editor| Arc::new(Mutex::new(editor)));
@ -191,7 +204,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
backend: AtomicRefCell::new(backend), backend: AtomicRefCell::new(backend),
plugin: Mutex::new(plugin), plugin: Mutex::new(plugin),
async_executor: async_executor.clone(), task_executor_wrapper: task_executor_wrapper.clone(),
params, params,
known_parameters: param_map.iter().map(|(_, ptr, _)| *ptr).collect(), known_parameters: param_map.iter().map(|(_, ptr, _)| *ptr).collect(),
param_map: param_map param_map: param_map
@ -200,7 +213,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
.collect(), .collect(),
editor, editor,
event_loop: OsEventLoop::new_and_spawn(Arc::downgrade(&async_executor)), event_loop: OsEventLoop::new_and_spawn(Arc::downgrade(&task_executor_wrapper)),
bus_config: BusConfig { bus_config: BusConfig {
num_input_channels: config.input_channels.unwrap_or(P::DEFAULT_INPUT_CHANNELS), num_input_channels: config.input_channels.unwrap_or(P::DEFAULT_INPUT_CHANNELS),

View file

@ -5,7 +5,6 @@ use std::sync::Arc;
use vst3_sys::vst::IComponentHandler; use vst3_sys::vst::IComponentHandler;
use super::inner::{Task, WrapperInner}; use super::inner::{Task, WrapperInner};
use crate::async_executor::AsyncExecutor;
use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport}; use crate::context::{GuiContext, InitContext, PluginApi, ProcessContext, Transport};
use crate::midi::NoteEvent; use crate::midi::NoteEvent;
use crate::params::internals::ParamPtr; use crate::params::internals::ParamPtr;
@ -117,8 +116,8 @@ impl<P: Vst3Plugin> GuiContext for WrapperGuiContext<P> {
} }
impl<P: Vst3Plugin> InitContext<P> for WrapperInitContext<'_, P> { impl<P: Vst3Plugin> InitContext<P> for WrapperInitContext<'_, P> {
fn execute(&self, task: <P::AsyncExecutor as crate::prelude::AsyncExecutor>::Task) { fn execute(&self, task: P::BackgroundTask) {
self.inner.async_executor.execute(task); (self.inner.task_executor)(task);
} }
fn plugin_api(&self) -> PluginApi { fn plugin_api(&self) -> PluginApi {
@ -135,7 +134,7 @@ impl<P: Vst3Plugin> InitContext<P> for WrapperInitContext<'_, P> {
} }
impl<P: Vst3Plugin> ProcessContext<P> for WrapperProcessContext<'_, P> { impl<P: Vst3Plugin> ProcessContext<P> for WrapperProcessContext<'_, P> {
fn execute_async(&self, task: <P::AsyncExecutor as AsyncExecutor>::Task) { fn execute_async(&self, task: P::BackgroundTask) {
let task_posted = self.inner.do_maybe_async(Task::PluginTask(task)); let task_posted = self.inner.do_maybe_async(Task::PluginTask(task));
nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
} }

View file

@ -14,7 +14,6 @@ use super::note_expressions::NoteExpressionController;
use super::param_units::ParamUnits; 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::async_executor::AsyncExecutor;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::context::Transport; use crate::context::Transport;
use crate::editor::Editor; use crate::editor::Editor;
@ -22,7 +21,9 @@ use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
use crate::midi::{MidiConfig, NoteEvent}; use crate::midi::{MidiConfig, NoteEvent};
use crate::params::internals::ParamPtr; use crate::params::internals::ParamPtr;
use crate::params::{ParamFlags, Params}; use crate::params::{ParamFlags, Params};
use crate::plugin::{BufferConfig, BusConfig, Plugin, ProcessMode, ProcessStatus, Vst3Plugin}; use crate::plugin::{
BufferConfig, BusConfig, Plugin, ProcessMode, ProcessStatus, TaskExecutor, Vst3Plugin,
};
use crate::wrapper::state::{self, PluginState}; use crate::wrapper::state::{self, PluginState};
use crate::wrapper::util::{hash_param_id, process_wrapper}; use crate::wrapper::util::{hash_param_id, process_wrapper};
@ -32,8 +33,8 @@ use crate::wrapper::util::{hash_param_id, process_wrapper};
pub(crate) struct WrapperInner<P: Vst3Plugin> { pub(crate) struct WrapperInner<P: Vst3Plugin> {
/// The wrapped plugin instance. /// The wrapped plugin instance.
pub plugin: Mutex<P>, pub plugin: Mutex<P>,
/// The plugin's background task executor. /// The plugin's background task executor closure.
pub async_executor: P::AsyncExecutor, pub task_executor: TaskExecutor<P>,
/// The plugin's parameters. These are fetched once during initialization. That way the /// The plugin's parameters. These are fetched once during initialization. That way the
/// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with /// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with
/// the `Params` object without having to acquire a lock on `plugin`. /// the `Params` object without having to acquire a lock on `plugin`.
@ -154,7 +155,7 @@ pub(crate) struct WrapperInner<P: Vst3Plugin> {
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
pub enum Task<P: Plugin> { pub enum Task<P: Plugin> {
/// Execute one of the plugin's background tasks. /// Execute one of the plugin's background tasks.
PluginTask(<P::AsyncExecutor as AsyncExecutor>::Task), PluginTask(P::BackgroundTask),
/// Trigger a restart with the given restart flags. This is a bit set of the flags from /// Trigger a restart with the given restart flags. This is a bit set of the flags from
/// [`vst3_sys::vst::RestartFlags`]. /// [`vst3_sys::vst::RestartFlags`].
TriggerRestart(i32), TriggerRestart(i32),
@ -196,7 +197,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
pub fn new() -> Arc<Self> { pub fn new() -> Arc<Self> {
let plugin = P::default(); let plugin = P::default();
let async_executor = plugin.async_executor(); let task_executor = plugin.task_executor();
let editor = plugin.editor().map(|editor| Arc::new(Mutex::new(editor))); 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
@ -280,7 +281,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
let wrapper = Self { let wrapper = Self {
plugin: Mutex::new(plugin), plugin: Mutex::new(plugin),
async_executor, task_executor,
params, params,
editor, editor,
@ -526,7 +527,7 @@ impl<P: Vst3Plugin> MainThreadExecutor<Task<P>> for WrapperInner<P> {
// function for checking if a to be scheduled task can be handled right there and // function for checking if a to be scheduled task can be handled right there and
// then). // then).
match task { match task {
Task::PluginTask(task) => AsyncExecutor::execute(&self.async_executor, task), Task::PluginTask(task) => (self.task_executor)(task),
Task::TriggerRestart(flags) => match &*self.component_handler.borrow() { Task::TriggerRestart(flags) => match &*self.component_handler.borrow() {
Some(handler) => { Some(handler) => {
handler.restart_component(flags); handler.restart_component(flags);