diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 52fc3ff4..acb7cafd 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -8,6 +8,17 @@ code then it will not be listed here. ## [2022-10-21] +- 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 + `Plugin` instance now needs to define an `AsyncExecutor` type definition and + constructor function as Rust does not yet support defaults for associated + types (Rust issue [#29661](https://github.com/rust-lang/rust/issues/29661)): + + ```rust + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + ``` + - The `Editor` trait and the `ParentWindowHandle` struct have been moved from `nih_plug::plugin` to a new `nih_plug::editor` module. diff --git a/plugins/crisp/src/lib.rs b/plugins/crisp/src/lib.rs index 4a93c6ee..d9e4fbf8 100644 --- a/plugins/crisp/src/lib.rs +++ b/plugins/crisp/src/lib.rs @@ -307,6 +307,9 @@ impl Plugin for Crisp { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/crossover/src/lib.rs b/plugins/crossover/src/lib.rs index c83c16a9..fec11adf 100644 --- a/plugins/crossover/src/lib.rs +++ b/plugins/crossover/src/lib.rs @@ -183,6 +183,9 @@ impl Plugin for Crossover { aux_outputs: Some(&["Band 1", "Band 2", "Band 3", "Band 4", "Band 5"]), }; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/diopser/src/lib.rs b/plugins/diopser/src/lib.rs index 0b768f88..54f8e66a 100644 --- a/plugins/diopser/src/lib.rs +++ b/plugins/diopser/src/lib.rs @@ -252,6 +252,9 @@ impl Plugin for Diopser { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/gain/src/lib.rs b/plugins/examples/gain/src/lib.rs index 32a35569..80b9687b 100644 --- a/plugins/examples/gain/src/lib.rs +++ b/plugins/examples/gain/src/lib.rs @@ -132,6 +132,12 @@ impl Plugin for Gain { // splits. const SAMPLE_ACCURATE_AUTOMATION: bool = true; + // More advanced plugins can use this to run expensive background tasks. See the `AsyncExecutor` + // trait's documentation for more information. `()` means that the plugin does not have any + // background tasks. + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/gain_gui_egui/src/lib.rs b/plugins/examples/gain_gui_egui/src/lib.rs index e97926c4..952941b1 100644 --- a/plugins/examples/gain_gui_egui/src/lib.rs +++ b/plugins/examples/gain_gui_egui/src/lib.rs @@ -83,6 +83,9 @@ impl Plugin for Gain { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/gain_gui_iced/src/lib.rs b/plugins/examples/gain_gui_iced/src/lib.rs index 86dfe97a..4698b61f 100644 --- a/plugins/examples/gain_gui_iced/src/lib.rs +++ b/plugins/examples/gain_gui_iced/src/lib.rs @@ -80,6 +80,9 @@ impl Plugin for Gain { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/gain_gui_vizia/src/lib.rs b/plugins/examples/gain_gui_vizia/src/lib.rs index 8bb2ea13..c15d443b 100644 --- a/plugins/examples/gain_gui_vizia/src/lib.rs +++ b/plugins/examples/gain_gui_vizia/src/lib.rs @@ -79,6 +79,9 @@ impl Plugin for Gain { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/midi_inverter/src/lib.rs b/plugins/examples/midi_inverter/src/lib.rs index 4bc0733a..e00ee8c9 100644 --- a/plugins/examples/midi_inverter/src/lib.rs +++ b/plugins/examples/midi_inverter/src/lib.rs @@ -33,6 +33,9 @@ impl Plugin for MidiInverter { const MIDI_OUTPUT: MidiConfig = MidiConfig::MidiCCs; const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/poly_mod_synth/src/lib.rs b/plugins/examples/poly_mod_synth/src/lib.rs index eb994432..429bbb6f 100644 --- a/plugins/examples/poly_mod_synth/src/lib.rs +++ b/plugins/examples/poly_mod_synth/src/lib.rs @@ -155,6 +155,9 @@ impl Plugin for PolyModSynth { const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/sine/src/lib.rs b/plugins/examples/sine/src/lib.rs index 9a1eacb2..6ff2e573 100644 --- a/plugins/examples/sine/src/lib.rs +++ b/plugins/examples/sine/src/lib.rs @@ -111,6 +111,9 @@ impl Plugin for Sine { const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/examples/stft/src/lib.rs b/plugins/examples/stft/src/lib.rs index bec9aec6..9e5cf7e9 100644 --- a/plugins/examples/stft/src/lib.rs +++ b/plugins/examples/stft/src/lib.rs @@ -95,6 +95,9 @@ impl Plugin for Stft { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/loudness_war_winner/src/lib.rs b/plugins/loudness_war_winner/src/lib.rs index 8cf44617..46da3a52 100644 --- a/plugins/loudness_war_winner/src/lib.rs +++ b/plugins/loudness_war_winner/src/lib.rs @@ -123,6 +123,9 @@ impl Plugin for LoudnessWarWinner { const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/puberty_simulator/src/lib.rs b/plugins/puberty_simulator/src/lib.rs index 043cefaa..94f0c828 100644 --- a/plugins/puberty_simulator/src/lib.rs +++ b/plugins/puberty_simulator/src/lib.rs @@ -168,6 +168,9 @@ impl Plugin for PubertySimulator { const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/safety_limiter/src/lib.rs b/plugins/safety_limiter/src/lib.rs index 3c984c19..5339ed20 100644 --- a/plugins/safety_limiter/src/lib.rs +++ b/plugins/safety_limiter/src/lib.rs @@ -157,6 +157,9 @@ impl Plugin for SafetyLimiter { const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/plugins/spectral_compressor/src/lib.rs b/plugins/spectral_compressor/src/lib.rs index e6a71ff2..e60490d8 100644 --- a/plugins/spectral_compressor/src/lib.rs +++ b/plugins/spectral_compressor/src/lib.rs @@ -272,6 +272,9 @@ impl Plugin for SpectralCompressor { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type AsyncExecutor = (); + fn async_executor(&self) -> Self::AsyncExecutor {} + fn params(&self) -> Arc { self.params.clone() } diff --git a/src/plugin.rs b/src/plugin.rs index b82ab5cd..39a53da8 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -2,6 +2,7 @@ use std::sync::Arc; +use crate::async_executor::AsyncExecutor; use crate::buffer::Buffer; use crate::context::{InitContext, ProcessContext}; use crate::editor::Editor; @@ -89,6 +90,20 @@ pub trait Plugin: Default + Send + 'static { /// to do offline processing. const HARD_REALTIME_ONLY: bool = false; + /// The plugin's [`AsyncExecutor`] type. Use `()` if the plugin does not need to perform + /// expensive background tasks. + // + // This needs to be an associated type so we can have a nice type safe interface in the + // `*Context` traits. + // + // NOTE: Sadly it's not yet possible to default this and the `async_executor()` function to + // `()`: https://github.com/rust-lang/rust/issues/29661 + type AsyncExecutor: AsyncExecutor; + + /// The plugin's background task executor. Use `()` if the plugin does not need this + /// functinlality. + fn async_executor(&self) -> Self::AsyncExecutor; + /// 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 /// plugin receives an update.