diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 68389762..b2325629 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -6,6 +6,18 @@ new and what's changed, this document lists all breaking changes in reverse chronological order. If a new feature did not require any changes to existing code then it will not be listed here. +## [2023-01-31] + +- NIH-plug has gained support MIDI SysEx in a simple, type-safe, and + realtime-safe way. This sadly does mean that every `Plugin` instance now needs + to define a `SysExMessage` 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 SysExMessage = (); + ``` + ## [2023-01-12] - The Vizia dependency has been updated. This updated version uses a new text diff --git a/README.md b/README.md index 956867bd..2134f0ef 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,9 @@ Scroll down for more information on the underlying plugin framework. - Full support for receiving and outputting both modern polyphonic note expression events as well as MIDI CCs, channel pressure, and pitch bend for CLAP and VST3. + - MIDI SysEx is also supported. Plugins can define their own structs or sum + types to wrap around those messages so they don't need to interact with raw + byte buffers in the process function. - Support for flexible dynamic buffer configurations, including multiple input and output busses. - A plugin bundler accessible through the diff --git a/plugins/buffr_glitch/src/lib.rs b/plugins/buffr_glitch/src/lib.rs index 6c4fdb99..62c8589c 100644 --- a/plugins/buffr_glitch/src/lib.rs +++ b/plugins/buffr_glitch/src/lib.rs @@ -186,6 +186,7 @@ impl Plugin for BuffrGlitch { const MIDI_INPUT: MidiConfig = MidiConfig::Basic; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/crisp/src/lib.rs b/plugins/crisp/src/lib.rs index 9cefb2d7..e37f0ad8 100644 --- a/plugins/crisp/src/lib.rs +++ b/plugins/crisp/src/lib.rs @@ -306,6 +306,7 @@ impl Plugin for Crisp { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/crossover/src/lib.rs b/plugins/crossover/src/lib.rs index 7d206ce0..6cacb21a 100644 --- a/plugins/crossover/src/lib.rs +++ b/plugins/crossover/src/lib.rs @@ -183,6 +183,7 @@ impl Plugin for Crossover { aux_outputs: Some(&["Band 1", "Band 2", "Band 3", "Band 4", "Band 5"]), }; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/diopser/src/lib.rs b/plugins/diopser/src/lib.rs index c792c2a6..1e6e7a3c 100644 --- a/plugins/diopser/src/lib.rs +++ b/plugins/diopser/src/lib.rs @@ -118,6 +118,7 @@ impl Plugin for Diopser { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/examples/gain/src/lib.rs b/plugins/examples/gain/src/lib.rs index eb36297c..09a3b569 100644 --- a/plugins/examples/gain/src/lib.rs +++ b/plugins/examples/gain/src/lib.rs @@ -134,6 +134,10 @@ impl Plugin for Gain { // splits. const SAMPLE_ACCURATE_AUTOMATION: bool = true; + // If the plugin can send or receive SysEx messages, it can define a type to wrap around those + // messages here. The type implements the `SysExMessage` trait, which allows conversion to and + // from plain byte buffers. + type SysExMessage = (); // More advanced plugins can use this to run expensive background tasks. See the field's // documentation for more information. `()` means that the plugin does not have any background // tasks. diff --git a/plugins/examples/gain_gui_egui/src/lib.rs b/plugins/examples/gain_gui_egui/src/lib.rs index 570b7655..abe4fb17 100644 --- a/plugins/examples/gain_gui_egui/src/lib.rs +++ b/plugins/examples/gain_gui_egui/src/lib.rs @@ -83,6 +83,7 @@ impl Plugin for Gain { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/examples/gain_gui_iced/src/lib.rs b/plugins/examples/gain_gui_iced/src/lib.rs index 07bd5910..2653e3b8 100644 --- a/plugins/examples/gain_gui_iced/src/lib.rs +++ b/plugins/examples/gain_gui_iced/src/lib.rs @@ -80,6 +80,7 @@ impl Plugin for Gain { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/examples/gain_gui_vizia/src/lib.rs b/plugins/examples/gain_gui_vizia/src/lib.rs index 76e763b0..a7ad83f9 100644 --- a/plugins/examples/gain_gui_vizia/src/lib.rs +++ b/plugins/examples/gain_gui_vizia/src/lib.rs @@ -79,6 +79,7 @@ impl Plugin for Gain { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/examples/midi_inverter/src/lib.rs b/plugins/examples/midi_inverter/src/lib.rs index 9e83c288..d87bd0e0 100644 --- a/plugins/examples/midi_inverter/src/lib.rs +++ b/plugins/examples/midi_inverter/src/lib.rs @@ -33,6 +33,7 @@ impl Plugin for MidiInverter { const MIDI_OUTPUT: MidiConfig = MidiConfig::MidiCCs; const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/examples/poly_mod_synth/src/lib.rs b/plugins/examples/poly_mod_synth/src/lib.rs index 02b366ac..94c6c0c1 100644 --- a/plugins/examples/poly_mod_synth/src/lib.rs +++ b/plugins/examples/poly_mod_synth/src/lib.rs @@ -155,6 +155,7 @@ impl Plugin for PolyModSynth { const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/examples/sine/src/lib.rs b/plugins/examples/sine/src/lib.rs index e6c6a4a4..4936c2fd 100644 --- a/plugins/examples/sine/src/lib.rs +++ b/plugins/examples/sine/src/lib.rs @@ -111,6 +111,7 @@ impl Plugin for Sine { const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/examples/stft/src/lib.rs b/plugins/examples/stft/src/lib.rs index 87185eab..066e2805 100644 --- a/plugins/examples/stft/src/lib.rs +++ b/plugins/examples/stft/src/lib.rs @@ -95,6 +95,7 @@ impl Plugin for Stft { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/loudness_war_winner/src/lib.rs b/plugins/loudness_war_winner/src/lib.rs index d9fc38cd..ee710fcd 100644 --- a/plugins/loudness_war_winner/src/lib.rs +++ b/plugins/loudness_war_winner/src/lib.rs @@ -123,6 +123,7 @@ impl Plugin for LoudnessWarWinner { const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/puberty_simulator/src/lib.rs b/plugins/puberty_simulator/src/lib.rs index 1d07b344..72c10cb2 100644 --- a/plugins/puberty_simulator/src/lib.rs +++ b/plugins/puberty_simulator/src/lib.rs @@ -168,6 +168,7 @@ impl Plugin for PubertySimulator { const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/safety_limiter/src/lib.rs b/plugins/safety_limiter/src/lib.rs index 088078d9..876598b8 100644 --- a/plugins/safety_limiter/src/lib.rs +++ b/plugins/safety_limiter/src/lib.rs @@ -158,6 +158,7 @@ impl Plugin for SafetyLimiter { const DEFAULT_INPUT_CHANNELS: u32 = 2; const DEFAULT_OUTPUT_CHANNELS: u32 = 2; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/plugins/spectral_compressor/src/lib.rs b/plugins/spectral_compressor/src/lib.rs index ba5c085d..a73e2c1a 100644 --- a/plugins/spectral_compressor/src/lib.rs +++ b/plugins/spectral_compressor/src/lib.rs @@ -265,6 +265,7 @@ impl Plugin for SpectralCompressor { const SAMPLE_ACCURATE_AUTOMATION: bool = true; + type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { diff --git a/src/plugin.rs b/src/plugin.rs index 0ba281b1..7ae04a6b 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -3,12 +3,13 @@ use std::sync::Arc; use crate::buffer::Buffer; +use crate::context::gui::AsyncExecutor; use crate::context::init::InitContext; use crate::context::process::ProcessContext; use crate::editor::Editor; +use crate::midi::sysex::SysExMessage; use crate::midi::MidiConfig; use crate::params::Params; -use crate::prelude::AsyncExecutor; use crate::wrapper::clap::features::ClapFeature; use crate::wrapper::state::PluginState; @@ -26,7 +27,7 @@ pub type TaskExecutor

= Box::BackgroundTask) + Send>; /// /// Some notable not yet implemented features include: /// -/// - MIDI SysEx and MIDI2 for CLAP, note expressions, polyphonic modulation and MIDI1 are already +/// - MIDI2 for CLAP, note expressions, polyphonic modulation and MIDI1, and MIDI SysEx are already /// supported /// - Audio thread thread pools (with host integration in CLAP) #[allow(unused_variables)] @@ -96,6 +97,12 @@ pub trait Plugin: Default + Send + 'static { /// to do offline processing. const HARD_REALTIME_ONLY: bool = false; + /// The plugin's SysEx message type if it supports sending or receiving MIDI SysEx messages, or + /// `()` if it does not. This type can be a struct or enum wrapping around one or more message + /// types, and the [`SysExMessage`] trait is then used to convert between this type and basic + /// byte buffers. + type SysExMessage: SysExMessage; + /// A type encoding the different background tasks this plugin wants to run, or `()` if it /// doesn't have any background tasks. This is usually set to 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 diff --git a/src/prelude.rs b/src/prelude.rs index ca4f7ccc..485b6623 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -16,6 +16,7 @@ pub use crate::context::init::InitContext; pub use crate::context::process::ProcessContext; // This also includes the derive macro pub use crate::editor::{Editor, ParentWindowHandle}; +pub use crate::midi::sysex::SysExMessage; pub use crate::midi::{control_change, MidiConfig, NoteEvent}; pub use crate::params::enums::{Enum, EnumParam}; pub use crate::params::internals::ParamPtr;