From c713cfb864176fd759e0ad03e29722d3e96c01e8 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Thu, 28 Nov 2019 19:42:42 -0500 Subject: [PATCH] Make midi::Message a wrapper for std::vector since MIDI messages have unbounded size (due to SysEx). --- include/dsp/midi.hpp | 2 +- include/midi.hpp | 60 +++++++++++++++++++++++++++++------------- src/core/CV_MIDI.cpp | 2 +- src/core/MIDI_CC.cpp | 10 ++++--- src/core/MIDI_CV.cpp | 10 +++---- src/core/MIDI_Gate.cpp | 6 ++--- src/core/MIDI_Map.cpp | 8 +++--- src/gamepad.cpp | 2 +- src/midi.cpp | 37 +++++++++++++------------- src/rtmidi.cpp | 14 +++------- 10 files changed, 86 insertions(+), 65 deletions(-) diff --git a/include/dsp/midi.hpp b/include/dsp/midi.hpp index 660ee221..f8dd029f 100644 --- a/include/dsp/midi.hpp +++ b/include/dsp/midi.hpp @@ -212,7 +212,7 @@ struct MidiGenerator { } } - virtual void onMessage(midi::Message message) {} + virtual void onMessage(const midi::Message &message) {} }; diff --git a/include/midi.hpp b/include/midi.hpp index ba92d785..ee0de4e4 100644 --- a/include/midi.hpp +++ b/include/midi.hpp @@ -15,38 +15,59 @@ namespace midi { struct Message { - uint8_t size = 3; - uint8_t bytes[3] = {}; + /** Initialized to 3 empty bytes. */ + std::vector bytes; - uint8_t getSize() { - return size; + Message() : bytes(3) {} + + int getSize() const { + return bytes.size(); } - void setSize(uint8_t size) { - assert(size <= 3); - this->size = size; + void setSize(int size) { + bytes.resize(size); } - uint8_t getChannel() { + + uint8_t getChannel() const { + if (bytes.size() < 1) + return 0; return bytes[0] & 0xf; } void setChannel(uint8_t channel) { + if (bytes.size() < 1) + return; bytes[0] = (bytes[0] & 0xf0) | (channel & 0xf); } - uint8_t getStatus() { + + uint8_t getStatus() const { + if (bytes.size() < 1) + return 0; return bytes[0] >> 4; } void setStatus(uint8_t status) { + if (bytes.size() < 1) + return; bytes[0] = (bytes[0] & 0xf) | (status << 4); } - uint8_t getNote() { + + uint8_t getNote() const { + if (bytes.size() < 2) + return 0; return bytes[1]; } void setNote(uint8_t note) { + if (bytes.size() < 2) + return; bytes[1] = note & 0x7f; } - uint8_t getValue() { + + uint8_t getValue() const { + if (bytes.size() < 3) + return 0; return bytes[2]; } void setValue(uint8_t value) { + if (bytes.size() < 3) + return; bytes[2] = value & 0x7f; } }; @@ -103,14 +124,14 @@ struct InputDevice : Device { std::set subscribed; void subscribe(Input* input); void unsubscribe(Input* input); - void onMessage(Message message); + void onMessage(const Message &message); }; struct OutputDevice : Device { std::set subscribed; void subscribe(Output* input); void unsubscribe(Output* input); - virtual void sendMessage(Message message) {} + virtual void sendMessage(const Message &message) {} }; //////////////////// @@ -187,16 +208,19 @@ struct Input : Port { std::vector getChannels() override; - virtual void onMessage(Message message) {} + virtual void onMessage(const Message &message) {} }; struct InputQueue : Input { int queueMaxSize = 8192; std::queue queue; - void onMessage(Message message) override; - /** If a Message is available, writes `message` and return true */ - bool shift(Message* message); + void onMessage(const Message &message) override; + bool empty(); + /** Returns Message from first in queue. + You must check empty(). If the queue is empty, the behavior of this method is undefined. + */ + Message shift(); }; @@ -222,7 +246,7 @@ struct Output : Port { std::vector getChannels() override; - void sendMessage(Message message); + void sendMessage(const Message &message); }; diff --git a/src/core/CV_MIDI.cpp b/src/core/CV_MIDI.cpp index d9543469..278227dc 100644 --- a/src/core/CV_MIDI.cpp +++ b/src/core/CV_MIDI.cpp @@ -6,7 +6,7 @@ namespace core { struct MidiOutput : dsp::MidiGenerator, midi::Output { - void onMessage(midi::Message message) override { + void onMessage(const midi::Message &message) override { Output::sendMessage(message); } diff --git a/src/core/MIDI_CC.cpp b/src/core/MIDI_CC.cpp index 92decfb6..3346402d 100644 --- a/src/core/MIDI_CC.cpp +++ b/src/core/MIDI_CC.cpp @@ -48,8 +48,8 @@ struct MIDI_CC : Module { } void process(const ProcessArgs& args) override { - midi::Message msg; - while (midiInput.shift(&msg)) { + while (!midiInput.empty()) { + midi::Message msg = midiInput.shift(); processMessage(msg); } @@ -73,7 +73,7 @@ struct MIDI_CC : Module { } } - void processMessage(midi::Message msg) { + void processMessage(const midi::Message &msg) { switch (msg.getStatus()) { // cc case 0xb: { @@ -83,8 +83,10 @@ struct MIDI_CC : Module { } } - void processCC(midi::Message msg) { + void processCC(const midi::Message &msg) { uint8_t cc = msg.getNote(); + if (msg.bytes.size() < 2) + return; // Allow CC to be negative if the 8th bit is set. // The gamepad driver abuses this, for example. // Cast uint8_t to int8_t diff --git a/src/core/MIDI_CV.cpp b/src/core/MIDI_CV.cpp index e2cf7584..4a7ff19b 100644 --- a/src/core/MIDI_CV.cpp +++ b/src/core/MIDI_CV.cpp @@ -119,8 +119,8 @@ struct MIDI_CV : Module { } void process(const ProcessArgs& args) override { - midi::Message msg; - while (midiInput.shift(&msg)) { + while (!midiInput.empty()) { + midi::Message msg = midiInput.shift(); processMessage(msg); } @@ -159,7 +159,7 @@ struct MIDI_CV : Module { outputs[CONTINUE_OUTPUT].setVoltage(continuePulse.process(args.sampleTime) ? 10.f : 0.f); } - void processMessage(midi::Message msg) { + void processMessage(const midi::Message &msg) { // DEBUG("MIDI: %01x %01x %02x %02x", msg.getStatus(), msg.getChannel(), msg.getNote(), msg.getValue()); switch (msg.getStatus()) { @@ -217,7 +217,7 @@ struct MIDI_CV : Module { } } - void processCC(midi::Message msg) { + void processCC(const midi::Message &msg) { switch (msg.getNote()) { // mod case 0x01: { @@ -235,7 +235,7 @@ struct MIDI_CV : Module { } } - void processSystem(midi::Message msg) { + void processSystem(const midi::Message &msg) { switch (msg.getChannel()) { // Timing case 0x8: { diff --git a/src/core/MIDI_Gate.cpp b/src/core/MIDI_Gate.cpp index 2c30d89e..f24acb41 100644 --- a/src/core/MIDI_Gate.cpp +++ b/src/core/MIDI_Gate.cpp @@ -55,8 +55,8 @@ struct MIDI_Gate : Module { } void process(const ProcessArgs& args) override { - midi::Message msg; - while (midiInput.shift(&msg)) { + while (!midiInput.empty()) { + midi::Message msg = midiInput.shift(); processMessage(msg); } @@ -75,7 +75,7 @@ struct MIDI_Gate : Module { } } - void processMessage(midi::Message msg) { + void processMessage(const midi::Message &msg) { switch (msg.getStatus()) { // note off case 0x8: { diff --git a/src/core/MIDI_Map.cpp b/src/core/MIDI_Map.cpp index 2440c761..600df8c1 100644 --- a/src/core/MIDI_Map.cpp +++ b/src/core/MIDI_Map.cpp @@ -78,8 +78,8 @@ struct MIDI_Map : Module { void process(const ProcessArgs& args) override { if (divider.process()) { - midi::Message msg; - while (midiInput.shift(&msg)) { + while (!midiInput.empty()) { + midi::Message msg = midiInput.shift(); processMessage(msg); } @@ -123,7 +123,7 @@ struct MIDI_Map : Module { } } - void processMessage(midi::Message msg) { + void processMessage(const midi::Message &msg) { // DEBUG("MIDI: %01x %01x %02x %02x", msg.getStatus(), msg.getChannel(), msg.getNote(), msg.getValue()); switch (msg.getStatus()) { @@ -135,7 +135,7 @@ struct MIDI_Map : Module { } } - void processCC(midi::Message msg) { + void processCC(const midi::Message &msg) { uint8_t cc = msg.getNote(); int8_t value = msg.getValue(); // Learn diff --git a/src/gamepad.cpp b/src/gamepad.cpp index 4dc3d9b0..9bb85a56 100644 --- a/src/gamepad.cpp +++ b/src/gamepad.cpp @@ -51,7 +51,7 @@ struct InputDevice : midi::InputDevice { midi::Message msg; msg.setStatus(0xb); msg.setNote(i); - // Allow 8th bit to be set + // Allow 8th bit to be set to allow bipolar value hack. msg.bytes[2] = cc; onMessage(msg); } diff --git a/src/midi.cpp b/src/midi.cpp index 4b472f71..b8527279 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -25,7 +25,7 @@ void InputDevice::unsubscribe(Input* input) { subscribed.erase(it); } -void InputDevice::onMessage(Message message) { +void InputDevice::onMessage(const Message &message) { for (Input* input : subscribed) { // Filter channel if (input->channel < 0 || message.getStatus() == 0xf || message.getChannel() == input->channel) { @@ -163,21 +163,21 @@ std::vector Input::getChannels() { return channels; } -void InputQueue::onMessage(Message message) { +void InputQueue::onMessage(const Message &message) { + if ((int) queue.size() >= queueMaxSize) + return; // Push to queue - if ((int) queue.size() < queueMaxSize) - queue.push(message); + queue.push(message); } -bool InputQueue::shift(Message* message) { - if (!message) - return false; - if (!queue.empty()) { - *message = queue.front(); - queue.pop(); - return true; - } - return false; +bool InputQueue::empty() { + return queue.empty(); +} + +Message InputQueue::shift() { + Message msg = queue.front(); + queue.pop(); + return msg; } //////////////////// @@ -220,16 +220,17 @@ std::vector Output::getChannels() { return channels; } -void Output::sendMessage(Message message) { +void Output::sendMessage(const Message &message) { if (!outputDevice) return; // Set channel - if (message.getStatus() != 0xf) { - message.setChannel(channel); + Message msg = message; + if (msg.getStatus() != 0xf) { + msg.setChannel(channel); } - // DEBUG("sendMessage %02x %02x %02x", message.cmd, message.data1, message.data2); - outputDevice->sendMessage(message); + // DEBUG("sendMessage %02x %02x %02x", msg.cmd, msg.data1, msg.data2); + outputDevice->sendMessage(msg); } diff --git a/src/rtmidi.cpp b/src/rtmidi.cpp index f41e9427..7b93a4b7 100644 --- a/src/rtmidi.cpp +++ b/src/rtmidi.cpp @@ -45,15 +45,8 @@ struct RtMidiInputDevice : midi::InputDevice { if (!midiInputDevice) return; - // Users have reported that some MIDI devices can send messages >3 bytes. I don't know how this is possible, so just reject the message. - if (message->size() > 3) - return; - midi::Message msg; - msg.size = message->size(); - for (int i = 0; i < msg.size; i++) { - msg.bytes[i] = (*message)[i]; - } + msg.bytes = std::vector(message->begin(), message->end()); midiInputDevice->onMessage(msg); } }; @@ -79,8 +72,9 @@ struct RtMidiOutputDevice : midi::OutputDevice { return name; } - void sendMessage(midi::Message message) override { - rtMidiOut->sendMessage(message.bytes, message.size); + void sendMessage(const midi::Message &message) override { + std::vector bytes(message.bytes.begin(), message.bytes.end()); + rtMidiOut->sendMessage(&bytes); } };