commit da528328bf4e13004bfddf9b71e214a5bea97e95
parent ef64e046b060c6fdd2fa17d3cfcb74518946bc6e
Author: falkTX <falktx@falktx.com>
Date: Fri, 24 Sep 2021 21:22:04 +0100
VST3: Initial MIDI support, enable midi-through vst3 build
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
2 files changed, 150 insertions(+), 7 deletions(-)
diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp
@@ -220,6 +220,9 @@ public:
: fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback),
fComponentHandler(nullptr),
fComponentHandlerArg(nullptr)
+#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+ , fHostEventOutputHandle(nullptr)
+#endif
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
@@ -458,7 +461,7 @@ public:
if (busDirection == V3_INPUT)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
- DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG);
+ DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG);
#else
return V3_INVALID_ARG;
#endif
@@ -466,7 +469,7 @@ public:
else
{
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
- DISTRHO_SAFE_ASSERT_RETURN(index == 0, V3_INVALID_ARG);
+ DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG);
#else
return V3_INVALID_ARG;
#endif
@@ -771,7 +774,6 @@ public:
}
#if DISTRHO_PLUGIN_WANT_TIMEPOS
- // TODO implement v3_process_context_requirements as query_interface of something..
if (v3_process_context* const ctx = data->ctx)
{
fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING;
@@ -858,12 +860,95 @@ public:
outputs[i] = nullptr; // TODO use dummy buffer
}
+#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+ fHostEventOutputHandle = data->output_events;
+#endif
+
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
- fPlugin.run(inputs, outputs, data->nframes, nullptr, 0);
+ uint32_t midiEventCount = 0;
+
+ if (v3_event_list** const eventarg = data->input_events)
+ {
+ // offset struct by sizeof(v3_funknown), because of differences between C and C++
+ v3_event_list* const eventptr = (v3_event_list*)((uint8_t*)(*eventarg)+sizeof(v3_funknown));
+
+ v3_event event;
+ for (uint32_t i = 0, count = eventptr->get_event_count(eventarg); i < count; ++i)
+ {
+ if (eventptr->get_event(eventarg, i, &event) != V3_OK)
+ break;
+
+ // check if event can be encoded as MIDI
+ switch (event.type)
+ {
+ case V3_EVENT_NOTE_ON:
+ case V3_EVENT_NOTE_OFF:
+ // case V3_EVENT_DATA:
+ case V3_EVENT_POLY_PRESSURE:
+ // 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;
+ default:
+ continue;
+ }
+
+ MidiEvent& midiEvent(fMidiEvents[midiEventCount++]);
+ midiEvent.frame = event.sample_offset;
+
+ // encode event as MIDI
+ switch (event.type)
+ {
+ case V3_EVENT_NOTE_ON:
+ midiEvent.size = 3;
+ midiEvent.data[0] = 0x90 | (event.note_on.channel & 0xf);
+ midiEvent.data[1] = event.note_on.pitch;
+ midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_on.velocity * 127)));
+ midiEvent.data[3] = 0;
+ break;
+ case V3_EVENT_NOTE_OFF:
+ midiEvent.size = 3;
+ midiEvent.data[0] = 0x80 | (event.note_off.channel & 0xf);
+ midiEvent.data[1] = event.note_off.pitch;
+ midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_off.velocity * 127)));
+ midiEvent.data[3] = 0;
+ break;
+ case V3_EVENT_POLY_PRESSURE:
+ midiEvent.size = 3;
+ midiEvent.data[0] = 0xA0 | (event.poly_pressure.channel & 0xf);
+ midiEvent.data[1] = event.poly_pressure.pitch;
+ 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;
+ }
+
+ if (midiEventCount == kMaxMidiEvents)
+ break;
+ }
+ }
+
+ fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount);
#else
fPlugin.run(inputs, outputs, data->nframes);
#endif
+#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+ fHostEventOutputHandle = nullptr;
+#endif
+
// TODO updateParameterOutputsAndTriggers()
return V3_OK;
@@ -1032,6 +1117,12 @@ private:
void* fComponentHandlerArg;
// Temporary data
+#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
+ MidiEvent fMidiEvents[kMaxMidiEvents];
+#endif
+#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+ v3_event_list** fHostEventOutputHandle;
+#endif
#if DISTRHO_PLUGIN_WANT_TIMEPOS
TimePosition fTimePosition;
#endif
@@ -1061,8 +1152,55 @@ private:
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
bool writeMidi(const MidiEvent& midiEvent)
{
- // TODO
- return true;
+ DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fHostEventOutputHandle != nullptr, false);
+
+ v3_event event;
+ std::memset(&event, 0, sizeof(event));
+ event.sample_offset = midiEvent.frame;
+
+ const uint8_t* const data = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
+
+ switch (data[0] & 0xf0)
+ {
+ case 0x80:
+ event.type = V3_EVENT_NOTE_OFF;
+ event.note_off.channel = data[0] & 0xf;
+ event.note_off.pitch = data[1];
+ event.note_off.velocity = (float)data[2] / 127.0f;
+ // int32_t note_id;
+ // float tuning;
+ break;
+ case 0x90:
+ event.type = V3_EVENT_NOTE_ON;
+ event.note_on.channel = data[0] & 0xf;
+ event.note_on.pitch = data[1];
+ // float tuning;
+ event.note_on.velocity = (float)data[2] / 127.0f;
+ // int32_t length;
+ // int32_t note_id;
+ break;
+ case 0xA0:
+ event.type = V3_EVENT_POLY_PRESSURE;
+ event.poly_pressure.channel = data[0] & 0xf;
+ event.poly_pressure.pitch = data[1];
+ event.poly_pressure.pressure = (float)data[2] / 127.0f;
+ // int32_t note_id;
+ break;
+ case 0xB0:
+ event.type = V3_EVENT_LEGACY_MIDI_CC_OUT;
+ 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;
+ break;
+ default:
+ return true;
+ }
+
+ // offset struct by sizeof(v3_funknown), because of differences between C and C++
+ v3_event_list* const hostptr = (v3_event_list*)((uint8_t*)(*fHostEventOutputHandle)+sizeof(v3_funknown));
+
+ return hostptr->add_event(fHostEventOutputHandle, &event) == V3_OK;
}
static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
@@ -1431,12 +1569,16 @@ struct dpf_process_context_requirements : v3_process_context_requirements_cpp {
req.get_process_context_requirements = []V3_API(void*) -> uint32_t
{
+#if DISTRHO_PLUGIN_WANT_TIMEPOS
return 0x0
|V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID
|V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID
|V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID
|V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID
|V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING
+#else
+ return 0x0;
+#endif
};
}
diff --git a/examples/MidiThrough/Makefile b/examples/MidiThrough/Makefile
@@ -25,7 +25,8 @@ include ../../Makefile.plugins.mk
TARGETS += jack
TARGETS += lv2_dsp
-TARGETS += vst
+TARGETS += vst2
+TARGETS += vst3
all: $(TARGETS)