diff --git a/plugins/examples/gain-gui-egui/src/lib.rs b/plugins/examples/gain-gui-egui/src/lib.rs index 780b6341..02d2407e 100644 --- a/plugins/examples/gain-gui-egui/src/lib.rs +++ b/plugins/examples/gain-gui-egui/src/lib.rs @@ -70,7 +70,7 @@ impl Plugin for Gain { const DEFAULT_NUM_INPUTS: u32 = 2; const DEFAULT_NUM_OUTPUTS: u32 = 2; - const ACCEPTS_MIDI: bool = false; + const MIDI_INPUT: MidiConfig = MidiConfig::None; const SAMPLE_ACCURATE_AUTOMATION: bool = true; 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 4ae8b473..454afb8d 100644 --- a/plugins/examples/gain-gui-iced/src/lib.rs +++ b/plugins/examples/gain-gui-iced/src/lib.rs @@ -67,7 +67,7 @@ impl Plugin for Gain { const DEFAULT_NUM_INPUTS: u32 = 2; const DEFAULT_NUM_OUTPUTS: u32 = 2; - const ACCEPTS_MIDI: bool = false; + const MIDI_INPUT: MidiConfig = MidiConfig::None; const SAMPLE_ACCURATE_AUTOMATION: bool = true; 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 a3249ed3..670250b4 100644 --- a/plugins/examples/gain-gui-vizia/src/lib.rs +++ b/plugins/examples/gain-gui-vizia/src/lib.rs @@ -67,7 +67,7 @@ impl Plugin for Gain { const DEFAULT_NUM_INPUTS: u32 = 2; const DEFAULT_NUM_OUTPUTS: u32 = 2; - const ACCEPTS_MIDI: bool = false; + const MIDI_INPUT: MidiConfig = MidiConfig::None; const SAMPLE_ACCURATE_AUTOMATION: bool = true; fn params(&self) -> Arc { diff --git a/plugins/examples/gain/src/lib.rs b/plugins/examples/gain/src/lib.rs index 7daedaa3..08a753f0 100644 --- a/plugins/examples/gain/src/lib.rs +++ b/plugins/examples/gain/src/lib.rs @@ -103,7 +103,7 @@ impl Plugin for Gain { const DEFAULT_NUM_INPUTS: u32 = 2; const DEFAULT_NUM_OUTPUTS: u32 = 2; - const ACCEPTS_MIDI: bool = false; + const MIDI_INPUT: MidiConfig = MidiConfig::None; // Setting this to `true` will tell the wrapper to split the buffer up into smaller blocks // whenever there are inter-buffer parameter changes. This way no changes to the plugin are // required to support sample accurate automation and the wrapper handles all of the boring diff --git a/plugins/examples/sine/src/lib.rs b/plugins/examples/sine/src/lib.rs index cd62da8c..1e87bf51 100644 --- a/plugins/examples/sine/src/lib.rs +++ b/plugins/examples/sine/src/lib.rs @@ -103,7 +103,7 @@ impl Plugin for Sine { const DEFAULT_NUM_INPUTS: u32 = 0; const DEFAULT_NUM_OUTPUTS: u32 = 2; - const ACCEPTS_MIDI: bool = true; + const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const SAMPLE_ACCURATE_AUTOMATION: bool = true; fn params(&self) -> Arc { diff --git a/plugins/examples/stft/src/lib.rs b/plugins/examples/stft/src/lib.rs index 8f2e2417..396d6a3b 100644 --- a/plugins/examples/stft/src/lib.rs +++ b/plugins/examples/stft/src/lib.rs @@ -87,7 +87,7 @@ impl Plugin for Stft { const DEFAULT_NUM_INPUTS: u32 = 2; const DEFAULT_NUM_OUTPUTS: u32 = 2; - const ACCEPTS_MIDI: bool = false; + const MIDI_INPUT: MidiConfig = MidiConfig::None; const SAMPLE_ACCURATE_AUTOMATION: bool = true; fn params(&self) -> Arc { diff --git a/src/plugin.rs b/src/plugin.rs index 4f4b5278..a3a25df9 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -41,9 +41,9 @@ pub trait Plugin: Default + Send + Sync + 'static { /// instead of setting up the busses properly. const DEFAULT_NUM_OUTPUTS: u32 = 2; - /// Whether the plugin accepts note events. If this is set to `false`, then the plugin won't - /// receive any note events. - const ACCEPTS_MIDI: bool = false; + /// Whether the plugin accepts note events, and which level of . If this is set to + /// [`MidiConfig::None`], then the plugin won't receive any note events. + const MIDI_INPUT: MidiConfig = MidiConfig::None; /// If enabled, the audio processing cycle may be split up into multiple smaller chunks if /// parameter values change occur in the middle of the buffer. Depending on the host these /// blocks may be as small as a single sample. Bitwig Studio sends at most one parameter change @@ -303,15 +303,30 @@ pub enum ProcessStatus { KeepAlive, } -/// Event for (incoming) notes. Right now this only supports a very small subset of the MIDI -/// specification. See the util module for convenient conversion functions. +/// Determines which note events a plugin receives. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum MidiConfig { + /// The plugin will not have a note input port and will thus not receive any not events. + None, + /// The plugin receives note on/off events, pressure, and potentially a couple standardized + /// expression types depending on the plugin standard and host. + Basic, + // // TODO: + // /// The plugin receives full MIDI CCs as well as pitch bend information. For VST3 plugins this + // /// involves adding + // MidiCCs, +} + +/// Event for (incoming) notes. The set of supported note events depends on the value of +/// [`Plugin::MIDI_INPUT`]. Also check out the [`util`][crate::util] module for convenient +/// conversion functions. /// /// All of the timings are sample offsets withing the current buffer. /// /// TODO: Add more events as needed -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum NoteEvent { - /// A note on event. + /// A note on event, available on [`MidiConfig::Basic`] and up. NoteOn { timing: u32, /// The note's channel, from 0 to 16. @@ -322,7 +337,7 @@ pub enum NoteEvent { /// 127 levels available in MIDI. velocity: f32, }, - /// A note off event. + /// A note off event, available on [`MidiConfig::Basic`] and up. NoteOff { timing: u32, /// The note's channel, from 0 to 16. diff --git a/src/prelude.rs b/src/prelude.rs index f7fef544..bc01fa3d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -19,7 +19,7 @@ pub use crate::param::range::{FloatRange, IntRange}; pub use crate::param::smoothing::{Smoother, SmoothingStyle}; pub use crate::param::{BoolParam, FloatParam, IntParam, Param, ParamFlags}; pub use crate::plugin::{ - BufferConfig, BusConfig, ClapPlugin, Editor, NoteEvent, ParentWindowHandle, Plugin, + BufferConfig, BusConfig, ClapPlugin, Editor, MidiConfig, NoteEvent, ParentWindowHandle, Plugin, ProcessStatus, Vst3Plugin, }; pub use crate::wrapper::state::PluginState; diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 2457cb54..f75292da 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -72,7 +72,8 @@ use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY}; use crate::param::internals::{ParamPtr, Params}; use crate::param::ParamFlags; use crate::plugin::{ - BufferConfig, BusConfig, ClapPlugin, Editor, NoteEvent, ParentWindowHandle, ProcessStatus, + BufferConfig, BusConfig, ClapPlugin, Editor, MidiConfig, NoteEvent, ParentWindowHandle, + ProcessStatus, }; use crate::util::permit_alloc; use crate::wrapper::state::{self, PluginState}; @@ -116,7 +117,8 @@ pub struct Wrapper { /// The current buffer configuration, containing the sample rate and the maximum block size. /// Will be set in `clap_plugin::activate()`. current_buffer_config: AtomicCell>, - /// The incoming events for the plugin, if `P::ACCEPTS_MIDI` is set. + /// The incoming events for the plugin, if `P::MIDI_INPUT` is set to `MidiConfig::Basic` or + /// higher. /// /// TODO: Maybe load these lazily at some point instead of needing to spool them all to this /// queue first @@ -867,7 +869,7 @@ impl Wrapper

{ // true // } (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_ON) => { - if P::ACCEPTS_MIDI { + if P::MIDI_INPUT >= MidiConfig::Basic { let event = &*(event as *const clap_event_note); input_events.push_back(NoteEvent::NoteOn { // When splitting up the buffer for sample accurate automation all events @@ -882,7 +884,7 @@ impl Wrapper

{ false } (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_OFF) => { - if P::ACCEPTS_MIDI { + if P::MIDI_INPUT >= MidiConfig::Basic { let event = &*(event as *const clap_event_note); input_events.push_back(NoteEvent::NoteOff { timing: raw_event.time - current_sample_idx as u32, @@ -895,7 +897,7 @@ impl Wrapper

{ false } (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_EXPRESSION) => { - if P::ACCEPTS_MIDI { + if P::MIDI_INPUT >= MidiConfig::Basic { // We currently don't report supporting this at all in the event filter, add that once // we support MIDI CCs // TODO: Implement pressure and other expressions along with MIDI CCs @@ -904,7 +906,7 @@ impl Wrapper

{ false } (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_MIDI) => { - if P::ACCEPTS_MIDI { + if P::MIDI_INPUT >= MidiConfig::Basic { // We currently don't report supporting this at all in the event filter, add that once // we support MIDI CCs // TODO: Implement raw MIDI handling once we add CCs @@ -1392,7 +1394,7 @@ impl Wrapper

{ &wrapper.clap_plugin_gui as *const _ as *const c_void } else if id == CStr::from_ptr(CLAP_EXT_LATENCY) { &wrapper.clap_plugin_latency as *const _ as *const c_void - } else if id == CStr::from_ptr(CLAP_EXT_NOTE_PORTS) && P::ACCEPTS_MIDI { + } else if id == CStr::from_ptr(CLAP_EXT_NOTE_PORTS) && P::MIDI_INPUT >= MidiConfig::Basic { &wrapper.clap_plugin_note_ports as *const _ as *const c_void } else if id == CStr::from_ptr(CLAP_EXT_PARAMS) { &wrapper.clap_plugin_params as *const _ as *const c_void @@ -1618,7 +1620,7 @@ impl Wrapper

{ // TODO: Implement midi CC handling // | (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_EXPRESSION) // | (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_MIDI) - if P::ACCEPTS_MIDI => + if P::MIDI_INPUT >= MidiConfig::Basic => { true } @@ -1870,7 +1872,7 @@ impl Wrapper

{ unsafe extern "C" fn ext_note_ports_count(_plugin: *const clap_plugin, is_input: bool) -> u32 { // TODO: Outputting notes match is_input { - true if P::ACCEPTS_MIDI => 1, + true if P::MIDI_INPUT >= MidiConfig::Basic => 1, _ => 0, } } @@ -1882,7 +1884,7 @@ impl Wrapper

{ info: *mut clap_note_port_info, ) -> bool { match (index, is_input) { - (0, true) if P::ACCEPTS_MIDI => { + (0, true) if P::MIDI_INPUT >= MidiConfig::Basic => { *info = std::mem::zeroed(); let info = &mut *info; diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index adbe6cd9..7af8b6de 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -19,7 +19,7 @@ use super::util::VstPtr; use super::view::WrapperView; use crate::context::Transport; use crate::param::ParamFlags; -use crate::plugin::{BufferConfig, BusConfig, NoteEvent, ProcessStatus, Vst3Plugin}; +use crate::plugin::{BufferConfig, BusConfig, MidiConfig, NoteEvent, ProcessStatus, Vst3Plugin}; use crate::util::permit_alloc; use crate::wrapper::state; use crate::wrapper::util::{process_wrapper, u16strlcpy}; @@ -71,7 +71,7 @@ impl IComponent for Wrapper

{ x if x == vst3_sys::vst::MediaTypes::kAudio as i32 => 1, x if x == vst3_sys::vst::MediaTypes::kEvent as i32 && dir == vst3_sys::vst::BusDirections::kInput as i32 - && P::ACCEPTS_MIDI => + && P::MIDI_INPUT >= MidiConfig::Basic => { 1 } @@ -119,7 +119,7 @@ impl IComponent for Wrapper

{ (t, d, 0) if t == vst3_sys::vst::MediaTypes::kEvent as i32 && d == vst3_sys::vst::BusDirections::kInput as i32 - && P::ACCEPTS_MIDI => + && P::MIDI_INPUT >= MidiConfig::Basic => { *info = mem::zeroed(); @@ -172,7 +172,7 @@ impl IComponent for Wrapper

{ (t, d, 0) if t == vst3_sys::vst::MediaTypes::kEvent as i32 && d == vst3_sys::vst::BusDirections::kInput as i32 - && P::ACCEPTS_MIDI => + && P::MIDI_INPUT >= MidiConfig::Basic => { kResultOk } @@ -743,7 +743,7 @@ impl IAudioProcessor for Wrapper

{ parameter_values_changed = false; } - if P::ACCEPTS_MIDI { + if P::MIDI_INPUT >= MidiConfig::Basic { let mut input_events = self.inner.input_events.borrow_mut(); if let Some(events) = data.input_events.upgrade() { let num_events = events.get_event_count();