From c3a1aa5b9bb183caf7c60156ba09734070559f02 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 11:46:27 +0100 Subject: [PATCH 01/10] Implement sendNote for VST, and also add a SendNote example --- Makefile | 2 + Makefile.plugins.mk | 8 +- distrho/src/DistrhoPluginVST.cpp | 127 ++++++++++++- examples/SendNote/DistrhoPluginInfo.h | 32 ++++ examples/SendNote/Makefile | 46 +++++ examples/SendNote/README.md | 6 + examples/SendNote/SendNoteExamplePlugin.cpp | 196 ++++++++++++++++++++ examples/SendNote/SendNoteExampleUI.cpp | 151 +++++++++++++++ 8 files changed, 562 insertions(+), 6 deletions(-) create mode 100644 examples/SendNote/DistrhoPluginInfo.h create mode 100644 examples/SendNote/Makefile create mode 100644 examples/SendNote/README.md create mode 100644 examples/SendNote/SendNoteExamplePlugin.cpp create mode 100644 examples/SendNote/SendNoteExampleUI.cpp diff --git a/Makefile b/Makefile index d16647c07..d5577a5f9 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ examples: dgl $(MAKE) all -C examples/Meters $(MAKE) all -C examples/MidiThrough $(MAKE) all -C examples/Parameters + $(MAKE) all -C examples/SendNote $(MAKE) all -C examples/States ifeq ($(HAVE_CAIRO),true) @@ -58,6 +59,7 @@ clean: $(MAKE) clean -C examples/Meters $(MAKE) clean -C examples/MidiThrough $(MAKE) clean -C examples/Parameters + $(MAKE) clean -C examples/SendNote $(MAKE) clean -C examples/States $(MAKE) clean -C utils/lv2-ttl-generator ifneq ($(MACOS_OR_WINDOWS),true) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 7c1e55404..4d69c3f1c 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -98,6 +98,12 @@ DGL_FLAGS += -DDGL_EXTERNAL HAVE_DGL = true endif +ifneq ($(UI_TYPE),none) +ifneq ($(WINDOWS),true) +VST_LIBS += -lpthread +endif +endif + DGL_LIBS += $(DGL_SYSTEM_LIBS) ifneq ($(HAVE_DGL),true) @@ -234,7 +240,7 @@ $(vst): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(VST_LIBS) $(SHARED) -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index bc57b7ecc..d5175815a 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -23,6 +23,9 @@ #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT +# include "extra/Mutex.hpp" +# endif #endif #ifndef __cdecl @@ -155,13 +158,116 @@ class ParameterCheckHelper #endif }; +#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT +// ----------------------------------------------------------------------- + +class MidiSendFromEditorHelper +{ +public: + MidiSendFromEditorHelper() + : fMidiStorage(nullptr), + fMidiCount(0), + fWriterIndex(0) + { + fMidiStorage = new ShortMessage[kMidiStorageCapacity]; + } + + virtual ~MidiSendFromEditorHelper() + { + delete[] fMidiStorage; + fMidiStorage = nullptr; + } + + void clearEditorMidi() + { + MutexLocker locker(fMutex); + fMidiCount = 0; + } + + void sendEditorMidi(const uint8_t midiData[3]) + { + MutexLocker locker(fMutex); + + uint32_t count = fMidiCount; + if (count == kMidiStorageCapacity) + return; + + uint32_t index = fWriterIndex; + ShortMessage &msg = fMidiStorage[index]; + std::memcpy(msg.data, midiData, 3); + + fMidiCount = count + 1; + fWriterIndex = (index + 1) % kMidiStorageCapacity; + } + + uint32_t receiveEditorMidi(MidiEvent *events, uint32_t eventCount) + { + if (fMidiCount == 0) + return eventCount; + + MutexTryLocker locker(fMutex); + if (locker.wasNotLocked()) + return eventCount; + + // preserve the ordering of frame times according to messages before us + uint32_t frame = 0; + if (eventCount > 0) + frame = events[eventCount - 1].frame; + + uint32_t countAvailable = fMidiCount; + uint32_t readerIndex = (fWriterIndex + kMidiStorageCapacity - countAvailable) % kMidiStorageCapacity; + for (; countAvailable > 0 && eventCount < kMaxMidiEvents; --countAvailable) + { + ShortMessage msg = fMidiStorage[readerIndex]; + MidiEvent &event = events[eventCount++]; + event.frame = frame; + event.size = 3; + std::memcpy(event.data, msg.data, sizeof(uint8_t)*3); + readerIndex = (readerIndex + 1) % kMaxMidiEvents; + } + + fMidiCount = countAvailable; + return eventCount; + } + +protected: + enum + { + kMidiStorageCapacity = 256, + }; + + struct ShortMessage + { + uint8_t data[3]; + }; + + ShortMessage *fMidiStorage; + volatile uint32_t fMidiCount; + uint32_t fWriterIndex; + Mutex fMutex; +}; +#endif + +// ----------------------------------------------------------------------- + +class UIHelperVst : public ParameterCheckHelper +#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT + , public MidiSendFromEditorHelper +#endif +{ +public: + virtual ~UIHelperVst() + { + } +}; + #if DISTRHO_PLUGIN_HAS_UI // ----------------------------------------------------------------------- class UIVst { public: - UIVst(const audioMasterCallback audioMaster, AEffect* const effect, ParameterCheckHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId, const float scaleFactor) + UIVst(const audioMasterCallback audioMaster, AEffect* const effect, UIHelperVst* const uiHelper, PluginExporter* const plugin, const intptr_t winId, const float scaleFactor) : fAudioMaster(audioMaster), fEffect(effect), fUiHelper(uiHelper), @@ -318,8 +424,12 @@ class UIVst void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) { -# if 0 //DISTRHO_PLUGIN_WANT_MIDI_INPUT - // TODO +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + uint8_t midiData[3]; + midiData[0] = 0x90 | channel; + midiData[1] = note; + midiData[2] = velocity; + fUiHelper->sendEditorMidi(midiData); # else return; // unused (void)channel; @@ -338,7 +448,7 @@ class UIVst // Vst stuff const audioMasterCallback fAudioMaster; AEffect* const fEffect; - ParameterCheckHelper* const fUiHelper; + UIHelperVst* const fUiHelper; PluginExporter* const fPlugin; // Plugin UI @@ -381,7 +491,7 @@ class UIVst // ----------------------------------------------------------------------- -class PluginVst : public ParameterCheckHelper +class PluginVst : public UIHelperVst { public: PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) @@ -542,6 +652,10 @@ class PluginVst : public ParameterCheckHelper #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fMidiEventCount = 0; +#if DISTRHO_PLUGIN_HAS_UI + clearEditorMidi(); +#endif + // tell host we want MIDI events hostCallback(audioMasterWantMidi); #endif @@ -1017,6 +1131,9 @@ class PluginVst : public ParameterCheckHelper #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT +#if DISTRHO_PLUGIN_HAS_UI + fMidiEventCount = receiveEditorMidi(fMidiEvents, fMidiEventCount); +#endif fPlugin.run(inputs, outputs, sampleFrames, fMidiEvents, fMidiEventCount); fMidiEventCount = 0; #else diff --git a/examples/SendNote/DistrhoPluginInfo.h b/examples/SendNote/DistrhoPluginInfo.h new file mode 100644 index 000000000..6bde8a158 --- /dev/null +++ b/examples/SendNote/DistrhoPluginInfo.h @@ -0,0 +1,32 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2018 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED +#define DISTRHO_PLUGIN_INFO_H_INCLUDED + +#define DISTRHO_PLUGIN_BRAND "DISTRHO" +#define DISTRHO_PLUGIN_NAME "SendNote" +#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/SendNote" + +#define DISTRHO_PLUGIN_HAS_UI 1 +#define DISTRHO_PLUGIN_HAS_EMBED_UI 1 +#define DISTRHO_PLUGIN_IS_RT_SAFE 1 +#define DISTRHO_PLUGIN_NUM_INPUTS 0 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 +#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 +#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 + +#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/examples/SendNote/Makefile b/examples/SendNote/Makefile new file mode 100644 index 000000000..f10bef32b --- /dev/null +++ b/examples/SendNote/Makefile @@ -0,0 +1,46 @@ +#!/usr/bin/make -f +# Makefile for DISTRHO Plugins # +# ---------------------------- # +# Created by falkTX +# + +# -------------------------------------------------------------- +# Project name, used for binaries + +NAME = d_sendNote + +# -------------------------------------------------------------- +# Files to build + +FILES_DSP = \ + SendNoteExamplePlugin.cpp + + +FILES_UI = \ + SendNoteExampleUI.cpp + +# -------------------------------------------------------------- +# Do some magic + +include ../../Makefile.plugins.mk + +# -------------------------------------------------------------- +# Enable all possible plugin types + +ifeq ($(HAVE_JACK),true) +ifeq ($(HAVE_OPENGL),true) +TARGETS += jack +endif +endif + +ifeq ($(HAVE_OPENGL),true) +TARGETS += lv2_sep +else +TARGETS += lv2_dsp +endif + +TARGETS += vst + +all: $(TARGETS) + +# -------------------------------------------------------------- diff --git a/examples/SendNote/README.md b/examples/SendNote/README.md new file mode 100644 index 000000000..e56ee4e8f --- /dev/null +++ b/examples/SendNote/README.md @@ -0,0 +1,6 @@ +# SendNote example + +This example will show how to send MIDI notes in DPF based UIs.
+ +The UI presents a row of MIDI keys which transmit note events to a synthesizer. + diff --git a/examples/SendNote/SendNoteExamplePlugin.cpp b/examples/SendNote/SendNoteExamplePlugin.cpp new file mode 100644 index 000000000..6727febca --- /dev/null +++ b/examples/SendNote/SendNoteExamplePlugin.cpp @@ -0,0 +1,196 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2018 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPlugin.hpp" + +#include +#include + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------------------------------------- + +/** + Plugin that demonstrates sending notes from the editor in DPF. + */ +class SendNoteExamplePlugin : public Plugin +{ +public: + SendNoteExamplePlugin() + : Plugin(0, 0, 0) + { + std::memset(fNotesPlayed, 0, sizeof(fNotesPlayed)); + std::memset(fOscillatorPhases, 0, sizeof(fOscillatorPhases)); + } + +protected: + /* -------------------------------------------------------------------------------------------------------- + * Information */ + + /** + Get the plugin label. + This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters. + */ + const char* getLabel() const override + { + return "SendNote"; + } + + /** + Get an extensive comment/description about the plugin. + */ + const char* getDescription() const override + { + return "Plugin that demonstrates sending notes from the editor in DPF."; + } + + /** + Get the plugin author/maker. + */ + const char* getMaker() const override + { + return "DISTRHO"; + } + + /** + Get the plugin homepage. + */ + const char* getHomePage() const override + { + return "https://github.com/DISTRHO/DPF"; + } + + /** + Get the plugin license name (a single line of text). + For commercial plugins this should return some short copyright information. + */ + const char* getLicense() const override + { + return "ISC"; + } + + /** + Get the plugin version, in hexadecimal. + */ + uint32_t getVersion() const override + { + return d_version(1, 0, 0); + } + + /** + Get the plugin unique Id. + This value is used by LADSPA, DSSI and VST plugin formats. + */ + int64_t getUniqueId() const override + { + return d_cconst('d', 'S', 'N', 'o'); + } + + /* -------------------------------------------------------------------------------------------------------- + * Init and Internal data, unused in this plugin */ + + void initParameter(uint32_t, Parameter&) override {} + float getParameterValue(uint32_t) const override { return 0.0f;} + void setParameterValue(uint32_t, float) override {} + + /* -------------------------------------------------------------------------------------------------------- + * Audio/MIDI Processing */ + + /** + Run/process function for plugins with MIDI input. + This synthesizes the MIDI voices with a sum of sine waves. + */ + void run(const float**, float** outputs, uint32_t frames, + const MidiEvent* midiEvents, uint32_t midiEventCount) override + { + for (uint32_t i = 0; i < midiEventCount; ++i) + { + if (midiEvents[i].size <= 3) + { + uint8_t status = midiEvents[i].data[0]; + uint8_t note = midiEvents[i].data[1] & 127; + uint8_t velocity = midiEvents[i].data[2] & 127; + + switch (status & 0xf0) + { + case 0x90: + if (velocity != 0) + { + fNotesPlayed[note] = velocity; + break; + } + /* fall through */ + case 0x80: + fNotesPlayed[note] = 0; + fOscillatorPhases[note] = 0; + break; + } + } + } + + float *outputLeft = outputs[0]; + float *outputRight = outputs[1]; + + std::memset(outputLeft, 0, frames * sizeof(float)); + + for (uint32_t noteNumber = 0; noteNumber < 128; ++noteNumber) + { + if (fNotesPlayed[noteNumber] == 0) + continue; + + float notePitch = 8.17579891564 * std::exp(0.0577622650 * noteNumber); + + float phase = fOscillatorPhases[noteNumber]; + float timeStep = notePitch / getSampleRate(); + float k2pi = 2.0 * M_PI; + float gain = 0.1; + + for (uint32_t i = 0; i < frames; ++i) + { + outputLeft[i] += gain * std::sin(k2pi * phase); + phase += timeStep; + phase -= (int)phase; + } + + fOscillatorPhases[noteNumber] = phase; + } + + std::memcpy(outputRight, outputLeft, frames * sizeof(float)); + } + + // ------------------------------------------------------------------------------------------------------- + +private: + uint8_t fNotesPlayed[128]; + float fOscillatorPhases[128]; + + /** + Set our plugin class as non-copyable and add a leak detector just in case. + */ + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SendNoteExamplePlugin) +}; + +/* ------------------------------------------------------------------------------------------------------------ + * Plugin entry point, called by DPF to create a new plugin instance. */ + +Plugin* createPlugin() +{ + return new SendNoteExamplePlugin(); +} + +// ----------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/examples/SendNote/SendNoteExampleUI.cpp b/examples/SendNote/SendNoteExampleUI.cpp new file mode 100644 index 000000000..0dbadffee --- /dev/null +++ b/examples/SendNote/SendNoteExampleUI.cpp @@ -0,0 +1,151 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2019 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoPluginInfo.h" + +#include "DistrhoUI.hpp" + +#include "Window.hpp" + +#include + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------------------------------------- + +class SendNoteExampleUI : public UI +{ +public: + SendNoteExampleUI() + : UI(64*12+8, 64+8) + { + std::memset(fKeyState, 0, sizeof(fKeyState)); + } + +protected: + /* -------------------------------------------------------------------------------------------------------- + * DSP/Plugin Callbacks */ + + /** + A parameter has changed on the plugin side. + This is called by the host to inform the UI about parameter changes. + */ + void parameterChanged(uint32_t index, float value) override + { + (void)index; + (void)value; + } + + /* -------------------------------------------------------------------------------------------------------- + * Widget Callbacks */ + + /** + The OpenGL drawing function. + This UI will draw a row of 12 keys, with on/off states according to pressed status. + */ + void onDisplay() override + { + for (int key = 0; key < 12; ++key) + { + bool pressed = fKeyState[key]; + Rectangle bounds = getKeyBounds(key); + + if (pressed) + glColor3f(0.8f, 0.5f, 0.3f); + else + glColor3f(0.3f, 0.5f, 0.8f); + + bounds.draw(); + } + } + + /** + Mouse press event. + This UI will de/activate keys when you click them and reports it as MIDI note events to the plugin. + */ + bool onMouse(const MouseEvent& ev) override + { + // Test for left-clicked + pressed first. + if (ev.button != 1 || ! ev.press) + return false; + + // Find the key which is pressed, if any + int whichKey = -1; + for (int key = 0; key < 12 && whichKey == -1; ++key) + { + Rectangle bounds = getKeyBounds(key); + + if (bounds.contains(ev.pos)) + whichKey = key; + } + + if (whichKey == -1) + return false; + + // Send a note event. Velocity=0 means off + bool pressed = !fKeyState[whichKey]; + sendNote(0, kNoteOctaveStart+whichKey, pressed ? kNoteVelocity : 0); + + // Invert the pressed state of this key, and update display + fKeyState[whichKey] = pressed; + repaint(); + + return true; + } + + /** + Set our UI class as non-copyable and add a leak detector just in case. + */ + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SendNoteExampleUI) + +private: + /** + Get the bounds of a particular key of the virtual MIDI keyboard. + */ + Rectangle getKeyBounds(unsigned index) const + { + Rectangle bounds; + int padding = 8; + bounds.setX(64 * index + padding); + bounds.setY(padding); + bounds.setWidth(64 - padding); + bounds.setHeight(64 - padding); + return bounds; + } + + /** + The pressed state of one octave of a virtual MIDI keyboard. + */ + bool fKeyState[12]; + + enum + { + kNoteVelocity = 100, // velocity of sent Note-On events + kNoteOctaveStart = 60, // starting note of the virtual MIDI keyboard + }; +}; + +/* ------------------------------------------------------------------------------------------------------------ + * UI entry point, called by DPF to create a new UI instance. */ + +UI* createUI() +{ + return new SendNoteExampleUI(); +} + +// ----------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO From 4669c671860831e91fcb667005d6b06ba58f6fef Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 12:53:20 +0100 Subject: [PATCH 02/10] Make a few things const --- distrho/src/DistrhoPluginVST.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index d5175815a..eb22455bf 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -180,13 +180,13 @@ class MidiSendFromEditorHelper void clearEditorMidi() { - MutexLocker locker(fMutex); + const MutexLocker locker(fMutex); fMidiCount = 0; } void sendEditorMidi(const uint8_t midiData[3]) { - MutexLocker locker(fMutex); + const MutexLocker locker(fMutex); uint32_t count = fMidiCount; if (count == kMidiStorageCapacity) @@ -205,7 +205,7 @@ class MidiSendFromEditorHelper if (fMidiCount == 0) return eventCount; - MutexTryLocker locker(fMutex); + const MutexTryLocker locker(fMutex); if (locker.wasNotLocked()) return eventCount; From f904fee84bf440816107fb527f8f5a792ca86cfb Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 15:47:00 +0100 Subject: [PATCH 03/10] Move the MIDI queue outside of VST --- distrho/src/DistrhoPluginInternal.hpp | 100 ++++++++++++++++++++++++++ distrho/src/DistrhoPluginVST.cpp | 77 ++------------------ 2 files changed, 106 insertions(+), 71 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index b05663ef4..076386418 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -19,6 +19,10 @@ #include "../DistrhoPlugin.hpp" +#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT +# include "extra/Mutex.hpp" +#endif + START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- @@ -672,6 +676,102 @@ class PluginExporter DISTRHO_PREVENT_HEAP_ALLOCATION }; +#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT +// ----------------------------------------------------------------------- +// Midi queue class + +/** + Single-consumer, single-producer FIFO queue of short MIDI messages, intended + for UI-to-DSP messaging in case of VST or similar plugin formats. + The access is guarded by mutex, using try-lock on the receiving side. + */ +class SimpleMidiQueue +{ +public: + SimpleMidiQueue() + : fMidiStorage(nullptr), + fMidiCount(0), + fWriterIndex(0) + { + fMidiStorage = new ShortMessage[kMidiStorageCapacity]; + } + + virtual ~SimpleMidiQueue() + { + delete[] fMidiStorage; + fMidiStorage = nullptr; + } + + void clear() + { + const MutexLocker locker(fMutex); + fMidiCount = 0; + } + + void send(const uint8_t midiData[3]) + { + const MutexLocker locker(fMutex); + + uint32_t count = fMidiCount; + if (count == kMidiStorageCapacity) + return; + + uint32_t index = fWriterIndex; + ShortMessage &msg = fMidiStorage[index]; + std::memcpy(msg.data, midiData, 3); + + fMidiCount = count + 1; + fWriterIndex = (index + 1) % kMidiStorageCapacity; + } + + uint32_t receive(MidiEvent *events, uint32_t eventCount) + { + if (fMidiCount == 0) + return eventCount; + + const MutexTryLocker locker(fMutex); + if (locker.wasNotLocked()) + return eventCount; + + // preserve the ordering of frame times according to messages before us + uint32_t frame = 0; + if (eventCount > 0) + frame = events[eventCount - 1].frame; + + uint32_t countAvailable = fMidiCount; + uint32_t readerIndex = (fWriterIndex + kMidiStorageCapacity - countAvailable) % kMidiStorageCapacity; + for (; countAvailable > 0 && eventCount < kMaxMidiEvents; --countAvailable) + { + ShortMessage msg = fMidiStorage[readerIndex]; + MidiEvent &event = events[eventCount++]; + event.frame = frame; + event.size = 3; + std::memcpy(event.data, msg.data, sizeof(uint8_t)*3); + readerIndex = (readerIndex + 1) % kMaxMidiEvents; + } + + fMidiCount = countAvailable; + return eventCount; + } + +protected: + enum + { + kMidiStorageCapacity = 256, + }; + + struct ShortMessage + { + uint8_t data[3]; + }; + + ShortMessage *fMidiStorage; + volatile uint32_t fMidiCount; + uint32_t fWriterIndex; + Mutex fMutex; +}; +#endif + // ----------------------------------------------------------------------- END_NAMESPACE_DISTRHO diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index eb22455bf..109b386b0 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -23,9 +23,6 @@ #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT -# include "extra/Mutex.hpp" -# endif #endif #ifndef __cdecl @@ -164,87 +161,25 @@ class ParameterCheckHelper class MidiSendFromEditorHelper { public: - MidiSendFromEditorHelper() - : fMidiStorage(nullptr), - fMidiCount(0), - fWriterIndex(0) - { - fMidiStorage = new ShortMessage[kMidiStorageCapacity]; - } - - virtual ~MidiSendFromEditorHelper() - { - delete[] fMidiStorage; - fMidiStorage = nullptr; - } + virtual ~MidiSendFromEditorHelper() {} void clearEditorMidi() { - const MutexLocker locker(fMutex); - fMidiCount = 0; + fQueue.clear(); } void sendEditorMidi(const uint8_t midiData[3]) { - const MutexLocker locker(fMutex); - - uint32_t count = fMidiCount; - if (count == kMidiStorageCapacity) - return; - - uint32_t index = fWriterIndex; - ShortMessage &msg = fMidiStorage[index]; - std::memcpy(msg.data, midiData, 3); - - fMidiCount = count + 1; - fWriterIndex = (index + 1) % kMidiStorageCapacity; + fQueue.send(midiData); } uint32_t receiveEditorMidi(MidiEvent *events, uint32_t eventCount) { - if (fMidiCount == 0) - return eventCount; - - const MutexTryLocker locker(fMutex); - if (locker.wasNotLocked()) - return eventCount; - - // preserve the ordering of frame times according to messages before us - uint32_t frame = 0; - if (eventCount > 0) - frame = events[eventCount - 1].frame; - - uint32_t countAvailable = fMidiCount; - uint32_t readerIndex = (fWriterIndex + kMidiStorageCapacity - countAvailable) % kMidiStorageCapacity; - for (; countAvailable > 0 && eventCount < kMaxMidiEvents; --countAvailable) - { - ShortMessage msg = fMidiStorage[readerIndex]; - MidiEvent &event = events[eventCount++]; - event.frame = frame; - event.size = 3; - std::memcpy(event.data, msg.data, sizeof(uint8_t)*3); - readerIndex = (readerIndex + 1) % kMaxMidiEvents; - } - - fMidiCount = countAvailable; - return eventCount; + return fQueue.receive(events, eventCount); } -protected: - enum - { - kMidiStorageCapacity = 256, - }; - - struct ShortMessage - { - uint8_t data[3]; - }; - - ShortMessage *fMidiStorage; - volatile uint32_t fMidiCount; - uint32_t fWriterIndex; - Mutex fMutex; +private: + SimpleMidiQueue fQueue; }; #endif From 1adbf81544866cc97bf88b63135bc872ba3b4e89 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 16:38:10 +0100 Subject: [PATCH 04/10] Fix the coding style where necessary --- distrho/src/DistrhoPluginInternal.hpp | 4 ++-- distrho/src/DistrhoPluginVST.cpp | 2 +- examples/SendNote/SendNoteExamplePlugin.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 076386418..b167c37f0 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -724,7 +724,7 @@ class SimpleMidiQueue fWriterIndex = (index + 1) % kMidiStorageCapacity; } - uint32_t receive(MidiEvent *events, uint32_t eventCount) + uint32_t receive(MidiEvent* events, uint32_t eventCount) { if (fMidiCount == 0) return eventCount; @@ -765,7 +765,7 @@ class SimpleMidiQueue uint8_t data[3]; }; - ShortMessage *fMidiStorage; + ShortMessage* fMidiStorage; volatile uint32_t fMidiCount; uint32_t fWriterIndex; Mutex fMutex; diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index 109b386b0..b3ceb5231 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -173,7 +173,7 @@ class MidiSendFromEditorHelper fQueue.send(midiData); } - uint32_t receiveEditorMidi(MidiEvent *events, uint32_t eventCount) + uint32_t receiveEditorMidi(MidiEvent* events, uint32_t eventCount) { return fQueue.receive(events, eventCount); } diff --git a/examples/SendNote/SendNoteExamplePlugin.cpp b/examples/SendNote/SendNoteExamplePlugin.cpp index 6727febca..7d14c45cb 100644 --- a/examples/SendNote/SendNoteExamplePlugin.cpp +++ b/examples/SendNote/SendNoteExamplePlugin.cpp @@ -141,8 +141,8 @@ class SendNoteExamplePlugin : public Plugin } } - float *outputLeft = outputs[0]; - float *outputRight = outputs[1]; + float* outputLeft = outputs[0]; + float* outputRight = outputs[1]; std::memset(outputLeft, 0, frames * sizeof(float)); From 2aa84642a05e774600388bd7c7ecb6bf2b6ced81 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 17:56:45 +0100 Subject: [PATCH 05/10] Allow it to build when namespace DGL is not used --- examples/SendNote/SendNoteExampleUI.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/SendNote/SendNoteExampleUI.cpp b/examples/SendNote/SendNoteExampleUI.cpp index 0dbadffee..ae43ff6d6 100644 --- a/examples/SendNote/SendNoteExampleUI.cpp +++ b/examples/SendNote/SendNoteExampleUI.cpp @@ -61,7 +61,7 @@ class SendNoteExampleUI : public UI for (int key = 0; key < 12; ++key) { bool pressed = fKeyState[key]; - Rectangle bounds = getKeyBounds(key); + DGL::Rectangle bounds = getKeyBounds(key); if (pressed) glColor3f(0.8f, 0.5f, 0.3f); @@ -86,7 +86,7 @@ class SendNoteExampleUI : public UI int whichKey = -1; for (int key = 0; key < 12 && whichKey == -1; ++key) { - Rectangle bounds = getKeyBounds(key); + DGL::Rectangle bounds = getKeyBounds(key); if (bounds.contains(ev.pos)) whichKey = key; @@ -115,9 +115,9 @@ class SendNoteExampleUI : public UI /** Get the bounds of a particular key of the virtual MIDI keyboard. */ - Rectangle getKeyBounds(unsigned index) const + DGL::Rectangle getKeyBounds(unsigned index) const { - Rectangle bounds; + DGL::Rectangle bounds; int padding = 8; bounds.setX(64 * index + padding); bounds.setY(padding); From 087d7bec05f06af8d41084ebc49bf4fe5848afcd Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 18:24:32 +0100 Subject: [PATCH 06/10] Implement sendNote for JACK --- Makefile.plugins.mk | 6 +-- distrho/src/DistrhoPluginJack.cpp | 61 +++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 4d69c3f1c..d15418d8f 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -100,7 +100,7 @@ endif ifneq ($(UI_TYPE),none) ifneq ($(WINDOWS),true) -VST_LIBS += -lpthread +THREAD_LIBS += -lpthread endif endif @@ -177,7 +177,7 @@ $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating JACK standalone for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(shell $(PKG_CONFIG) --libs jack) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(THREAD_LIBS) $(shell $(PKG_CONFIG) --libs jack) -o $@ # --------------------------------------------------------------------------------------------------------------------- # LADSPA @@ -240,7 +240,7 @@ $(vst): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(VST_LIBS) $(SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(THREAD_LIBS) $(SHARED) -o $@ # --------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index 7cb0684a9..e1fa0c642 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -101,7 +101,7 @@ class PluginJack PluginJack(jack_client_t* const client) : fPlugin(this, writeMidiCallback), #if DISTRHO_PLUGIN_HAS_UI - fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, getDesktopScaleFactor(), fPlugin.getInstancePointer()), + fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, sendNoteCallback, setSizeCallback, getDesktopScaleFactor(), fPlugin.getInstancePointer()), #endif fClient(client) { @@ -356,12 +356,13 @@ class PluginJack jack_midi_clear_buffer(fPortMidiOutBuffer); #endif - if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf)) - { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - uint32_t midiEventCount = 0; - MidiEvent midiEvents[eventCount]; + uint32_t midiEventCount = 0; + MidiEvent midiEvents[kMaxMidiEvents]; #endif + + if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf)) + { jack_midi_event_t jevent; for (uint32_t i=0; i < eventCount; ++i) @@ -410,27 +411,26 @@ class PluginJack #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - MidiEvent& midiEvent(midiEvents[midiEventCount++]); + if (midiEventCount < kMaxMidiEvents) + { + MidiEvent& midiEvent(midiEvents[midiEventCount++]); - midiEvent.frame = jevent.time; - midiEvent.size = jevent.size; + midiEvent.frame = jevent.time; + midiEvent.size = jevent.size; - if (midiEvent.size > MidiEvent::kDataSize) - midiEvent.dataExt = jevent.buffer; - else - std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size); + if (midiEvent.size > MidiEvent::kDataSize) + midiEvent.dataExt = jevent.buffer; + else + std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size); + } #endif } - -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount); -#endif } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - else - { - fPlugin.run(audioIns, audioOuts, nframes, nullptr, 0); - } +# if DISTRHO_PLUGIN_HAS_UI + midiEventCount = fMidiQueue.receive(midiEvents, midiEventCount); +# endif + fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount); #else fPlugin.run(audioIns, audioOuts, nframes); #endif @@ -466,6 +466,17 @@ class PluginJack #endif #if DISTRHO_PLUGIN_HAS_UI +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + uint8_t midiData[3]; + midiData[0] = 0x90 | channel; + midiData[1] = note; + midiData[2] = velocity; + fMidiQueue.send(midiData); + } +# endif + void setSize(const uint width, const uint height) { fUI.setWindowSize(width, height); @@ -535,6 +546,9 @@ class PluginJack # if DISTRHO_PLUGIN_WANT_PROGRAMS int fProgramChanged; # endif +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + SimpleMidiQueue fMidiQueue; +# endif #endif // ------------------------------------------------------------------- @@ -578,6 +592,13 @@ class PluginJack #endif #if DISTRHO_PLUGIN_HAS_UI + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT + thisPtr->sendNote(channel, note, velocity); +#endif + } + static void setSizeCallback(void* ptr, uint width, uint height) { thisPtr->setSize(width, height); From 8e637c64cb46e3327c7a57caef22941b7a4a6b84 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 18:30:02 +0100 Subject: [PATCH 07/10] Clean up unused variables --- distrho/src/DistrhoPluginJack.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index e1fa0c642..261fb18c2 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -596,7 +596,12 @@ class PluginJack { # if DISTRHO_PLUGIN_WANT_MIDI_INPUT thisPtr->sendNote(channel, note, velocity); -#endif +# else + (void)ptr; + (void)channel; + (void)note; + (void)velocity; +# endif } static void setSizeCallback(void* ptr, uint width, uint height) From 1a9839e9344d91de5bce0ab69f1ed8a7d99af9c2 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 18:30:17 +0100 Subject: [PATCH 08/10] Require pthread also on Windows --- Makefile.plugins.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index d15418d8f..c30ebc7da 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -99,10 +99,8 @@ HAVE_DGL = true endif ifneq ($(UI_TYPE),none) -ifneq ($(WINDOWS),true) THREAD_LIBS += -lpthread endif -endif DGL_LIBS += $(DGL_SYSTEM_LIBS) From c7de707f7f287e0a303e91af6a22a7113f89adcb Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 8 Jan 2020 18:49:31 +0100 Subject: [PATCH 09/10] Fixing the reference to DGL Rectangle, take 2 --- examples/SendNote/SendNoteExampleUI.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/SendNote/SendNoteExampleUI.cpp b/examples/SendNote/SendNoteExampleUI.cpp index ae43ff6d6..e84bcf266 100644 --- a/examples/SendNote/SendNoteExampleUI.cpp +++ b/examples/SendNote/SendNoteExampleUI.cpp @@ -24,6 +24,11 @@ START_NAMESPACE_DISTRHO +/** + We need the rectangle class from DGL. + */ +using DGL_NAMESPACE::Rectangle; + // ----------------------------------------------------------------------------------------------------------- class SendNoteExampleUI : public UI @@ -61,7 +66,7 @@ class SendNoteExampleUI : public UI for (int key = 0; key < 12; ++key) { bool pressed = fKeyState[key]; - DGL::Rectangle bounds = getKeyBounds(key); + Rectangle bounds = getKeyBounds(key); if (pressed) glColor3f(0.8f, 0.5f, 0.3f); @@ -86,7 +91,7 @@ class SendNoteExampleUI : public UI int whichKey = -1; for (int key = 0; key < 12 && whichKey == -1; ++key) { - DGL::Rectangle bounds = getKeyBounds(key); + Rectangle bounds = getKeyBounds(key); if (bounds.contains(ev.pos)) whichKey = key; @@ -115,9 +120,9 @@ class SendNoteExampleUI : public UI /** Get the bounds of a particular key of the virtual MIDI keyboard. */ - DGL::Rectangle getKeyBounds(unsigned index) const + Rectangle getKeyBounds(unsigned index) const { - DGL::Rectangle bounds; + Rectangle bounds; int padding = 8; bounds.setX(64 * index + padding); bounds.setY(padding); From df89c74914732c2851e396428d48a9e3eda4cda9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 27 Jan 2020 16:23:33 +0100 Subject: [PATCH 10/10] sendMidi() API for arbitrary short events from UI --- distrho/DistrhoUI.hpp | 6 ++++++ distrho/src/DistrhoPluginCarla.cpp | 10 +++++----- distrho/src/DistrhoPluginJack.cpp | 23 ++++++++++++----------- distrho/src/DistrhoPluginVST.cpp | 23 ++++++++++++----------- distrho/src/DistrhoUI.cpp | 12 +++++++++++- distrho/src/DistrhoUIDSSI.cpp | 30 ++++++++++++++++-------------- distrho/src/DistrhoUIInternal.hpp | 16 ++++++++-------- distrho/src/DistrhoUILV2.cpp | 19 ++++++++----------- 8 files changed, 78 insertions(+), 61 deletions(-) diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index ecebc368c..07fed8ca3 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -118,6 +118,12 @@ class UI : public UIWidget @note Work in progress. Implemented for DSSI and LV2 formats. */ void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); + + /** + sendMidi. + @TODO Document this. + */ + void sendMidi(const uint8_t* data, uint32_t size); #endif #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS diff --git a/distrho/src/DistrhoPluginCarla.cpp b/distrho/src/DistrhoPluginCarla.cpp index 0bbb3a504..9c81d2b36 100644 --- a/distrho/src/DistrhoPluginCarla.cpp +++ b/distrho/src/DistrhoPluginCarla.cpp @@ -38,7 +38,7 @@ static const writeMidiFunc writeMidiCallback = nullptr; static const setStateFunc setStateCallback = nullptr; #endif #if ! DISTRHO_PLUGIN_IS_SYNTH -static const sendNoteFunc sendNoteCallback = nullptr; +static const sendMidiFunc sendMidiCallback = nullptr; #endif class UICarla @@ -46,7 +46,7 @@ class UICarla public: UICarla(const NativeHostDescriptor* const host, PluginExporter* const plugin) : fHost(host), - fUI(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, plugin->getInstancePointer()) + fUI(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendMidiCallback, setSizeCallback, plugin->getInstancePointer()) { fUI.setWindowTitle(host->uiName); @@ -116,7 +116,7 @@ class UICarla #endif #if DISTRHO_PLUGIN_IS_SYNTH - void handleSendNote(const uint8_t, const uint8_t, const uint8_t) + void handleSendMidi(const uint8_t* const, const uint32_t) { // TODO } @@ -159,9 +159,9 @@ class UICarla #endif #if DISTRHO_PLUGIN_IS_SYNTH - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + static void sendMidiCallback(void* ptr, const uint8_t* data, uint32_t size) { - handlePtr->handleSendNote(channel, note, velocity); + handlePtr->handleSendMidi(data, size); } #endif diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index 261fb18c2..081ca505e 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -101,7 +101,7 @@ class PluginJack PluginJack(jack_client_t* const client) : fPlugin(this, writeMidiCallback), #if DISTRHO_PLUGIN_HAS_UI - fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, sendNoteCallback, setSizeCallback, getDesktopScaleFactor(), fPlugin.getInstancePointer()), + fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, sendMidiCallback, setSizeCallback, getDesktopScaleFactor(), fPlugin.getInstancePointer()), #endif fClient(client) { @@ -467,12 +467,14 @@ class PluginJack #if DISTRHO_PLUGIN_HAS_UI # if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + void sendMidi(const uint8_t* const data, const uint32_t size) { - uint8_t midiData[3]; - midiData[0] = 0x90 | channel; - midiData[1] = note; - midiData[2] = velocity; + if (size > 3) + return; + + uint8_t midiData[3] = {0, 0, 0}; + memcpy(midiData, data, size); + fMidiQueue.send(midiData); } # endif @@ -592,15 +594,14 @@ class PluginJack #endif #if DISTRHO_PLUGIN_HAS_UI - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + static void sendMidiCallback(void* ptr, const uint8_t* data, uint32_t size) { # if DISTRHO_PLUGIN_WANT_MIDI_INPUT - thisPtr->sendNote(channel, note, velocity); + thisPtr->sendMidi(data, size); # else (void)ptr; - (void)channel; - (void)note; - (void)velocity; + (void)data; + (void)size; # endif } diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index b3ceb5231..ad7e90b7d 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -207,7 +207,7 @@ class UIVst fEffect(effect), fUiHelper(uiHelper), fPlugin(plugin), - fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, plugin->getInstancePointer()), + fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendMidiCallback, setSizeCallback, scaleFactor, plugin->getInstancePointer()), fShouldCaptureVstKeys(false) { // FIXME only needed for windows? @@ -357,19 +357,20 @@ class UIVst # endif } - void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + void sendMidi(const uint8_t* const data, const uint32_t size) { # if DISTRHO_PLUGIN_WANT_MIDI_INPUT - uint8_t midiData[3]; - midiData[0] = 0x90 | channel; - midiData[1] = note; - midiData[2] = velocity; + if (size > 3) + return; + + uint8_t midiData[3] = {0, 0, 0}; + memcpy(midiData, data, size); + fUiHelper->sendEditorMidi(midiData); # else return; // unused - (void)channel; - (void)note; - (void)velocity; + (void)data; + (void)size; # endif } @@ -410,9 +411,9 @@ class UIVst handlePtr->setState(key, value); } - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + static void sendMidiCallback(void* ptr, const uint8_t* data, uint32_t size) { - handlePtr->sendNote(channel, note, velocity); + handlePtr->sendMidi(data, size); } static void setSizeCallback(void* ptr, uint width, uint height) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index cc0814039..d6a936c26 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -106,7 +106,17 @@ void UI::setState(const char* key, const char* value) #if DISTRHO_PLUGIN_WANT_MIDI_INPUT void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) { - pData->sendNoteCallback(channel, note, velocity); + uint8_t data[3]; + data[0] = 0x90 | channel; + data[1] = note; + data[2] = velocity; + + sendMidi(data, sizeof(data)); +} + +void UI::sendMidi(const uint8_t* data, uint32_t size) +{ + pData->sendMidiCallback(data, size); } #endif diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp index b259c6e00..6c40d74b8 100644 --- a/distrho/src/DistrhoUIDSSI.cpp +++ b/distrho/src/DistrhoUIDSSI.cpp @@ -27,7 +27,7 @@ START_NAMESPACE_DISTRHO #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT -static const sendNoteFunc sendNoteCallback = nullptr; +static const sendMidiFunc sendMidiCallback = nullptr; #endif // ----------------------------------------------------------------------- @@ -97,7 +97,7 @@ class UIDssi { public: UIDssi(const OscData& oscData, const char* const uiTitle) - : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback), + : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendMidiCallback, setSizeCallback), fHostClosed(false), fOscData(oscData) { @@ -188,20 +188,22 @@ class UIDssi } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + void sendMidi(const uint8_t* const data, const uint32_t size) { - if (fOscData.server == nullptr) + if (size > 3) return; - if (channel > 0xF) + if (fOscData.server == nullptr) return; - uint8_t mdata[4] = { - 0, - static_cast(channel + (velocity != 0 ? 0x90 : 0x80)), - note, - velocity - }; - fOscData.send_midi(mdata); + uint8_t midiBuf[4] = {0, 0, 0, 0}; + memcpy(midiBuf + 1, data, size); + + if ((midiBuf[1] & 0xf0) == 0x90 && midiBuf[3] == 0) + { + midiBuf[1] = 0x80 | (midiBuf[1] & 0x0f); + } + + fOscData.send_midi(midiBuf); } #endif @@ -232,9 +234,9 @@ class UIDssi } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + static void sendMidiCallback(void* ptr, const uint8_t* data, uint32_t size) { - uiPtr->sendNote(channel, note, velocity); + uiPtr->sendMidi(data, size); } #endif diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index f6ddde6d0..c2f50e4da 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -51,7 +51,7 @@ extern Window* d_lastUiWindow; typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); -typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); +typedef void (*sendMidiFunc) (void* ptr, const uint8_t* data, uint32_t size); typedef void (*setSizeFunc) (void* ptr, uint width, uint height); // ----------------------------------------------------------------------- @@ -76,7 +76,7 @@ struct UI::PrivateData { editParamFunc editParamCallbackFunc; setParamFunc setParamCallbackFunc; setStateFunc setStateCallbackFunc; - sendNoteFunc sendNoteCallbackFunc; + sendMidiFunc sendMidiCallbackFunc; setSizeFunc setSizeCallbackFunc; PrivateData() noexcept @@ -93,7 +93,7 @@ struct UI::PrivateData { editParamCallbackFunc(nullptr), setParamCallbackFunc(nullptr), setStateCallbackFunc(nullptr), - sendNoteCallbackFunc(nullptr), + sendMidiCallbackFunc(nullptr), setSizeCallbackFunc(nullptr) { DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); @@ -133,10 +133,10 @@ struct UI::PrivateData { setStateCallbackFunc(callbacksPtr, key, value); } - void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) + void sendMidiCallback(const uint8_t* data, uint32_t size) { - if (sendNoteCallbackFunc != nullptr) - sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); + if (sendMidiCallbackFunc != nullptr) + sendMidiCallbackFunc(callbacksPtr, data, size); } void setSizeCallback(const uint width, const uint height) @@ -256,7 +256,7 @@ class UIExporter const editParamFunc editParamCall, const setParamFunc setParamCall, const setStateFunc setStateCall, - const sendNoteFunc sendNoteCall, + const sendMidiFunc sendMidiCall, const setSizeFunc setSizeCall, const float scaleFactor = 1.0f, void* const dspPtr = nullptr, @@ -278,7 +278,7 @@ class UIExporter fData->editParamCallbackFunc = editParamCall; fData->setParamCallbackFunc = setParamCall; fData->setStateCallbackFunc = setStateCall; - fData->sendNoteCallbackFunc = sendNoteCall; + fData->sendMidiCallbackFunc = sendMidiCall; fData->setSizeCallbackFunc = setSizeCall; #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 140909b06..09ae07bff 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -42,7 +42,7 @@ typedef struct _LV2_Atom_MidiEvent { } LV2_Atom_MidiEvent; #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT -static const sendNoteFunc sendNoteCallback = nullptr; +static const sendMidiFunc sendMidiCallback = nullptr; #endif // ----------------------------------------------------------------------- @@ -54,7 +54,7 @@ class UiLv2 const LV2_Options_Option* options, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch, const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc, const float scaleFactor, LV2UI_Widget* const widget, void* const dspPtr) - : fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, dspPtr, bundlePath), + : fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendMidiCallback, setSizeCallback, scaleFactor, dspPtr, bundlePath), fUridMap(uridMap), fUiResize(uiResz), fUiTouch(uiTouch), @@ -268,22 +268,19 @@ class UiLv2 } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + void sendMidi(const uint8_t* const data, const uint32_t size) { DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); - if (channel > 0xF) + if (size > 3) return; const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); LV2_Atom_MidiEvent atomMidiEvent; - atomMidiEvent.atom.size = 3; + atomMidiEvent.atom.size = size; atomMidiEvent.atom.type = fMidiEventURID; - - atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80); - atomMidiEvent.data[1] = note; - atomMidiEvent.data[2] = velocity; + memcpy(atomMidiEvent.data, data, size); // send to DSP side fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), fEventTransferURID, &atomMidiEvent); @@ -339,9 +336,9 @@ class UiLv2 } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + static void sendMidiCallback(void* ptr, const uint8_t* data, uint32_t size) { - uiPtr->sendNote(channel, note, velocity); + uiPtr->sendMidi(data, size); } #endif