src/JackCommand.h
branchv_0
changeset 17 4e6b33312c5a
parent 16 79520ded76f0
--- 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(&noteOn, 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});