From a79099f5737d3140f942604be15be7f9936a9676 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 23 Apr 2022 15:00:17 +0200 Subject: [PATCH] Work around Bitwig VST3 note expressions bug Bitwig will only send these note expression events if the plugin claims to support at least one custom note expression type, and if it returns `kResultOk` on `INoteExpressionController::get_note_expression_info()` when the expression ID is equal to the note expression type (even though these are out of range, since this index argument is supposed to be a linear index from `0..count`). --- src/wrapper/vst3/note_expressions.rs | 17 +++++++ src/wrapper/vst3/wrapper.rs | 73 ++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/wrapper/vst3/note_expressions.rs b/src/wrapper/vst3/note_expressions.rs index 79294adc..c2607120 100644 --- a/src/wrapper/vst3/note_expressions.rs +++ b/src/wrapper/vst3/note_expressions.rs @@ -40,6 +40,23 @@ pub struct NoteExpressionController { } impl NoteExpressionController { + /// Returns `true` if the VST3 expression type ID is one of the predefiend types we support. + /// This is only needed as a workaround for a Bitwig bug where it only sends us note expressions + /// if we explicitly return `kResultOk` on + /// `INoteExpressionController::get_note_expression_info()` with malformed expression index + /// arguments. + pub const fn known_expression_type_id(type_id: u32) -> bool { + match type_id { + VOLUME_EXPRESSION_ID + | PAN_EXPRESSION_ID + | TUNING_EXPRESSION_ID + | VIBRATO_EXPRESSION_ID + | EXPRESSION_EXPRESSION_ID + | BRIGHTNESS_EXPRESSION_ID => true, + _ => false, + } + } + /// Register the note ID from a note on event so it can later be retrieved when handling a note /// expression value event. pub fn register_note(&mut self, event: &NoteOnEvent) { diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index 69b30a11..83cf0bd5 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -9,8 +9,9 @@ use vst3_sys::base::{IBStream, IPluginBase}; use vst3_sys::utils::SharedVstPtr; use vst3_sys::vst::{ kNoProgramListId, kRootUnitId, Event, EventTypes, IAudioProcessor, IComponent, IEditController, - IEventList, IMidiMapping, IParamValueQueue, IParameterChanges, IUnitInfo, LegacyMidiCCOutEvent, - NoteOffEvent, NoteOnEvent, ParameterFlags, PolyPressureEvent, ProgramListInfo, TChar, UnitInfo, + IEventList, IMidiMapping, INoteExpressionController, IParamValueQueue, IParameterChanges, + IUnitInfo, LegacyMidiCCOutEvent, NoteExpressionTypeInfo, NoteOffEvent, NoteOnEvent, + ParameterFlags, PolyPressureEvent, ProgramListInfo, TChar, UnitInfo, }; use vst3_sys::VST3; use widestring::U16CStr; @@ -34,7 +35,14 @@ use crate::wrapper::vst3::util::{VST3_MIDI_CHANNELS, VST3_MIDI_PARAMS_END}; // Alias needed for the VST3 attribute macro use vst3_sys as vst3_com; -#[VST3(implements(IComponent, IEditController, IAudioProcessor, IMidiMapping, IUnitInfo))] +#[VST3(implements( + IComponent, + IEditController, + IAudioProcessor, + IMidiMapping, + IUnitInfo, + INoteExpressionController +))] pub(crate) struct Wrapper { inner: Arc>, } @@ -1399,3 +1407,62 @@ impl IUnitInfo for Wrapper

{ kInvalidArgument } } + +impl INoteExpressionController for Wrapper

{ + unsafe fn get_note_expression_count(&self, bus_idx: i32, _channel: i16) -> i32 { + // NOTE: We don't have any custom note expressions. But Bitwig won't send us the predefined + // note expressions unless we pretend we do. + if P::MIDI_INPUT >= MidiConfig::Basic && bus_idx == 0 { + 1 + } else { + 0 + } + } + + unsafe fn get_note_expression_info( + &self, + bus_idx: i32, + _channel: i16, + note_expression_idx: i32, + info: *mut NoteExpressionTypeInfo, + ) -> tresult { + if P::MIDI_INPUT < MidiConfig::Basic || bus_idx != 0 { + return kInvalidArgument; + } + + check_null_ptr!(info); + + // NOTE: As mentioned above, this is only a workaround for a Bitwig bug. We don't have any + // custom note expressions, and the IDs passed to this function are the type IDs which + // is incorrect. We won't even bother filling in the information. This argument is of + // course also incorrect, as these expression indices are supposed to be linear + // indices starting at 0, while Bitwig queries this with 0, 1, 2, and 5. + if NoteExpressionController::known_expression_type_id(note_expression_idx as u32) { + kResultOk + } else { + kResultFalse + } + } + + unsafe fn get_note_expression_string_by_value( + &self, + _bus_idx: i32, + _channel: i16, + _id: u32, + _value: f64, + _string: *mut TChar, + ) -> tresult { + kResultFalse + } + + unsafe fn get_note_expression_value_by_string( + &self, + _bus_idx: i32, + _channel: i16, + _id: u32, + _string: *const TChar, + _value: *mut f64, + ) -> tresult { + kResultFalse + } +}