--- a/src/JackCommand.h Wed Oct 14 17:45:48 2020 +0200
+++ b/src/JackCommand.h Sat Oct 17 19:36:13 2020 +0200
@@ -66,14 +66,20 @@
*/
struct MidiMessage {
uint8_t buffer[4096] = {0};
- uint32_t size;
- uint32_t time;
+ size_t size;
+ /**
+ * Time in micro seconds;
+ * starts on the first recorded note/message.
+ */
+ relpipe::common::type::Integer time;
};
/**
* JACK callbacks (called from the real-time thread)
*/
class RealTimeContext {
+ private:
+ jack_nframes_t startFrame = 0;
public:
jack_client_t* jackClient = nullptr;
jack_port_t* jackPort = nullptr;
@@ -88,17 +94,22 @@
void* buffer = jack_port_get_buffer(jackPort, frames);
if (buffer == nullptr) throw JackException(L"Unable to get port buffer."); // TODO: exception in RT callback?
+ jack_nframes_t lastFrame = jack_last_frame_time(jackClient);
+
for (jack_nframes_t i = 0, eventCount = jack_midi_get_event_count(buffer); i < eventCount; i++) {
jack_midi_event_t event;
int noData = jack_midi_event_get(&event, buffer, i);
if (noData) continue;
+ if (startFrame == 0) startFrame = lastFrame;
+
if (event.size > sizeof (MidiMessage::buffer)) {
// TODO: should not printf in RT callback:
fwprintf(stderr, L"Error: MIDI message was too large → skipping event. Maximum allowed size: %lu bytes.\n", sizeof (MidiMessage::buffer));
} else if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) {
MidiMessage m;
- m.time = event.time;
+ m.time = lastFrame - startFrame + event.time;
+ m.time = m.time * 1000 * 1000 / jack_get_sample_rate(jackClient);
m.size = event.size;
memcpy(m.buffer, event.buffer, event.size);
jack_ringbuffer_write(ringBuffer, (const char *) &m, sizeof (MidiMessage));
@@ -125,11 +136,14 @@
} realTimeContext;
static void writeRecord(std::shared_ptr<relpipe::writer::RelationalWriter> writer,
- relpipe::common::type::StringX eventType, relpipe::common::type::Integer channel,
+ relpipe::common::type::StringX eventType,
+ relpipe::common::type::Integer time,
+ relpipe::common::type::Integer channel,
relpipe::common::type::Boolean noteOn, relpipe::common::type::Integer pitch, relpipe::common::type::Integer velocity,
relpipe::common::type::Integer controllerId, relpipe::common::type::Integer value,
relpipe::common::type::StringX raw) {
writer->writeAttribute(eventType);
+ writer->writeAttribute(&time, typeid (time));
writer->writeAttribute(&channel, typeid (channel));
writer->writeAttribute(¬eOn, typeid (noteOn));
writer->writeAttribute(&pitch, typeid (pitch));
@@ -145,17 +159,16 @@
} else {
uint8_t type = event->buffer[0] & 0xF0;
uint8_t channel = event->buffer[0] & 0x0F;
-
- // TODO: write timestamp, message number
+ jack_time_t time = event->time;
if ((type == 0x90 || type == 0x80) && event->size == 3) {
- writeRecord(writer, L"note", channel, type == 0x90, event->buffer[1], event->buffer[2], 0, 0, toHex(event));
+ writeRecord(writer, L"note", time, channel, type == 0x90, event->buffer[1], event->buffer[2], 0, 0, toHex(event));
} else if (type == 0xB0 && event->size == 3) {
- writeRecord(writer, L"control", channel, false, 0, 0, event->buffer[1], event->buffer[2], toHex(event));
+ writeRecord(writer, L"control", time, channel, false, 0, 0, event->buffer[1], event->buffer[2], toHex(event));
} else if (event->buffer[0] == 0xF0) {
- writeRecord(writer, L"sysex", channel, false, 0, 0, 0, 0, toHex(event));
+ writeRecord(writer, L"sysex", time, channel, false, 0, 0, 0, 0, toHex(event));
} else {
- writeRecord(writer, L"unknown", channel, false, 0, 0, 0, 0, toHex(event));
+ writeRecord(writer, L"unknown", time, channel, false, 0, 0, 0, 0, toHex(event));
}
}
}
@@ -367,6 +380,7 @@
if (!configuration.listMidiMessages) return;
metadata.push_back({L"event", TypeId::STRING});
+ metadata.push_back({L"time", TypeId::INTEGER});
metadata.push_back({L"channel", TypeId::INTEGER});
metadata.push_back({L"note_on", TypeId::BOOLEAN});
metadata.push_back({L"note_pitch", TypeId::INTEGER});