@@ -37,7 +37,13 @@ struct Engine { | |||||
void yieldWorkers(); | void yieldWorkers(); | ||||
/** Returns the number of audio samples since the Engine's first sample. | /** 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 | // Modules | ||||
/** Adds a module to the rack engine. | /** Adds a module to the rack engine. | ||||
@@ -17,6 +17,8 @@ namespace midi { | |||||
struct Message { | struct Message { | ||||
/** Initialized to 3 empty bytes. */ | /** Initialized to 3 empty bytes. */ | ||||
std::vector<uint8_t> bytes; | std::vector<uint8_t> bytes; | ||||
/** Timestamp of MIDI message in nanoseconds. Negative if not set. */ | |||||
long timestamp = -1; | |||||
Message() : bytes(3) {} | Message() : bytes(3) {} | ||||
@@ -216,11 +218,6 @@ struct InputQueue : Input { | |||||
int queueMaxSize = 8192; | int queueMaxSize = 8192; | ||||
std::queue<Message> queue; | std::queue<Message> queue; | ||||
void onMessage(const Message &message) override; | 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(); | |||||
}; | }; | ||||
@@ -36,7 +36,7 @@ int getLogicalCoreCount(); | |||||
void setThreadName(const std::string& name); | void setThreadName(const std::string& name); | ||||
/** Returns the caller's human-readable stack trace with "\n"-separated lines. */ | /** Returns the caller's human-readable stack trace with "\n"-separated lines. */ | ||||
std::string getStackTrace(); | 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. | Currently uses std::chrono::high_resolution_clock. | ||||
*/ | */ | ||||
long getNanoseconds(); | long getNanoseconds(); | ||||
@@ -48,9 +48,15 @@ struct MIDI_CC : Module { | |||||
} | } | ||||
void process(const ProcessArgs& args) override { | 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); | processMessage(msg); | ||||
midiInput.queue.pop(); | |||||
} | } | ||||
for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
@@ -119,9 +119,15 @@ struct MIDI_CV : Module { | |||||
} | } | ||||
void process(const ProcessArgs& args) override { | 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); | processMessage(msg); | ||||
midiInput.queue.pop(); | |||||
} | } | ||||
outputs[CV_OUTPUT].setChannels(channels); | outputs[CV_OUTPUT].setChannels(channels); | ||||
@@ -55,9 +55,15 @@ struct MIDI_Gate : Module { | |||||
} | } | ||||
void process(const ProcessArgs& args) override { | 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); | processMessage(msg); | ||||
midiInput.queue.pop(); | |||||
} | } | ||||
for (int i = 0; i < 16; i++) { | for (int i = 0; i < 16; i++) { | ||||
@@ -78,9 +78,15 @@ struct MIDI_Map : Module { | |||||
void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
if (divider.process()) { | 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); | processMessage(msg); | ||||
midiInput.queue.pop(); | |||||
} | } | ||||
// Step channels | // Step channels | ||||
@@ -151,7 +151,10 @@ struct Engine::Internal { | |||||
float sampleRate = 0.f; | float sampleRate = 0.f; | ||||
float sampleTime = 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; | Module* primaryModule = NULL; | ||||
int nextModuleId = 0; | int nextModuleId = 0; | ||||
@@ -433,6 +436,10 @@ void Engine::step(int frames) { | |||||
initMXCSR(); | initMXCSR(); | ||||
random::init(); | random::init(); | ||||
internal->stepFrame = internal->frame; | |||||
internal->stepTime = system::getNanoseconds(); | |||||
internal->stepFrames = frames; | |||||
// Set sample rate | // Set sample rate | ||||
if (internal->sampleRate != settings::sampleRate) { | if (internal->sampleRate != settings::sampleRate) { | ||||
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 | // No lock, for performance | ||||
return internal->frame; | 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) { | void Engine::addModule(Module* module) { | ||||
std::lock_guard<std::recursive_mutex> lock(internal->mutex); | std::lock_guard<std::recursive_mutex> lock(internal->mutex); | ||||
assert(module); | assert(module); | ||||
@@ -1,5 +1,6 @@ | |||||
#include <midi.hpp> | #include <midi.hpp> | ||||
#include <string.hpp> | #include <string.hpp> | ||||
#include <system.hpp> | |||||
#include <map> | #include <map> | ||||
#include <utility> | #include <utility> | ||||
@@ -26,10 +27,15 @@ void InputDevice::unsubscribe(Input* input) { | |||||
} | } | ||||
void InputDevice::onMessage(const Message &message) { | 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) { | for (Input* input : subscribed) { | ||||
// Filter channel | // 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); | queue.push(message); | ||||
} | } | ||||
bool InputQueue::empty() { | |||||
return queue.empty(); | |||||
} | |||||
Message InputQueue::shift() { | |||||
Message msg = queue.front(); | |||||
queue.pop(); | |||||
return msg; | |||||
} | |||||
//////////////////// | //////////////////// | ||||
// Output | // Output | ||||
//////////////////// | //////////////////// | ||||
@@ -162,11 +162,9 @@ struct RtAudioDevice : audio::Device { | |||||
std::vector<int> getBlockSizes() override { | std::vector<int> getBlockSizes() override { | ||||
std::vector<int> blockSizes; | 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); | ||||
blockSizes.push_back((1 << i) / 2 * 3); | |||||
} | } | ||||
blockSizes.push_back(1 << 12); | |||||
return blockSizes; | return blockSizes; | ||||
} | } | ||||
int getBlockSize() override { | int getBlockSize() override { | ||||