Browse Source

Add timestamp to MIDI message. Make MIDI-* modules wait until a message is `stepFrames` frames old until processing it, improving MIDI stability. Add Engine::getStepFrame, getStepTime, and getStepFrames.

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
60d2283010
10 changed files with 75 additions and 32 deletions
  1. +7
    -1
      include/engine/Engine.hpp
  2. +2
    -5
      include/midi.hpp
  3. +1
    -1
      include/system.hpp
  4. +8
    -2
      src/core/MIDI_CC.cpp
  5. +8
    -2
      src/core/MIDI_CV.cpp
  6. +8
    -2
      src/core/MIDI_Gate.cpp
  7. +8
    -2
      src/core/MIDI_Map.cpp
  8. +24
    -2
      src/engine/Engine.cpp
  9. +8
    -12
      src/midi.cpp
  10. +1
    -3
      src/rtaudio.cpp

+ 7
- 1
include/engine/Engine.hpp View File

@@ -37,7 +37,13 @@ struct Engine {
void yieldWorkers();
/** Returns the number of audio samples since the Engine's first sample.
*/
uint64_t getFrame();
long getFrame();
/** Returns the frame when step() was last called. */
long getStepFrame();
/** Returns the timestamp in nanoseconds when step() was last called. */
long getStepTime();
/** Returns the total number of frames in the current step() call. */
int getStepFrames();

// Modules
/** Adds a module to the rack engine.


+ 2
- 5
include/midi.hpp View File

@@ -17,6 +17,8 @@ namespace midi {
struct Message {
/** Initialized to 3 empty bytes. */
std::vector<uint8_t> bytes;
/** Timestamp of MIDI message in nanoseconds. Negative if not set. */
long timestamp = -1;

Message() : bytes(3) {}

@@ -216,11 +218,6 @@ struct InputQueue : Input {
int queueMaxSize = 8192;
std::queue<Message> queue;
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();
};




+ 1
- 1
include/system.hpp View File

@@ -36,7 +36,7 @@ int getLogicalCoreCount();
void setThreadName(const std::string& name);
/** Returns the caller's human-readable stack trace with "\n"-separated lines. */
std::string getStackTrace();
/** Gets the current number of nanoseconds since the epoch.
/** Returns the current number of nanoseconds since the epoch.
Currently uses std::chrono::high_resolution_clock.
*/
long getNanoseconds();


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

@@ -48,9 +48,15 @@ struct MIDI_CC : Module {
}

void process(const ProcessArgs& args) override {
while (!midiInput.empty()) {
midi::Message msg = midiInput.shift();
// Process MIDI messages only if they arrived `stepFrames` frames ago.
while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front();
long msgTime = msg.timestamp + long(APP->engine->getStepFrames() * APP->engine->getSampleTime() * 1e9);
long frameTime = APP->engine->getStepTime() + long((APP->engine->getFrame() - APP->engine->getStepFrame()) * APP->engine->getSampleTime() * 1e9);
if (msgTime > frameTime)
break;
processMessage(msg);
midiInput.queue.pop();
}

for (int i = 0; i < 16; i++) {


+ 8
- 2
src/core/MIDI_CV.cpp View File

@@ -119,9 +119,15 @@ struct MIDI_CV : Module {
}

void process(const ProcessArgs& args) override {
while (!midiInput.empty()) {
midi::Message msg = midiInput.shift();
// Process MIDI messages only if they arrived `stepFrames` frames ago.
while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front();
long msgTime = msg.timestamp + long(APP->engine->getStepFrames() * APP->engine->getSampleTime() * 1e9);
long frameTime = APP->engine->getStepTime() + long((APP->engine->getFrame() - APP->engine->getStepFrame()) * APP->engine->getSampleTime() * 1e9);
if (msgTime > frameTime)
break;
processMessage(msg);
midiInput.queue.pop();
}

outputs[CV_OUTPUT].setChannels(channels);


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

@@ -55,9 +55,15 @@ struct MIDI_Gate : Module {
}

void process(const ProcessArgs& args) override {
while (!midiInput.empty()) {
midi::Message msg = midiInput.shift();
// Process MIDI messages only if they arrived `stepFrames` frames ago.
while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front();
long msgTime = msg.timestamp + long(APP->engine->getStepFrames() * APP->engine->getSampleTime() * 1e9);
long frameTime = APP->engine->getStepTime() + long((APP->engine->getFrame() - APP->engine->getStepFrame()) * APP->engine->getSampleTime() * 1e9);
if (msgTime > frameTime)
break;
processMessage(msg);
midiInput.queue.pop();
}

for (int i = 0; i < 16; i++) {


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

@@ -78,9 +78,15 @@ struct MIDI_Map : Module {

void process(const ProcessArgs& args) override {
if (divider.process()) {
while (!midiInput.empty()) {
midi::Message msg = midiInput.shift();
// Process MIDI messages only if they arrived `stepFrames` frames ago.
while (!midiInput.queue.empty()) {
midi::Message& msg = midiInput.queue.front();
long msgTime = msg.timestamp + long(APP->engine->getStepFrames() * APP->engine->getSampleTime() * 1e9);
long frameTime = APP->engine->getStepTime() + long((APP->engine->getFrame() - APP->engine->getStepFrame()) * APP->engine->getSampleTime() * 1e9);
if (msgTime > frameTime)
break;
processMessage(msg);
midiInput.queue.pop();
}

// Step channels


+ 24
- 2
src/engine/Engine.cpp View File

@@ -151,7 +151,10 @@ struct Engine::Internal {

float sampleRate = 0.f;
float sampleTime = 0.f;
uint64_t frame = 0;
long frame = 0;
long stepFrame = 0;
long stepTime = 0;
int stepFrames = 0;
Module* primaryModule = NULL;

int nextModuleId = 0;
@@ -433,6 +436,10 @@ void Engine::step(int frames) {
initMXCSR();
random::init();

internal->stepFrame = internal->frame;
internal->stepTime = system::getNanoseconds();
internal->stepFrames = frames;

// Set sample rate
if (internal->sampleRate != settings::sampleRate) {
internal->sampleRate = settings::sampleRate;
@@ -514,12 +521,27 @@ void Engine::yieldWorkers() {
}


uint64_t Engine::getFrame() {
long Engine::getFrame() {
// No lock, for performance
return internal->frame;
}


long Engine::getStepFrame() {
return internal->stepFrame;
}


long Engine::getStepTime() {
return internal->stepTime;
}


int Engine::getStepFrames() {
return internal->stepFrames;
}


void Engine::addModule(Module* module) {
std::lock_guard<std::recursive_mutex> lock(internal->mutex);
assert(module);


+ 8
- 12
src/midi.cpp View File

@@ -1,5 +1,6 @@
#include <midi.hpp>
#include <string.hpp>
#include <system.hpp>
#include <map>
#include <utility>

@@ -26,10 +27,15 @@ void InputDevice::unsubscribe(Input* input) {
}

void InputDevice::onMessage(const Message &message) {
// Set timestamp if unset
Message msg = message;
if (msg.timestamp < 0)
msg.timestamp = system::getNanoseconds();

for (Input* input : subscribed) {
// Filter channel
if (input->channel < 0 || message.getStatus() == 0xf || message.getChannel() == input->channel) {
input->onMessage(message);
if (input->channel < 0 || msg.getStatus() == 0xf || msg.getChannel() == input->channel) {
input->onMessage(msg);
}
}
}
@@ -170,16 +176,6 @@ void InputQueue::onMessage(const Message &message) {
queue.push(message);
}

bool InputQueue::empty() {
return queue.empty();
}

Message InputQueue::shift() {
Message msg = queue.front();
queue.pop();
return msg;
}

////////////////////
// Output
////////////////////


+ 1
- 3
src/rtaudio.cpp View File

@@ -162,11 +162,9 @@ struct RtAudioDevice : audio::Device {

std::vector<int> getBlockSizes() override {
std::vector<int> blockSizes;
for (int i = 5; i < 12; i++) {
for (int i = 5; i <= 12; i++) {
blockSizes.push_back(1 << i);
blockSizes.push_back((1 << i) / 2 * 3);
}
blockSizes.push_back(1 << 12);
return blockSizes;
}
int getBlockSize() override {


Loading…
Cancel
Save