From b7849f9a7abfbb9ac3ec67ed95505b29fc9e0a0a Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 31 Jan 2023 18:29:43 +0100 Subject: [PATCH] Add a NoteEvent::MidiSysEx variant Right now this isn't used, and there is not yet any way to convert to and from raw SysEx data. --- BREAKING_CHANGES.md | 4 +++ src/context/process.rs | 6 ++-- src/midi.rs | 45 ++++++++++++++++++++----- src/prelude.rs | 2 +- src/wrapper/clap/context.rs | 10 +++--- src/wrapper/clap/wrapper.rs | 8 ++--- src/wrapper/standalone.rs | 2 +- src/wrapper/standalone/backend.rs | 12 +++++-- src/wrapper/standalone/backend/cpal.rs | 26 +++++++++----- src/wrapper/standalone/backend/dummy.rs | 11 ++++-- src/wrapper/standalone/backend/jack.rs | 15 ++++++--- src/wrapper/standalone/context.rs | 24 ++++++------- src/wrapper/standalone/wrapper.rs | 12 +++---- src/wrapper/vst3/context.rs | 10 +++--- src/wrapper/vst3/inner.rs | 12 +++---- src/wrapper/vst3/note_expressions.rs | 7 ++-- src/wrapper/vst3/wrapper.rs | 20 +++++------ 17 files changed, 141 insertions(+), 85 deletions(-) diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index b2325629..b9c96a1e 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -18,6 +18,10 @@ code then it will not be listed here. type SysExMessage = (); ``` +- As the result of the above change, `NoteEvent` is now parameterized by a + `SysExMessage` type. There is a new `PluginNoteEvent

` type synonym that can + be parameterized by a `Plugin` to make using this slightly less verbose. + ## [2023-01-12] - The Vizia dependency has been updated. This updated version uses a new text diff --git a/src/context/process.rs b/src/context/process.rs index 7898fdce..0a6ca372 100644 --- a/src/context/process.rs +++ b/src/context/process.rs @@ -1,7 +1,7 @@ //! A context passed during the process function. use super::PluginApi; -use crate::midi::NoteEvent; +use crate::midi::PluginNoteEvent; use crate::plugin::Plugin; /// Contains both context data and callbacks the plugin can use during processing. Most notably this @@ -73,12 +73,12 @@ pub trait ProcessContext { /// /// ProcessStatus::Normal /// ``` - fn next_event(&mut self) -> Option; + fn next_event(&mut self) -> Option>; /// Send an event to the host. Only available when /// [`Plugin::MIDI_OUTPUT`][crate::prelude::Plugin::MIDI_INPUT] is set. Will not do anything /// otherwise. - fn send_event(&mut self, event: NoteEvent); + fn send_event(&mut self, event: PluginNoteEvent

); /// Update the current latency of the plugin. If the plugin is currently processing audio, then /// this may cause audio playback to be restarted. diff --git a/src/midi.rs b/src/midi.rs index 5de76de2..c8a3f83b 100644 --- a/src/midi.rs +++ b/src/midi.rs @@ -2,10 +2,22 @@ use midi_consts::channel_event as midi; +use self::sysex::SysExMessage; +use crate::plugin::Plugin; + pub mod sysex; pub use midi_consts::channel_event::control_change; +/// A plugin-specific note event type. +/// +/// The reason why this is defined like this instead of parameterizing `NoteEvent` with `P`` is +/// because deriving trait bounds requires all of the plugin's generic parameters to implement those +/// traits. And we can't require `P` to implement things like `Clone`. +/// +/// +pub type PluginNoteEvent

= NoteEvent<

::SysExMessage>; + /// Determines which note events a plugin receives. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum MidiConfig { @@ -31,7 +43,7 @@ pub enum MidiConfig { /// numbers are zero-indexed. #[derive(Debug, Clone, Copy, PartialEq)] #[non_exhaustive] -pub enum NoteEvent { +pub enum NoteEvent { /// A note on event, available on [`MidiConfig::Basic`] and up. NoteOn { timing: u32, @@ -299,9 +311,13 @@ pub enum NoteEvent { /// The program number, in `0..128`. program: u8, }, + /// A MIDI SysEx message supported by the plugin's `SysExMessage` type. If the conversion from + /// the raw byte array fails (e.g. the plugin doesn't support this kind of message), then this + /// will be logged during debug builds of the plugin, and no event is emitted. + MidiSysEx { timing: u32, message: S }, } -impl NoteEvent { +impl NoteEvent { /// Returns the sample within the current buffer this event belongs to. pub fn timing(&self) -> u32 { match self { @@ -322,6 +338,7 @@ impl NoteEvent { NoteEvent::MidiPitchBend { timing, .. } => *timing, NoteEvent::MidiCC { timing, .. } => *timing, NoteEvent::MidiProgramChange { timing, .. } => *timing, + NoteEvent::MidiSysEx { timing, .. } => *timing, } } @@ -345,9 +362,13 @@ impl NoteEvent { NoteEvent::MidiPitchBend { .. } => None, NoteEvent::MidiCC { .. } => None, NoteEvent::MidiProgramChange { .. } => None, + NoteEvent::MidiSysEx { .. } => None, } } + // TODO: `[u8; 3]` doesn't work anymore with SysEx. We can wrap this in an + // `enum MidiBuffer

{ simple: [u8; 3], sysex: P::SysExMessage::Buffer }`. + /// Parse MIDI into a [`NoteEvent`]. Will return `Err(event_type)` if the parsing failed. pub fn from_midi(timing: u32, midi_data: [u8; 3]) -> Result { // TODO: Maybe add special handling for 14-bit CCs and RPN messages at some @@ -408,6 +429,7 @@ impl NoteEvent { channel, program: midi_data[1], }), + // TODO: SysEx n => Err(n), } } @@ -500,6 +522,8 @@ impl NoteEvent { | NoteEvent::PolyVibrato { .. } | NoteEvent::PolyExpression { .. } | NoteEvent::PolyBrightness { .. } => None, + // TODO: These functions need to handle both simple and longer messages as documented above + NoteEvent::MidiSysEx { .. } => None, } } @@ -524,6 +548,7 @@ impl NoteEvent { NoteEvent::MidiPitchBend { timing, .. } => *timing -= samples, NoteEvent::MidiCC { timing, .. } => *timing -= samples, NoteEvent::MidiProgramChange { timing, .. } => *timing -= samples, + NoteEvent::MidiSysEx { timing, .. } => *timing -= samples, } } } @@ -536,7 +561,7 @@ mod tests { #[test] fn test_note_on_midi_conversion() { - let event = NoteEvent::NoteOn { + let event = NoteEvent::<()>::NoteOn { timing: TIMING, voice_id: None, channel: 1, @@ -553,7 +578,7 @@ mod tests { #[test] fn test_note_off_midi_conversion() { - let event = NoteEvent::NoteOff { + let event = NoteEvent::<()>::NoteOff { timing: TIMING, voice_id: None, channel: 1, @@ -569,7 +594,7 @@ mod tests { #[test] fn test_poly_pressure_midi_conversion() { - let event = NoteEvent::PolyPressure { + let event = NoteEvent::<()>::PolyPressure { timing: TIMING, voice_id: None, channel: 1, @@ -585,7 +610,7 @@ mod tests { #[test] fn test_channel_pressure_midi_conversion() { - let event = NoteEvent::MidiChannelPressure { + let event = NoteEvent::<()>::MidiChannelPressure { timing: TIMING, channel: 1, pressure: 0.6929134, @@ -599,7 +624,7 @@ mod tests { #[test] fn test_pitch_bend_midi_conversion() { - let event = NoteEvent::MidiPitchBend { + let event = NoteEvent::<()>::MidiPitchBend { timing: TIMING, channel: 1, value: 0.6929134, @@ -613,7 +638,7 @@ mod tests { #[test] fn test_cc_midi_conversion() { - let event = NoteEvent::MidiCC { + let event = NoteEvent::<()>::MidiCC { timing: TIMING, channel: 1, cc: 2, @@ -628,7 +653,7 @@ mod tests { #[test] fn test_program_change_midi_conversion() { - let event = NoteEvent::MidiProgramChange { + let event = NoteEvent::<()>::MidiProgramChange { timing: TIMING, channel: 1, program: 42, @@ -639,4 +664,6 @@ mod tests { event ); } + + // TODO: SysEx conversion } diff --git a/src/prelude.rs b/src/prelude.rs index 485b6623..9884ab3e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -17,7 +17,7 @@ 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::midi::{control_change, MidiConfig, NoteEvent, PluginNoteEvent}; pub use crate::params::enums::{Enum, EnumParam}; pub use crate::params::internals::ParamPtr; pub use crate::params::range::{FloatRange, IntRange}; diff --git a/src/wrapper/clap/context.rs b/src/wrapper/clap/context.rs index 893111bc..4adae5bb 100644 --- a/src/wrapper/clap/context.rs +++ b/src/wrapper/clap/context.rs @@ -9,7 +9,7 @@ use crate::context::init::InitContext; use crate::context::process::{ProcessContext, Transport}; use crate::context::PluginApi; use crate::event_loop::EventLoop; -use crate::midi::NoteEvent; +use crate::midi::PluginNoteEvent; use crate::params::internals::ParamPtr; use crate::plugin::ClapPlugin; @@ -37,8 +37,8 @@ pub(crate) struct PendingInitContextRequests { /// unnecessary atomic operations to lock the uncontested RwLocks. pub(crate) struct WrapperProcessContext<'a, P: ClapPlugin> { pub(super) wrapper: &'a Wrapper

, - pub(super) input_events_guard: AtomicRefMut<'a, VecDeque>, - pub(super) output_events_guard: AtomicRefMut<'a, VecDeque>, + pub(super) input_events_guard: AtomicRefMut<'a, VecDeque>>, + pub(super) output_events_guard: AtomicRefMut<'a, VecDeque>>, pub(super) transport: Transport, } @@ -96,11 +96,11 @@ impl ProcessContext

for WrapperProcessContext<'_, P> { &self.transport } - fn next_event(&mut self) -> Option { + fn next_event(&mut self) -> Option> { self.input_events_guard.pop_front() } - fn send_event(&mut self, event: NoteEvent) { + fn send_event(&mut self, event: PluginNoteEvent

) { self.output_events_guard.push_back(event); } diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index b47c8fc6..302a62e6 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -81,7 +81,7 @@ use crate::context::gui::AsyncExecutor; use crate::context::process::Transport; use crate::editor::{Editor, ParentWindowHandle}; use crate::event_loop::{BackgroundThread, EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY}; -use crate::midi::{MidiConfig, NoteEvent}; +use crate::midi::{MidiConfig, NoteEvent, PluginNoteEvent}; use crate::params::internals::ParamPtr; use crate::params::{ParamFlags, Params}; use crate::plugin::{ @@ -136,10 +136,10 @@ pub struct Wrapper { /// /// TODO: Maybe load these lazily at some point instead of needing to spool them all to this /// queue first - input_events: AtomicRefCell>, + input_events: AtomicRefCell>>, /// Stores any events the plugin has output during the current processing cycle, analogous to /// `input_events`. - output_events: AtomicRefCell>, + output_events: AtomicRefCell>>, /// The last process status returned by the plugin. This is used for tail handling. last_process_status: AtomicCell, /// The current latency in samples, as set by the plugin through the [`ProcessContext`]. uses @@ -1356,7 +1356,7 @@ impl Wrapper

{ pub unsafe fn handle_in_event( &self, event: *const clap_event_header, - input_events: &mut AtomicRefMut>, + input_events: &mut AtomicRefMut>>, transport_info: Option<&mut *const clap_event_transport>, current_sample_idx: usize, ) { diff --git a/src/wrapper/standalone.rs b/src/wrapper/standalone.rs index 58ad35e7..c26c3560 100644 --- a/src/wrapper/standalone.rs +++ b/src/wrapper/standalone.rs @@ -173,7 +173,7 @@ pub fn nih_export_standalone_with_args(backend: B, config: WrapperConfig) -> bool { +fn run_wrapper>(backend: B, config: WrapperConfig) -> bool { let wrapper = match Wrapper::::new(backend, config) { Ok(wrapper) => wrapper, Err(err) => { diff --git a/src/wrapper/standalone/backend.rs b/src/wrapper/standalone/backend.rs index c56d7fc6..1ef8ae7e 100644 --- a/src/wrapper/standalone/backend.rs +++ b/src/wrapper/standalone/backend.rs @@ -1,5 +1,5 @@ use crate::context::process::Transport; -use crate::midi::NoteEvent; +use crate::midi::PluginNoteEvent; mod cpal; mod dummy; @@ -9,9 +9,10 @@ pub use self::cpal::Cpal; pub use self::dummy::Dummy; pub use self::jack::Jack; pub use crate::buffer::Buffer; +pub use crate::plugin::Plugin; /// An audio+MIDI backend for the standalone wrapper. -pub trait Backend: 'static + Send + Sync { +pub trait Backend: 'static + Send + Sync { /// Start processing audio and MIDI on this thread. The process callback will be called whenever /// there's a new block of audio to be processed. The process callback receives the audio /// buffers for the wrapped plugin's outputs. Any inputs will have already been copied to this @@ -20,7 +21,12 @@ pub trait Backend: 'static + Send + Sync { /// TODO: Auxiliary inputs and outputs fn run( &mut self, - cb: impl FnMut(&mut Buffer, Transport, &[NoteEvent], &mut Vec) -> bool + cb: impl FnMut( + &mut Buffer, + Transport, + &[PluginNoteEvent

], + &mut Vec>, + ) -> bool + 'static + Send, ); diff --git a/src/wrapper/standalone/backend/cpal.rs b/src/wrapper/standalone/backend/cpal.rs index 7bb6cd4c..c527aa49 100644 --- a/src/wrapper/standalone/backend/cpal.rs +++ b/src/wrapper/standalone/backend/cpal.rs @@ -10,7 +10,7 @@ use super::super::config::WrapperConfig; use super::Backend; use crate::buffer::Buffer; use crate::context::process::Transport; -use crate::midi::{MidiConfig, NoteEvent}; +use crate::midi::{MidiConfig, PluginNoteEvent}; use crate::plugin::{AuxiliaryIOConfig, BusConfig, Plugin}; /// Uses CPAL for audio and midir for MIDI. @@ -26,10 +26,15 @@ pub struct Cpal { // TODO: MIDI } -impl Backend for Cpal { +impl Backend

for Cpal { fn run( &mut self, - cb: impl FnMut(&mut Buffer, Transport, &[NoteEvent], &mut Vec) -> bool + cb: impl FnMut( + &mut Buffer, + Transport, + &[PluginNoteEvent

], + &mut Vec>, + ) -> bool + 'static + Send, ) { @@ -100,17 +105,17 @@ impl Backend for Cpal { let output_stream = match self.output_sample_format { SampleFormat::I16 => self.output_device.build_output_stream( &self.output_config, - self.build_output_data_callback::(unparker, input_rb_consumer, cb), + self.build_output_data_callback::(unparker, input_rb_consumer, cb), error_cb, ), SampleFormat::U16 => self.output_device.build_output_stream( &self.output_config, - self.build_output_data_callback::(unparker, input_rb_consumer, cb), + self.build_output_data_callback::(unparker, input_rb_consumer, cb), error_cb, ), SampleFormat::F32 => self.output_device.build_output_stream( &self.output_config, - self.build_output_data_callback::(unparker, input_rb_consumer, cb), + self.build_output_data_callback::(unparker, input_rb_consumer, cb), error_cb, ), } @@ -306,11 +311,16 @@ impl Cpal { } } - fn build_output_data_callback( + fn build_output_data_callback( &self, unparker: Unparker, mut input_rb_consumer: Option>, - mut cb: impl FnMut(&mut Buffer, Transport, &[NoteEvent], &mut Vec) -> bool + mut cb: impl FnMut( + &mut Buffer, + Transport, + &[PluginNoteEvent

], + &mut Vec>, + ) -> bool + 'static + Send, ) -> impl FnMut(&mut [T], &OutputCallbackInfo) + Send + 'static { diff --git a/src/wrapper/standalone/backend/dummy.rs b/src/wrapper/standalone/backend/dummy.rs index acad8b6c..70b228bd 100644 --- a/src/wrapper/standalone/backend/dummy.rs +++ b/src/wrapper/standalone/backend/dummy.rs @@ -4,7 +4,7 @@ use super::super::config::WrapperConfig; use super::Backend; use crate::buffer::Buffer; use crate::context::process::Transport; -use crate::midi::NoteEvent; +use crate::midi::PluginNoteEvent; use crate::plugin::{AuxiliaryIOConfig, BusConfig, Plugin}; /// This backend doesn't input or output any audio or MIDI. It only exists so the standalone @@ -15,10 +15,15 @@ pub struct Dummy { bus_config: BusConfig, } -impl Backend for Dummy { +impl Backend

for Dummy { fn run( &mut self, - mut cb: impl FnMut(&mut Buffer, Transport, &[NoteEvent], &mut Vec) -> bool + mut cb: impl FnMut( + &mut Buffer, + Transport, + &[PluginNoteEvent

], + &mut Vec>, + ) -> bool + 'static + Send, ) { diff --git a/src/wrapper/standalone/backend/jack.rs b/src/wrapper/standalone/backend/jack.rs index aa5ef11e..93c692ee 100644 --- a/src/wrapper/standalone/backend/jack.rs +++ b/src/wrapper/standalone/backend/jack.rs @@ -12,7 +12,7 @@ use super::super::config::WrapperConfig; use super::Backend; use crate::buffer::Buffer; use crate::context::process::Transport; -use crate::midi::{MidiConfig, NoteEvent}; +use crate::midi::{MidiConfig, NoteEvent, PluginNoteEvent}; use crate::plugin::Plugin; /// Uses JACK audio and MIDI. @@ -28,10 +28,15 @@ pub struct Jack { midi_output: Option>>>, } -impl Backend for Jack { +impl Backend

for Jack { fn run( &mut self, - mut cb: impl FnMut(&mut Buffer, Transport, &[NoteEvent], &mut Vec) -> bool + mut cb: impl FnMut( + &mut Buffer, + Transport, + &[PluginNoteEvent

], + &mut Vec>, + ) -> bool + 'static + Send, ) { @@ -45,8 +50,8 @@ impl Backend for Jack { }) } - let mut input_events = Vec::with_capacity(2048); - let mut output_events = Vec::with_capacity(2048); + let mut input_events: Vec> = Vec::with_capacity(2048); + let mut output_events: Vec> = Vec::with_capacity(2048); // This thread needs to be blocked until processing is finished let parker = Parker::new(); diff --git a/src/wrapper/standalone/context.rs b/src/wrapper/standalone/context.rs index 8aa3c7e8..5c6001dc 100644 --- a/src/wrapper/standalone/context.rs +++ b/src/wrapper/standalone/context.rs @@ -7,33 +7,33 @@ use crate::context::gui::GuiContext; use crate::context::init::InitContext; use crate::context::process::{ProcessContext, Transport}; use crate::context::PluginApi; -use crate::midi::NoteEvent; +use crate::midi::PluginNoteEvent; use crate::params::internals::ParamPtr; use crate::plugin::Plugin; /// An [`InitContext`] implementation for the standalone wrapper. -pub(crate) struct WrapperInitContext<'a, P: Plugin, B: Backend> { +pub(crate) struct WrapperInitContext<'a, P: Plugin, B: Backend

> { pub(super) wrapper: &'a Wrapper, } /// A [`ProcessContext`] implementation for the standalone wrapper. This is a separate object so it /// can hold on to lock guards for event queues. Otherwise reading these events would require /// constant unnecessary atomic operations to lock the uncontested RwLocks. -pub(crate) struct WrapperProcessContext<'a, P: Plugin, B: Backend> { +pub(crate) struct WrapperProcessContext<'a, P: Plugin, B: Backend

> { #[allow(dead_code)] pub(super) wrapper: &'a Wrapper, - pub(super) input_events: &'a [NoteEvent], + pub(super) input_events: &'a [PluginNoteEvent

], // The current index in `input_events`, since we're not actually popping anything from a queue // here to keep the standalone backend implementation a bit more flexible pub(super) input_events_idx: usize, - pub(super) output_events: &'a mut Vec, + pub(super) output_events: &'a mut Vec>, pub(super) transport: Transport, } /// A [`GuiContext`] implementation for the wrapper. This is passed to the plugin in /// [`Editor::spawn()`][crate::prelude::Editor::spawn()] so it can interact with the rest of the plugin and /// with the host for things like setting parameters. -pub(crate) struct WrapperGuiContext { +pub(crate) struct WrapperGuiContext> { pub(super) wrapper: Arc>, /// This allows us to send tasks to the parent view that will be handled at the start of its @@ -41,7 +41,7 @@ pub(crate) struct WrapperGuiContext { pub(super) gui_task_sender: channel::Sender, } -impl InitContext

for WrapperInitContext<'_, P, B> { +impl> InitContext

for WrapperInitContext<'_, P, B> { fn plugin_api(&self) -> PluginApi { PluginApi::Standalone } @@ -59,7 +59,7 @@ impl InitContext

for WrapperInitContext<'_, P, B> { } } -impl ProcessContext

for WrapperProcessContext<'_, P, B> { +impl> ProcessContext

for WrapperProcessContext<'_, P, B> { fn plugin_api(&self) -> PluginApi { PluginApi::Standalone } @@ -79,10 +79,10 @@ impl ProcessContext

for WrapperProcessContext<'_, P, B &self.transport } - fn next_event(&mut self) -> Option { + fn next_event(&mut self) -> Option> { // We'll pretend we're a queue, choo choo if self.input_events_idx < self.input_events.len() { - let event = self.input_events[self.input_events_idx]; + let event = self.input_events[self.input_events_idx].clone(); self.input_events_idx += 1; Some(event) @@ -91,7 +91,7 @@ impl ProcessContext

for WrapperProcessContext<'_, P, B } } - fn send_event(&mut self, event: NoteEvent) { + fn send_event(&mut self, event: PluginNoteEvent

) { self.output_events.push(event); } @@ -104,7 +104,7 @@ impl ProcessContext

for WrapperProcessContext<'_, P, B } } -impl GuiContext for WrapperGuiContext { +impl> GuiContext for WrapperGuiContext { fn plugin_api(&self) -> PluginApi { PluginApi::Standalone } diff --git a/src/wrapper/standalone/wrapper.rs b/src/wrapper/standalone/wrapper.rs index b2780545..78f92baa 100644 --- a/src/wrapper/standalone/wrapper.rs +++ b/src/wrapper/standalone/wrapper.rs @@ -17,7 +17,7 @@ use crate::context::gui::AsyncExecutor; use crate::context::process::Transport; use crate::editor::{Editor, ParentWindowHandle}; use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop}; -use crate::midi::NoteEvent; +use crate::midi::PluginNoteEvent; use crate::params::internals::ParamPtr; use crate::params::{ParamFlags, Params}; use crate::plugin::{ @@ -32,7 +32,7 @@ use crate::wrapper::util::process_wrapper; /// than this many parameters at a time will cause changes to get lost. const EVENT_QUEUE_CAPACITY: usize = 2048; -pub struct Wrapper { +pub struct Wrapper> { backend: AtomicRefCell, /// The wrapped plugin instance. @@ -154,7 +154,7 @@ impl WindowHandler for WrapperWindowHandler { } } -impl MainThreadExecutor> for Wrapper { +impl> MainThreadExecutor> for Wrapper { fn execute(&self, task: Task

, _is_gui_thread: bool) { match task { Task::PluginTask(task) => (self.task_executor.lock())(task), @@ -175,7 +175,7 @@ impl MainThreadExecutor> for Wrapper { } } -impl Wrapper { +impl> Wrapper { /// Instantiate a new instance of the standalone wrapper. Returns an error if the plugin does /// not accept the IO configuration from the wrapper config. pub fn new(backend: B, config: WrapperConfig) -> Result, WrapperError> { @@ -602,8 +602,8 @@ impl Wrapper { fn make_process_context<'a>( &'a self, transport: Transport, - input_events: &'a [NoteEvent], - output_events: &'a mut Vec, + input_events: &'a [PluginNoteEvent

], + output_events: &'a mut Vec>, ) -> WrapperProcessContext<'a, P, B> { WrapperProcessContext { wrapper: self, diff --git a/src/wrapper/vst3/context.rs b/src/wrapper/vst3/context.rs index 488508f8..c01f2cbe 100644 --- a/src/wrapper/vst3/context.rs +++ b/src/wrapper/vst3/context.rs @@ -10,7 +10,7 @@ use crate::context::gui::GuiContext; use crate::context::init::InitContext; use crate::context::process::{ProcessContext, Transport}; use crate::context::PluginApi; -use crate::midi::NoteEvent; +use crate::midi::PluginNoteEvent; use crate::params::internals::ParamPtr; use crate::plugin::Vst3Plugin; use crate::wrapper::state::PluginState; @@ -42,8 +42,8 @@ pub(crate) struct PendingInitContextRequests { /// unnecessary atomic operations to lock the uncontested locks. pub(crate) struct WrapperProcessContext<'a, P: Vst3Plugin> { pub(super) inner: &'a WrapperInner

, - pub(super) input_events_guard: AtomicRefMut<'a, VecDeque>, - pub(super) output_events_guard: AtomicRefMut<'a, VecDeque>, + pub(super) input_events_guard: AtomicRefMut<'a, VecDeque>>, + pub(super) output_events_guard: AtomicRefMut<'a, VecDeque>>, pub(super) transport: Transport, } @@ -101,11 +101,11 @@ impl ProcessContext

for WrapperProcessContext<'_, P> { &self.transport } - fn next_event(&mut self) -> Option { + fn next_event(&mut self) -> Option> { self.input_events_guard.pop_front() } - fn send_event(&mut self, event: NoteEvent) { + fn send_event(&mut self, event: PluginNoteEvent

) { self.output_events_guard.push_back(event); } diff --git a/src/wrapper/vst3/inner.rs b/src/wrapper/vst3/inner.rs index 92426b7d..73ea8e33 100644 --- a/src/wrapper/vst3/inner.rs +++ b/src/wrapper/vst3/inner.rs @@ -19,7 +19,7 @@ use crate::context::gui::AsyncExecutor; use crate::context::process::Transport; use crate::editor::Editor; use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop}; -use crate::midi::{MidiConfig, NoteEvent}; +use crate::midi::{MidiConfig, PluginNoteEvent}; use crate::params::internals::ParamPtr; use crate::params::{ParamFlags, Params}; use crate::plugin::{ @@ -102,10 +102,10 @@ pub(crate) struct WrapperInner { /// NOTE: Because with VST3 MIDI CC messages are sent as parameter changes and VST3 does not /// interleave parameter changes and note events, this queue has to be sorted when /// creating the process context - pub input_events: AtomicRefCell>, + pub input_events: AtomicRefCell>>, /// Stores any events the plugin has output during the current processing cycle, analogous to /// `input_events`. - pub output_events: AtomicRefCell>, + pub output_events: AtomicRefCell>>, /// VST3 has several useful predefined note expressions, but for some reason they are the only /// note event type that don't have MIDI note ID and channel fields. So we need to keep track of /// the most recent VST3 note IDs we've seen, and then map those back to MIDI note IDs and @@ -120,7 +120,7 @@ pub(crate) struct WrapperInner { /// parameter changes and events into a vector at the start of the process call, sort it, and /// then do the block splitting based on that. Note events need to have their timing adjusted to /// match the block start, since they're all read upfront. - pub process_events: AtomicRefCell>, + pub process_events: AtomicRefCell>>, /// The plugin is able to restore state through a method on the `GuiContext`. To avoid changing /// parameters mid-processing and running into garbled data if the host also tries to load state /// at the same time the restoring happens at the end of each processing call. If this zero @@ -177,7 +177,7 @@ pub enum Task { /// sample accurate automation and MIDI CC handling through parameters we need to put all parameter /// changes and (translated) note events into a sorted array first. #[derive(Debug, PartialEq)] -pub enum ProcessEvent { +pub enum ProcessEvent { /// An incoming parameter change sent by the host. This will only be used when sample accurate /// automation has been enabled, and the parameters are only updated when we process this /// spooled event at the start of a block. @@ -198,7 +198,7 @@ pub enum ProcessEvent { timing: u32, /// The actual note event, make sure to subtract the block start index with /// [`NoteEvent::subtract_timing()`] before putting this into the input event queue. - event: NoteEvent, + event: PluginNoteEvent

, }, } diff --git a/src/wrapper/vst3/note_expressions.rs b/src/wrapper/vst3/note_expressions.rs index e4deda1f..d5e1c4e1 100644 --- a/src/wrapper/vst3/note_expressions.rs +++ b/src/wrapper/vst3/note_expressions.rs @@ -3,6 +3,7 @@ use vst3_sys::vst::{NoteExpressionValueEvent, NoteOnEvent}; +use crate::midi::sysex::SysExMessage; use crate::midi::NoteEvent; type MidiNote = u8; @@ -100,11 +101,11 @@ impl NoteExpressionController { /// Translate the note expression value event into an internal NIH-plug event, if we handle the /// expression type from the note expression value event. The timing is provided here because we /// may be splitting buffers on inter-buffer parameter changes. - pub fn translate_event( + pub fn translate_event( &self, timing: u32, event: &NoteExpressionValueEvent, - ) -> Option { + ) -> Option> { // We're calling it a voice ID, VST3 (and CLAP) calls it a note ID let (note_id, note, channel) = *self .note_ids @@ -168,7 +169,7 @@ impl NoteExpressionController { /// `translate_event()`. pub fn translate_event_reverse( note_id: i32, - event: &NoteEvent, + event: &NoteEvent, ) -> Option { match &event { NoteEvent::PolyVolume { gain, .. } => Some(NoteExpressionValueEvent { diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index 25567b6d..fe99d459 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -1271,7 +1271,7 @@ impl IAudioProcessor for Wrapper

{ block_end = data.num_samples as usize; for event_idx in event_start_idx..process_events.len() { - match process_events[event_idx] { + match &process_events[event_idx] { ProcessEvent::ParameterChange { timing, hash, @@ -1280,24 +1280,22 @@ impl IAudioProcessor for Wrapper

{ // If this parameter change happens after the start of this block, then // we'll split the block here and handle this parameter change after // we've processed this block - if timing != block_start as u32 { + if *timing != block_start as u32 { event_start_idx = event_idx; - block_end = timing as usize; + block_end = *timing as usize; break; } self.inner.set_normalized_value_by_hash( - hash, - normalized_value, + *hash, + *normalized_value, Some(sample_rate), ); } - ProcessEvent::NoteEvent { - timing: _, - mut event, - } => { + ProcessEvent::NoteEvent { timing: _, event } => { // We need to make sure to compensate the event for any block splitting, // since we had to create the event object beforehand + let mut event = event.clone(); event.subtract_timing(block_start as u32); input_events.push_back(event); } @@ -1626,7 +1624,7 @@ impl IAudioProcessor for Wrapper

{ pressure, }; } - event @ (NoteEvent::PolyVolume { + ref event @ (NoteEvent::PolyVolume { voice_id, channel, note, @@ -1665,7 +1663,7 @@ impl IAudioProcessor for Wrapper

{ match NoteExpressionController::translate_event_reverse( voice_id .unwrap_or_else(|| ((channel as i32) << 8) | note as i32), - &event, + event, ) { Some(translated_event) => { vst3_event.type_ =