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:
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"