From 701590f1cd28d48cfe7653ef2f28d9efe670893f Mon Sep 17 00:00:00 2001 From: Robbert van der Helm <mail@robbertvanderhelm.nl> Date: Tue, 31 Jan 2023 21:35:52 +0100 Subject: [PATCH] Support SysEx for CLAP plugins --- src/wrapper/clap/wrapper.rs | 87 ++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 302a62e6..14834760 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -1,15 +1,16 @@ use atomic_float::AtomicF32; use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use clap_sys::events::{ - clap_event_header, clap_event_midi, clap_event_note, clap_event_note_expression, - clap_event_param_gesture, clap_event_param_mod, clap_event_param_value, clap_event_transport, - clap_input_events, clap_output_events, CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_IS_LIVE, - CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_CHOKE, CLAP_EVENT_NOTE_END, CLAP_EVENT_NOTE_EXPRESSION, - CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_GESTURE_BEGIN, - CLAP_EVENT_PARAM_GESTURE_END, CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE, - CLAP_EVENT_TRANSPORT, CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION, - CLAP_NOTE_EXPRESSION_PAN, CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING, - CLAP_NOTE_EXPRESSION_VIBRATO, CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE, + clap_event_header, clap_event_midi, clap_event_midi_sysex, clap_event_note, + clap_event_note_expression, clap_event_param_gesture, clap_event_param_mod, + clap_event_param_value, clap_event_transport, clap_input_events, clap_output_events, + CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_IS_LIVE, CLAP_EVENT_MIDI, CLAP_EVENT_MIDI_SYSEX, + CLAP_EVENT_NOTE_CHOKE, CLAP_EVENT_NOTE_END, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF, + CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END, + CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_TRANSPORT, + CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION, CLAP_NOTE_EXPRESSION_PAN, + CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING, CLAP_NOTE_EXPRESSION_VIBRATO, + CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE, CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, CLAP_TRANSPORT_HAS_TEMPO, CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE, CLAP_TRANSPORT_IS_PLAYING, CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL, @@ -62,6 +63,7 @@ use crossbeam::queue::ArrayQueue; use parking_lot::Mutex; use raw_window_handle::RawWindowHandle; use std::any::Any; +use std::borrow::Borrow; use std::cmp; use std::collections::{HashMap, HashSet, VecDeque}; use std::ffi::{c_void, CStr}; @@ -81,7 +83,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, PluginNoteEvent}; +use crate::midi::{MidiConfig, MidiResult, NoteEvent, PluginNoteEvent}; use crate::params::internals::ParamPtr; use crate::params::{ParamFlags, Params}; use crate::plugin::{ @@ -1314,9 +1316,13 @@ impl<P: ClapPlugin> Wrapper<P> { { // NIH-plug already includes MIDI conversion functions, so we'll reuse those for // the MIDI events - let midi = midi_event - .as_midi() - .expect("Missing MIDI conversion for MIDI event"); + let midi_data = match midi_event.as_midi(&mut Default::default()) { + Some(MidiResult::Basic(midi_data)) => midi_data, + Some(MidiResult::SysEx(_)) => unreachable!( + "Basic MIDI event read as SysEx, something's gone horribly wrong" + ), + None => unreachable!("Missing MIDI conversion for MIDI event"), + }; let event = clap_event_midi { header: clap_event_header { @@ -1327,7 +1333,42 @@ impl<P: ClapPlugin> Wrapper<P> { flags: 0, }, port_index: 0, - data: midi, + data: midi_data, + }; + + clap_call! { out=>try_push(out, &event.header) } + } + midi_event @ NoteEvent::MidiSysEx { .. } if P::MIDI_OUTPUT >= MidiConfig::Basic => { + // SysEx is supported on the basic MIDI config so this is separate + let mut sysex_buffer = Default::default(); + let midi_data: &[u8] = match midi_event.as_midi(&mut sysex_buffer) { + Some(MidiResult::Basic(_)) => unreachable!( + "SysEx event read as basic MIDI, something's gone horribly wrong" + ), + Some(MidiResult::SysEx(length)) => { + // If this is a SysEx event then the `SysExMessage` implementation will + // have serialized the event to `midi_data` and returned the correct + // size in bytes + let sysex_buffer = sysex_buffer.borrow(); + nih_debug_assert!(sysex_buffer.len() >= length); + + &sysex_buffer[..length] + } + None => unreachable!("Missing MIDI conversion for MIDI event"), + }; + + let event = clap_event_midi_sysex { + header: clap_event_header { + size: mem::size_of::<clap_event_midi_sysex>() as u32, + time, + space_id: CLAP_CORE_EVENT_SPACE_ID, + type_: CLAP_EVENT_MIDI_SYSEX, + flags: 0, + }, + port_index: 0, + // The host _should_ be making a copy of the data if it accepts the event. Should... + buffer: midi_data.as_ptr(), + size: midi_data.len() as u32, }; clap_call! { out=>try_push(out, &event.header) } @@ -1590,7 +1631,8 @@ impl<P: ClapPlugin> Wrapper<P> { // messages to stay consistent with the VST3 wrapper. let event = &*(event as *const clap_event_midi); - match NoteEvent::from_midi(raw_event.time - current_sample_idx as u32, event.data) { + match NoteEvent::from_midi(raw_event.time - current_sample_idx as u32, &event.data) + { Ok( note_event @ (NoteEvent::NoteOn { .. } | NoteEvent::NoteOff { .. } @@ -1605,6 +1647,21 @@ impl<P: ClapPlugin> Wrapper<P> { Err(n) => nih_debug_assert_failure!("Unhandled MIDI message type {}", n), }; } + (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_MIDI_SYSEX) + if P::MIDI_INPUT >= MidiConfig::Basic => + { + let event = &*(event as *const clap_event_midi_sysex); + + assert!(!event.buffer.is_null()); + let sysex_buffer = std::slice::from_raw_parts(event.buffer, event.size as usize); + match NoteEvent::from_midi(raw_event.time - current_sample_idx as u32, sysex_buffer) + { + Ok(note_event) => { + input_events.push_back(note_event); + } + Err(n) => nih_debug_assert_failure!("Unhandled MIDI message type {}", n), + }; + } _ => { nih_trace!( "Unhandled CLAP event type {} for namespace {}",