DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit de29a1ad9723db9bc1c9932ab60e5b4e553c72aa
parent 8d0f5600c79e36e95d794e9edecc5deea821188f
Author: falkTX <falktx@falktx.com>
Date:   Fri,  1 Oct 2021 18:50:31 +0100

VST3: Add MIDI CC, channel pressure and pitchbend support

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdistrho/src/DistrhoPluginVST3.cpp | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mdistrho/src/travesty/audio_processor.h | 32++++++++++++++++----------------
Mdistrho/src/travesty/edit_controller.h | 62+++++++++++++++++++++++++++++++++++++++++++++-----------------
3 files changed, 230 insertions(+), 52 deletions(-)

diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp @@ -48,8 +48,8 @@ * - hide parameter outputs? * - hide program parameter? * - deal with parameter triggers - * - midi cc parameter mapping - * - full MIDI1 encode and decode + * - MIDI program changes + * - append MIDI input events in a sorted way * - decode version number (0x010203 -> 1.2.3) * - bus arrangements * - optional audio buses, create dummy buffer of max_block_size length for them @@ -130,7 +130,6 @@ const char* tuid2str(const v3_tuid iid) { V3_ID(0xF040B4B3,0xA36045EC,0xABCDC045,0xB4D5A2CC), "{v3_component_handler2|NOT}" }, { V3_ID(0x7F4EFE59,0xF3204967,0xAC27A3AE,0xAFB63038), "{v3_edit_controller2|NOT}" }, { V3_ID(0x067D02C1,0x5B4E274D,0xA92D90FD,0x6EAF7240), "{v3_component_handler_bus_activation|NOT}" }, - { V3_ID(0xDF0FF9F7,0x49B74669,0xB63AB732,0x7ADBF5E5), "{v3_midi_mapping|NOT}" }, { V3_ID(0xC1271208,0x70594098,0xB9DD34B3,0x6BB0195E), "{v3_edit_controller_host_editing|NOT}" }, // units { V3_ID(0x8683B01F,0x7B354F70,0xA2651DEC,0x353AF4FF), "{v3_program_list_data|NOT}" }, @@ -164,6 +163,8 @@ const char* tuid2str(const v3_tuid iid) return "{v3_funknown}"; if (v3_tuid_match(iid, v3_message_iid)) return "{v3_message_iid}"; + if (v3_tuid_match(iid, v3_midi_mapping_iid)) + return "{v3_midi_mapping_iid}"; if (v3_tuid_match(iid, v3_param_value_queue_iid)) return "{v3_param_value_queue}"; if (v3_tuid_match(iid, v3_param_changes_iid)) @@ -1189,12 +1190,12 @@ public: case V3_EVENT_NOTE_OFF: // case V3_EVENT_DATA: case V3_EVENT_POLY_PRESSURE: + break; // case V3_EVENT_NOTE_EXP_VALUE: // case V3_EVENT_NOTE_EXP_TEXT: // case V3_EVENT_CHORD: // case V3_EVENT_SCALE: - case V3_EVENT_LEGACY_MIDI_CC_OUT: - break; + // case V3_EVENT_LEGACY_MIDI_CC_OUT: default: continue; } @@ -1226,14 +1227,6 @@ public: midiEvent.data[2] = std::max(0, std::min(127, (int)(event.poly_pressure.pressure * 127))); midiEvent.data[3] = 0; break; - case V3_EVENT_LEGACY_MIDI_CC_OUT: - midiEvent.size = 3; - midiEvent.data[0] = 0xB0 | (event.midi_cc_out.channel & 0xf); - midiEvent.data[1] = event.midi_cc_out.cc_number; - midiEvent.data[2] = event.midi_cc_out.value; - midiEvent.data[3] = 0; - // midiEvent.data[3] = event.midi_cc_out.value2; // TODO check when size should be 4 - break; default: midiEvent.size = 0; break; @@ -1244,6 +1237,75 @@ public: } } + // TODO append MIDI events in a sorted way + if (v3_param_changes** const inparamsptr = data->input_params) + { + v3_param_value_queue** queue; + int32_t offset; + double value; + + for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i) + { + queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i); + DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr); + + v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue); + DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fRealParameterCount, rindex); + + // not supported yet + if (rindex >= fParameterOffset) + continue; + +# if DISTRHO_PLUGIN_WANT_PROGRAMS + if (rindex == 0) + continue; + --rindex; +# endif + + for (int32_t j = 0, pcount = v3_cpp_obj(queue)->get_point_count(queue); j < pcount; ++j) + { + if (v3_cpp_obj(queue)->get_point(queue, j, &offset, &value) != V3_OK) + break; + + DISTRHO_SAFE_ASSERT_BREAK(offset < data->nframes); + + MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); + midiEvent.frame = offset; + midiEvent.size = 3; + midiEvent.data[0] = (rindex / 130) & 0xf; + + switch (rindex) + { + case 128: // channel pressure + midiEvent.data[0] |= 0xD0; + midiEvent.data[1] = std::max(0, std::min(127, (int)(value * 127))); + midiEvent.data[2] = 0; + midiEvent.data[3] = 0; + break; + case 129: // pitchbend + midiEvent.data[0] |= 0xE0; + midiEvent.data[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f; + midiEvent.data[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7; + midiEvent.data[3] = 0; + break; + default: + midiEvent.data[0] |= 0xB0; + midiEvent.data[1] = rindex % 130; + midiEvent.data[2] = std::max(0, std::min(127, (int)(value * 127))); + midiEvent.data[3] = 0; + break; + } + + if (midiEventCount == kMaxMidiEvents) + break; + } + + if (midiEventCount == kMaxMidiEvents) + break; + } + } + + fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount); #else fPlugin.run(inputs, outputs, data->nframes); @@ -2018,7 +2080,25 @@ private: event.midi_cc_out.channel = data[0] & 0xf; event.midi_cc_out.cc_number = data[1]; event.midi_cc_out.value = data[2]; - event.midi_cc_out.value2 = midiEvent.size == 4 ? data[3] : 0; + if (midiEvent.size == 4) + event.midi_cc_out.value2 = midiEvent.size == 4; + break; + /* TODO how do we deal with program changes?? + case 0xC0: + break; + */ + case 0xD0: + event.type = V3_EVENT_LEGACY_MIDI_CC_OUT; + event.midi_cc_out.channel = data[0] & 0xf; + event.midi_cc_out.cc_number = 128; + event.midi_cc_out.value = data[1]; + break; + case 0xE0: + event.type = V3_EVENT_LEGACY_MIDI_CC_OUT; + event.midi_cc_out.channel = data[0] & 0xf; + event.midi_cc_out.cc_number = 129; + event.midi_cc_out.value = data[1]; + event.midi_cc_out.value2 = data[2]; break; default: return true; @@ -2594,19 +2674,79 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp { }; #endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT // -------------------------------------------------------------------------------------------------------------------- -// dpf_edit_controller +// dpf_midi_mapping -struct v3_edit_controller_cpp : v3_funknown { - v3_plugin_base base; - v3_edit_controller controller; +struct dpf_midi_mapping : v3_midi_mapping_cpp { + ScopedPointer<PluginVst3>& vst3; + + dpf_midi_mapping(ScopedPointer<PluginVst3>& v) + : vst3(v) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_midi_mapping_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_midi_mapping::query_interface => %p %s %p", self, tuid2str(iid), iface); + *iface = NULL; + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + // there is only a single instance of this, so we don't have to care here + ref = []V3_API(void*) -> uint32_t { return 1; }; + unref = []V3_API(void*) -> uint32_t { return 0; }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_midi_mapping + + map.get_midi_controller_assignment = []V3_API(void*, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id) -> v3_result + { + DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE); + DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE); + DISTRHO_SAFE_ASSERT_INT_RETURN(cc >= 0 && cc < 130, cc, V3_FALSE); + +#if DISTRHO_PLUGIN_WANT_PROGRAMS + static constexpr const v3_param_id offset = 1; +#else + static const constexpr v3_param_id offset = 0; +#endif + + *id = offset + channel * 130 + cc; + return V3_OK; + }; + } }; +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_edit_controller struct dpf_edit_controller : v3_edit_controller_cpp { #if DISTRHO_PLUGIN_HAS_UI ScopedPointer<dpf_dsp_connection_point> connectionComp; // kConnectionPointController ScopedPointer<dpf_dsp_connection_point> connectionBridge; // kConnectionPointBridge #endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + ScopedPointer<dpf_midi_mapping> midiMapping; +#endif ScopedPointer<PluginVst3>& vst3; bool initialized; // cached values @@ -2640,10 +2780,10 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } } -#if DISTRHO_PLUGIN_HAS_UI dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_NO_INTERFACE); +#if DISTRHO_PLUGIN_HAS_UI if (v3_tuid_match(v3_connection_point_iid, iid)) { if (controller->connectionComp == nullptr) @@ -2654,6 +2794,16 @@ struct dpf_edit_controller : v3_edit_controller_cpp { } #endif +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + if (v3_tuid_match(v3_midi_mapping_iid, iid)) + { + if (controller->midiMapping == nullptr) + controller->midiMapping = new dpf_midi_mapping(controller->vst3); + *iface = &controller->midiMapping; + return V3_OK; + } +#endif + return V3_NO_INTERFACE; }; diff --git a/distrho/src/travesty/audio_processor.h b/distrho/src/travesty/audio_processor.h @@ -92,10 +92,10 @@ struct v3_process_setup { struct v3_param_value_queue { struct v3_funknown; - v3_param_id (V3_API *get_param_id)(void* self); - int32_t (V3_API *get_point_count)(void* self); - v3_result (V3_API *get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); - v3_result (V3_API *add_point)(void* self, int32_t sample_offset, double value, int32_t* idx); + v3_param_id (V3_API* get_param_id)(void* self); + int32_t (V3_API* get_point_count)(void* self); + v3_result (V3_API* get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); + v3_result (V3_API* add_point)(void* self, int32_t sample_offset, double value, int32_t* idx); }; static constexpr const v3_tuid v3_param_value_queue_iid = @@ -104,9 +104,9 @@ static constexpr const v3_tuid v3_param_value_queue_iid = struct v3_param_changes { struct v3_funknown; - int32_t (V3_API *get_param_count)(void* self); - struct v3_param_value_queue** (V3_API *get_param_data)(void* self, int32_t idx); - struct v3_param_value_queue** (V3_API *add_param_data)(void* self, v3_param_id* id, int32_t* index); + int32_t (V3_API* get_param_count)(void* self); + struct v3_param_value_queue** (V3_API* get_param_data)(void* self, int32_t idx); + struct v3_param_value_queue** (V3_API* add_param_data)(void* self, v3_param_id* id, int32_t* index); }; static constexpr const v3_tuid v3_param_changes_iid = @@ -183,7 +183,7 @@ enum { struct v3_process_context_requirements { struct v3_funknown; - uint32_t (V3_API *get_process_context_requirements)(void* self); + uint32_t (V3_API* get_process_context_requirements)(void* self); }; static constexpr const v3_tuid v3_process_context_requirements_iid = @@ -224,15 +224,15 @@ struct v3_process_data { struct v3_audio_processor { struct v3_funknown; - v3_result (V3_API *set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, + v3_result (V3_API* set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs); - v3_result (V3_API *get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); - v3_result (V3_API *can_process_sample_size)(void* self, int32_t symbolic_sample_size); - uint32_t (V3_API *get_latency_samples)(void* self); - v3_result (V3_API *setup_processing)(void* self, struct v3_process_setup* setup); - v3_result (V3_API *set_processing)(void* self, v3_bool state); - v3_result (V3_API *process)(void* self, struct v3_process_data* data); - uint32_t (V3_API *get_tail_samples)(void* self); + v3_result (V3_API* get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); + v3_result (V3_API* can_process_sample_size)(void* self, int32_t symbolic_sample_size); + uint32_t (V3_API* get_latency_samples)(void* self); + v3_result (V3_API* setup_processing)(void* self, struct v3_process_setup* setup); + v3_result (V3_API* set_processing)(void* self, v3_bool state); + v3_result (V3_API* process)(void* self, struct v3_process_data* data); + uint32_t (V3_API* get_tail_samples)(void* self); }; static constexpr const v3_tuid v3_audio_processor_iid = diff --git a/distrho/src/travesty/edit_controller.h b/distrho/src/travesty/edit_controller.h @@ -42,10 +42,10 @@ enum { struct v3_component_handler { struct v3_funknown; - v3_result (V3_API *begin_edit)(void* self, v3_param_id); - v3_result (V3_API *perform_edit)(void* self, v3_param_id, double value_normalised); - v3_result (V3_API *end_edit)(void* self, v3_param_id); - v3_result (V3_API *restart_component)(void* self, int32_t flags); + v3_result (V3_API* begin_edit)(void* self, v3_param_id); + v3_result (V3_API* perform_edit)(void* self, v3_param_id, double value_normalised); + v3_result (V3_API* end_edit)(void* self, v3_param_id); + v3_result (V3_API* restart_component)(void* self, int32_t flags); }; static constexpr const v3_tuid v3_component_handler_iid = @@ -79,25 +79,52 @@ struct v3_param_info { struct v3_edit_controller { struct v3_plugin_base; - v3_result (V3_API *set_component_state)(void* self, struct v3_bstream*); - v3_result (V3_API *set_state)(void* self, struct v3_bstream*); - v3_result (V3_API *get_state)(void* self, struct v3_bstream*); - int32_t (V3_API *get_parameter_count)(void* self); - v3_result (V3_API *get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); - v3_result (V3_API *get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); - v3_result (V3_API *get_parameter_value_for_string)(void* self, v3_param_id, int16_t* input, double* output); - double (V3_API *normalised_parameter_to_plain)(void* self, v3_param_id, double normalised); - double (V3_API *plain_parameter_to_normalised)(void* self, v3_param_id, double plain); - double (V3_API *get_parameter_normalised)(void* self, v3_param_id); - v3_result (V3_API *set_parameter_normalised)(void* self, v3_param_id, double normalised); - v3_result (V3_API *set_component_handler)(void* self, struct v3_component_handler**); - struct v3_plugin_view** (V3_API *create_view)(void* self, const char* name); + v3_result (V3_API* set_component_state)(void* self, struct v3_bstream*); + v3_result (V3_API* set_state)(void* self, struct v3_bstream*); + v3_result (V3_API* get_state)(void* self, struct v3_bstream*); + int32_t (V3_API* get_parameter_count)(void* self); + v3_result (V3_API* get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); + v3_result (V3_API* get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); + v3_result (V3_API* get_parameter_value_for_string)(void* self, v3_param_id, int16_t* input, double* output); + double (V3_API* normalised_parameter_to_plain)(void* self, v3_param_id, double normalised); + double (V3_API* plain_parameter_to_normalised)(void* self, v3_param_id, double plain); + double (V3_API* get_parameter_normalised)(void* self, v3_param_id); + v3_result (V3_API* set_parameter_normalised)(void* self, v3_param_id, double normalised); + v3_result (V3_API* set_component_handler)(void* self, struct v3_component_handler**); + struct v3_plugin_view** (V3_API* create_view)(void* self, const char* name); }; static constexpr const v3_tuid v3_edit_controller_iid = V3_ID(0xDCD7BBE3, 0x7742448D, 0xA874AACC, 0x979C759E); +/** + * midi mapping + */ + +struct v3_midi_mapping { + struct v3_funknown; + + v3_result (V3_API* get_midi_controller_assignment)(void* self, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id); +}; + +static constexpr const v3_tuid v3_midi_mapping_iid = + V3_ID(0xDF0FF9F7, 0x49B74669, 0xB63AB732, 0x7ADBF5E5); + #ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_edit_controller_cpp : v3_funknown { + v3_plugin_base base; + v3_edit_controller controller; +}; + +struct v3_midi_mapping_cpp : v3_funknown { + v3_midi_mapping map; +}; + template<> inline constexpr v3_edit_controller* v3_cpp_obj(v3_edit_controller** obj) { @@ -108,6 +135,7 @@ constexpr v3_edit_controller* v3_cpp_obj(v3_edit_controller** obj) return static_cast<v3_edit_controller*>( static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(*obj)) + sizeof(void*)*5)); } + #endif #include "align_pop.h"