| @@ -212,7 +212,7 @@ struct MidiGenerator { | |||||
| } | } | ||||
| } | } | ||||
| virtual void onMessage(midi::Message message) {} | |||||
| virtual void onMessage(const midi::Message &message) {} | |||||
| }; | }; | ||||
| @@ -15,38 +15,59 @@ namespace midi { | |||||
| struct Message { | struct Message { | ||||
| uint8_t size = 3; | |||||
| uint8_t bytes[3] = {}; | |||||
| /** Initialized to 3 empty bytes. */ | |||||
| std::vector<uint8_t> 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; | return bytes[0] & 0xf; | ||||
| } | } | ||||
| void setChannel(uint8_t channel) { | void setChannel(uint8_t channel) { | ||||
| if (bytes.size() < 1) | |||||
| return; | |||||
| bytes[0] = (bytes[0] & 0xf0) | (channel & 0xf); | bytes[0] = (bytes[0] & 0xf0) | (channel & 0xf); | ||||
| } | } | ||||
| uint8_t getStatus() { | |||||
| uint8_t getStatus() const { | |||||
| if (bytes.size() < 1) | |||||
| return 0; | |||||
| return bytes[0] >> 4; | return bytes[0] >> 4; | ||||
| } | } | ||||
| void setStatus(uint8_t status) { | void setStatus(uint8_t status) { | ||||
| if (bytes.size() < 1) | |||||
| return; | |||||
| bytes[0] = (bytes[0] & 0xf) | (status << 4); | bytes[0] = (bytes[0] & 0xf) | (status << 4); | ||||
| } | } | ||||
| uint8_t getNote() { | |||||
| uint8_t getNote() const { | |||||
| if (bytes.size() < 2) | |||||
| return 0; | |||||
| return bytes[1]; | return bytes[1]; | ||||
| } | } | ||||
| void setNote(uint8_t note) { | void setNote(uint8_t note) { | ||||
| if (bytes.size() < 2) | |||||
| return; | |||||
| bytes[1] = note & 0x7f; | bytes[1] = note & 0x7f; | ||||
| } | } | ||||
| uint8_t getValue() { | |||||
| uint8_t getValue() const { | |||||
| if (bytes.size() < 3) | |||||
| return 0; | |||||
| return bytes[2]; | return bytes[2]; | ||||
| } | } | ||||
| void setValue(uint8_t value) { | void setValue(uint8_t value) { | ||||
| if (bytes.size() < 3) | |||||
| return; | |||||
| bytes[2] = value & 0x7f; | bytes[2] = value & 0x7f; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -103,14 +124,14 @@ struct InputDevice : Device { | |||||
| std::set<Input*> subscribed; | std::set<Input*> subscribed; | ||||
| void subscribe(Input* input); | void subscribe(Input* input); | ||||
| void unsubscribe(Input* input); | void unsubscribe(Input* input); | ||||
| void onMessage(Message message); | |||||
| void onMessage(const Message &message); | |||||
| }; | }; | ||||
| struct OutputDevice : Device { | struct OutputDevice : Device { | ||||
| std::set<Output*> subscribed; | std::set<Output*> subscribed; | ||||
| void subscribe(Output* input); | void subscribe(Output* input); | ||||
| void unsubscribe(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<int> getChannels() override; | std::vector<int> getChannels() override; | ||||
| virtual void onMessage(Message message) {} | |||||
| virtual void onMessage(const Message &message) {} | |||||
| }; | }; | ||||
| struct InputQueue : Input { | struct InputQueue : Input { | ||||
| int queueMaxSize = 8192; | int queueMaxSize = 8192; | ||||
| std::queue<Message> queue; | std::queue<Message> 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<int> getChannels() override; | std::vector<int> getChannels() override; | ||||
| void sendMessage(Message message); | |||||
| void sendMessage(const Message &message); | |||||
| }; | }; | ||||
| @@ -6,7 +6,7 @@ namespace core { | |||||
| struct MidiOutput : dsp::MidiGenerator<PORT_MAX_CHANNELS>, midi::Output { | struct MidiOutput : dsp::MidiGenerator<PORT_MAX_CHANNELS>, midi::Output { | ||||
| void onMessage(midi::Message message) override { | |||||
| void onMessage(const midi::Message &message) override { | |||||
| Output::sendMessage(message); | Output::sendMessage(message); | ||||
| } | } | ||||
| @@ -48,8 +48,8 @@ struct MIDI_CC : Module { | |||||
| } | } | ||||
| void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
| midi::Message msg; | |||||
| while (midiInput.shift(&msg)) { | |||||
| while (!midiInput.empty()) { | |||||
| midi::Message msg = midiInput.shift(); | |||||
| processMessage(msg); | processMessage(msg); | ||||
| } | } | ||||
| @@ -73,7 +73,7 @@ struct MIDI_CC : Module { | |||||
| } | } | ||||
| } | } | ||||
| void processMessage(midi::Message msg) { | |||||
| void processMessage(const midi::Message &msg) { | |||||
| switch (msg.getStatus()) { | switch (msg.getStatus()) { | ||||
| // cc | // cc | ||||
| case 0xb: { | 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(); | uint8_t cc = msg.getNote(); | ||||
| if (msg.bytes.size() < 2) | |||||
| return; | |||||
| // Allow CC to be negative if the 8th bit is set. | // Allow CC to be negative if the 8th bit is set. | ||||
| // The gamepad driver abuses this, for example. | // The gamepad driver abuses this, for example. | ||||
| // Cast uint8_t to int8_t | // Cast uint8_t to int8_t | ||||
| @@ -119,8 +119,8 @@ struct MIDI_CV : Module { | |||||
| } | } | ||||
| void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
| midi::Message msg; | |||||
| while (midiInput.shift(&msg)) { | |||||
| while (!midiInput.empty()) { | |||||
| midi::Message msg = midiInput.shift(); | |||||
| processMessage(msg); | processMessage(msg); | ||||
| } | } | ||||
| @@ -159,7 +159,7 @@ struct MIDI_CV : Module { | |||||
| outputs[CONTINUE_OUTPUT].setVoltage(continuePulse.process(args.sampleTime) ? 10.f : 0.f); | 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()); | // DEBUG("MIDI: %01x %01x %02x %02x", msg.getStatus(), msg.getChannel(), msg.getNote(), msg.getValue()); | ||||
| switch (msg.getStatus()) { | 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()) { | switch (msg.getNote()) { | ||||
| // mod | // mod | ||||
| case 0x01: { | case 0x01: { | ||||
| @@ -235,7 +235,7 @@ struct MIDI_CV : Module { | |||||
| } | } | ||||
| } | } | ||||
| void processSystem(midi::Message msg) { | |||||
| void processSystem(const midi::Message &msg) { | |||||
| switch (msg.getChannel()) { | switch (msg.getChannel()) { | ||||
| // Timing | // Timing | ||||
| case 0x8: { | case 0x8: { | ||||
| @@ -55,8 +55,8 @@ struct MIDI_Gate : Module { | |||||
| } | } | ||||
| void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
| midi::Message msg; | |||||
| while (midiInput.shift(&msg)) { | |||||
| while (!midiInput.empty()) { | |||||
| midi::Message msg = midiInput.shift(); | |||||
| processMessage(msg); | processMessage(msg); | ||||
| } | } | ||||
| @@ -75,7 +75,7 @@ struct MIDI_Gate : Module { | |||||
| } | } | ||||
| } | } | ||||
| void processMessage(midi::Message msg) { | |||||
| void processMessage(const midi::Message &msg) { | |||||
| switch (msg.getStatus()) { | switch (msg.getStatus()) { | ||||
| // note off | // note off | ||||
| case 0x8: { | case 0x8: { | ||||
| @@ -78,8 +78,8 @@ struct MIDI_Map : Module { | |||||
| void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
| if (divider.process()) { | if (divider.process()) { | ||||
| midi::Message msg; | |||||
| while (midiInput.shift(&msg)) { | |||||
| while (!midiInput.empty()) { | |||||
| midi::Message msg = midiInput.shift(); | |||||
| processMessage(msg); | 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()); | // DEBUG("MIDI: %01x %01x %02x %02x", msg.getStatus(), msg.getChannel(), msg.getNote(), msg.getValue()); | ||||
| switch (msg.getStatus()) { | 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(); | uint8_t cc = msg.getNote(); | ||||
| int8_t value = msg.getValue(); | int8_t value = msg.getValue(); | ||||
| // Learn | // Learn | ||||
| @@ -51,7 +51,7 @@ struct InputDevice : midi::InputDevice { | |||||
| midi::Message msg; | midi::Message msg; | ||||
| msg.setStatus(0xb); | msg.setStatus(0xb); | ||||
| msg.setNote(i); | msg.setNote(i); | ||||
| // Allow 8th bit to be set | |||||
| // Allow 8th bit to be set to allow bipolar value hack. | |||||
| msg.bytes[2] = cc; | msg.bytes[2] = cc; | ||||
| onMessage(msg); | onMessage(msg); | ||||
| } | } | ||||
| @@ -25,7 +25,7 @@ void InputDevice::unsubscribe(Input* input) { | |||||
| subscribed.erase(it); | subscribed.erase(it); | ||||
| } | } | ||||
| void InputDevice::onMessage(Message message) { | |||||
| void InputDevice::onMessage(const Message &message) { | |||||
| for (Input* input : subscribed) { | for (Input* input : subscribed) { | ||||
| // Filter channel | // Filter channel | ||||
| if (input->channel < 0 || message.getStatus() == 0xf || message.getChannel() == input->channel) { | if (input->channel < 0 || message.getStatus() == 0xf || message.getChannel() == input->channel) { | ||||
| @@ -163,21 +163,21 @@ std::vector<int> Input::getChannels() { | |||||
| return channels; | return channels; | ||||
| } | } | ||||
| void InputQueue::onMessage(Message message) { | |||||
| void InputQueue::onMessage(const Message &message) { | |||||
| if ((int) queue.size() >= queueMaxSize) | |||||
| return; | |||||
| // Push to queue | // 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<int> Output::getChannels() { | |||||
| return channels; | return channels; | ||||
| } | } | ||||
| void Output::sendMessage(Message message) { | |||||
| void Output::sendMessage(const Message &message) { | |||||
| if (!outputDevice) | if (!outputDevice) | ||||
| return; | return; | ||||
| // Set channel | // 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); | |||||
| } | } | ||||
| @@ -45,15 +45,8 @@ struct RtMidiInputDevice : midi::InputDevice { | |||||
| if (!midiInputDevice) | if (!midiInputDevice) | ||||
| return; | 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; | midi::Message msg; | ||||
| msg.size = message->size(); | |||||
| for (int i = 0; i < msg.size; i++) { | |||||
| msg.bytes[i] = (*message)[i]; | |||||
| } | |||||
| msg.bytes = std::vector<uint8_t>(message->begin(), message->end()); | |||||
| midiInputDevice->onMessage(msg); | midiInputDevice->onMessage(msg); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -79,8 +72,9 @@ struct RtMidiOutputDevice : midi::OutputDevice { | |||||
| return name; | return name; | ||||
| } | } | ||||
| void sendMessage(midi::Message message) override { | |||||
| rtMidiOut->sendMessage(message.bytes, message.size); | |||||
| void sendMessage(const midi::Message &message) override { | |||||
| std::vector<unsigned char> bytes(message.bytes.begin(), message.bytes.end()); | |||||
| rtMidiOut->sendMessage(&bytes); | |||||
| } | } | ||||
| }; | }; | ||||