From 6be8c94a3d14b580b70409adc4ade52c065b3786 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 28 Jul 2021 05:33:09 -0400 Subject: [PATCH 1/3] Internalize implementation of midi::InputQueue. Add tryPop() method. --- include/midi.hpp | 15 ++++++++-- src/core/MIDI_CC.cpp | 8 ++---- src/core/MIDI_CV.cpp | 11 ++++---- src/core/MIDI_Gate.cpp | 8 ++---- src/core/MIDI_Map.cpp | 8 ++---- src/midi.cpp | 64 ++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 85 insertions(+), 29 deletions(-) diff --git a/include/midi.hpp b/include/midi.hpp index c537b810..468e3700 100644 --- a/include/midi.hpp +++ b/include/midi.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include #include #include @@ -248,10 +247,20 @@ struct Input : Port { }; +/** An Input port that stores incoming MIDI messages and releases them when ready according to their frame timestamp. +*/ struct InputQueue : Input { - int queueMaxSize = 8192; - std::queue queue; + struct Internal; + Internal* internal; + + InputQueue(); + ~InputQueue(); void onMessage(const Message& message) override; + /** Pops and returns the next message (by setting `messageOut`) if its frame timestamp is `maxFrame` or earlier. + Returns whether a message was returned. + */ + bool tryPop(Message* messageOut, int64_t maxFrame); + size_t size(); }; diff --git a/src/core/MIDI_CC.cpp b/src/core/MIDI_CC.cpp index 69295569..37bdce6d 100644 --- a/src/core/MIDI_CC.cpp +++ b/src/core/MIDI_CC.cpp @@ -71,13 +71,9 @@ struct MIDI_CC : Module { } void process(const ProcessArgs& args) override { - while (!midiInput.queue.empty()) { - const midi::Message& msg = midiInput.queue.front(); - // Don't process MIDI message until we've reached its frame. - if (msg.frame > args.frame) - break; + midi::Message msg; + while (midiInput.tryPop(&msg, args.frame)) { processMessage(msg); - midiInput.queue.pop(); } int channels = mpeMode ? 16 : 1; diff --git a/src/core/MIDI_CV.cpp b/src/core/MIDI_CV.cpp index 93e8b988..6bca6609 100644 --- a/src/core/MIDI_CV.cpp +++ b/src/core/MIDI_CV.cpp @@ -125,13 +125,12 @@ struct MIDI_CV : Module { } void process(const ProcessArgs& args) override { - while (!midiInput.queue.empty()) { - const midi::Message& msg = midiInput.queue.front(); - // Don't process MIDI message until we've reached its frame. - if (msg.frame > args.frame) - break; + if (args.frame % 1000 == 0) + printf("queue size %lu\n", midiInput.size()); + + midi::Message msg; + while (midiInput.tryPop(&msg, args.frame)) { processMessage(msg); - midiInput.queue.pop(); } outputs[PITCH_OUTPUT].setChannels(channels); diff --git a/src/core/MIDI_Gate.cpp b/src/core/MIDI_Gate.cpp index d0727339..e3e2c141 100644 --- a/src/core/MIDI_Gate.cpp +++ b/src/core/MIDI_Gate.cpp @@ -66,13 +66,9 @@ struct MIDI_Gate : Module { } void process(const ProcessArgs& args) override { - while (!midiInput.queue.empty()) { - const midi::Message& msg = midiInput.queue.front(); - // Don't process MIDI message until we've reached its frame. - if (msg.frame > args.frame) - break; + midi::Message msg; + while (midiInput.tryPop(&msg, args.frame)) { processMessage(msg); - midiInput.queue.pop(); } int channels = mpeMode ? 16 : 1; diff --git a/src/core/MIDI_Map.cpp b/src/core/MIDI_Map.cpp index 1430cfde..4d4c7041 100644 --- a/src/core/MIDI_Map.cpp +++ b/src/core/MIDI_Map.cpp @@ -82,13 +82,9 @@ struct MIDI_Map : Module { if (!divider.process()) return; - while (!midiInput.queue.empty()) { - const midi::Message& msg = midiInput.queue.front(); - // Don't process MIDI message until we've reached its frame. - if (msg.frame > args.frame) - break; + midi::Message msg; + while (midiInput.tryPop(&msg, args.frame)) { processMessage(msg); - midiInput.queue.pop(); } // Step channels diff --git a/src/midi.cpp b/src/midi.cpp index 379b4fd1..155bdf82 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -273,13 +274,72 @@ std::vector Input::getChannels() { return channels; } +//////////////////// +// InputQueue +//////////////////// + +static const size_t InputQueue_maxSize = 8192; + +struct InputQueue_Compare { + bool operator()(const Message& a, const Message& b) { + return a.frame > b.frame; + } +}; + +struct InputQueue_Queue : std::priority_queue, InputQueue_Compare> { + void reserve(size_t capacity) { + c.reserve(capacity); + } + void clear() { + // Messing with the protected container is dangerous, but completely clearing it should be fine. + c.clear(); + } +}; + +struct InputQueue::Internal { + InputQueue_Queue queue; +}; + +InputQueue::InputQueue() { + internal = new Internal; + internal->queue.reserve(InputQueue_maxSize); +} + +InputQueue::~InputQueue() { + delete internal; +} + void InputQueue::onMessage(const Message& message) { - if ((int) queue.size() >= queueMaxSize) + if (internal->queue.size() >= InputQueue_maxSize) return; // Push to queue - queue.push(message); + internal->queue.push(message); } +bool InputQueue::tryPop(Message* messageOut, int64_t maxFrame) { + if (!internal->queue.empty()) { + const Message& msg = internal->queue.top(); + if (msg.frame <= maxFrame) { + *messageOut = msg; + internal->queue.pop(); + return true; + } + + // If next MIDI message is too far in the future, clear the queue. + // This solves the issue of unconsumed messages getting stuck in the future when a DAW rewinds the engine frame. + int futureFrames = 2 * APP->engine->getBlockFrames(); + if (msg.frame - maxFrame > futureFrames) { + internal->queue.clear(); + } + } + return false; +} + +size_t InputQueue::size() { + return internal->queue.size(); +} + + //////////////////// // Output //////////////////// From 29ab1716fd2976b9fee0f1edbee9b9a153108b30 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 28 Jul 2021 05:34:39 -0400 Subject: [PATCH 2/3] Add settings::isPlugin. --- include/settings.hpp | 1 + src/settings.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/settings.hpp b/include/settings.hpp index e280de46..d54e784a 100644 --- a/include/settings.hpp +++ b/include/settings.hpp @@ -25,6 +25,7 @@ namespace settings { extern std::string settingsPath; extern bool devMode; extern bool headless; +extern bool isPlugin; // Persistent state, serialized to settings.json. diff --git a/src/settings.cpp b/src/settings.cpp index 616bd629..e71c043a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,6 +18,7 @@ namespace settings { std::string settingsPath; bool devMode = false; bool headless = false; +bool isPlugin = false; std::string token; bool windowMaximized = false; From a98acd380e0f18484f5e688b267d34e75131b400 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 28 Jul 2021 05:39:14 -0400 Subject: [PATCH 3/3] Remove MIDI-CV InputQueue debug statement. --- src/core/MIDI_CV.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/MIDI_CV.cpp b/src/core/MIDI_CV.cpp index 6bca6609..c8792942 100644 --- a/src/core/MIDI_CV.cpp +++ b/src/core/MIDI_CV.cpp @@ -125,9 +125,6 @@ struct MIDI_CV : Module { } void process(const ProcessArgs& args) override { - if (args.frame % 1000 == 0) - printf("queue size %lu\n", midiInput.size()); - midi::Message msg; while (midiInput.tryPop(&msg, args.frame)) { processMessage(msg);