Browse Source

Internalize implementation of midi::InputQueue. Add tryPop() method.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
6be8c94a3d
6 changed files with 85 additions and 29 deletions
  1. +12
    -3
      include/midi.hpp
  2. +2
    -6
      src/core/MIDI_CC.cpp
  3. +5
    -6
      src/core/MIDI_CV.cpp
  4. +2
    -6
      src/core/MIDI_Gate.cpp
  5. +2
    -6
      src/core/MIDI_Map.cpp
  6. +62
    -2
      src/midi.cpp

+ 12
- 3
include/midi.hpp View File

@@ -1,6 +1,5 @@
#pragma once
#include <vector>
#include <queue>
#include <set>

#include <jansson.h>
@@ -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<Message> 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();
};




+ 2
- 6
src/core/MIDI_CC.cpp View File

@@ -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;


+ 5
- 6
src/core/MIDI_CV.cpp View File

@@ -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);


+ 2
- 6
src/core/MIDI_Gate.cpp View File

@@ -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;


+ 2
- 6
src/core/MIDI_Map.cpp View File

@@ -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


+ 62
- 2
src/midi.cpp View File

@@ -1,5 +1,6 @@
#include <map>
#include <utility>
#include <queue>

#include <midi.hpp>
#include <string.hpp>
@@ -273,13 +274,72 @@ std::vector<int> 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<Message, std::vector<Message>, 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
////////////////////


Loading…
Cancel
Save