# HG changeset patch # User František Kučera # Date 1602956173 -7200 # Node ID 4e6b33312c5a25e82c17e44b98d3b14f724254d8 # Parent 79520ded76f0f0724a75ea598be67939b522721a timing (first version) diff -r 79520ded76f0 -r 4e6b33312c5a src/JackCommand.h --- 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 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});