src/JackCommand.h
branchv_0
changeset 17 4e6b33312c5a
parent 16 79520ded76f0
equal deleted inserted replaced
16:79520ded76f0 17:4e6b33312c5a
    64 	 * Is passed through the ring buffer
    64 	 * Is passed through the ring buffer
    65 	 * from the the jack-writing thread (callback) to the relpipe-writing thread.
    65 	 * from the the jack-writing thread (callback) to the relpipe-writing thread.
    66 	 */
    66 	 */
    67 	struct MidiMessage {
    67 	struct MidiMessage {
    68 		uint8_t buffer[4096] = {0};
    68 		uint8_t buffer[4096] = {0};
    69 		uint32_t size;
    69 		size_t size;
    70 		uint32_t time;
    70 		/**
       
    71 		 * Time in micro seconds;
       
    72 		 * starts on the first recorded note/message.
       
    73 		 */
       
    74 		relpipe::common::type::Integer time;
    71 	};
    75 	};
    72 
    76 
    73 	/**
    77 	/**
    74 	 * JACK callbacks (called from the real-time thread)
    78 	 * JACK callbacks (called from the real-time thread)
    75 	 */
    79 	 */
    76 	class RealTimeContext {
    80 	class RealTimeContext {
       
    81 	private:
       
    82 		jack_nframes_t startFrame = 0;
    77 	public:
    83 	public:
    78 		jack_client_t* jackClient = nullptr;
    84 		jack_client_t* jackClient = nullptr;
    79 		jack_port_t* jackPort = nullptr;
    85 		jack_port_t* jackPort = nullptr;
    80 		jack_ringbuffer_t* ringBuffer = nullptr;
    86 		jack_ringbuffer_t* ringBuffer = nullptr;
    81 
    87 
    86 
    92 
    87 		int processCallback(jack_nframes_t frames) {
    93 		int processCallback(jack_nframes_t frames) {
    88 			void* buffer = jack_port_get_buffer(jackPort, frames);
    94 			void* buffer = jack_port_get_buffer(jackPort, frames);
    89 			if (buffer == nullptr) throw JackException(L"Unable to get port buffer."); // TODO: exception in RT callback?
    95 			if (buffer == nullptr) throw JackException(L"Unable to get port buffer."); // TODO: exception in RT callback?
    90 
    96 
       
    97 			jack_nframes_t lastFrame = jack_last_frame_time(jackClient);
       
    98 
    91 			for (jack_nframes_t i = 0, eventCount = jack_midi_get_event_count(buffer); i < eventCount; i++) {
    99 			for (jack_nframes_t i = 0, eventCount = jack_midi_get_event_count(buffer); i < eventCount; i++) {
    92 				jack_midi_event_t event;
   100 				jack_midi_event_t event;
    93 				int noData = jack_midi_event_get(&event, buffer, i);
   101 				int noData = jack_midi_event_get(&event, buffer, i);
    94 				if (noData) continue;
   102 				if (noData) continue;
       
   103 
       
   104 				if (startFrame == 0) startFrame = lastFrame;
    95 
   105 
    96 				if (event.size > sizeof (MidiMessage::buffer)) {
   106 				if (event.size > sizeof (MidiMessage::buffer)) {
    97 					// TODO: should not printf in RT callback:
   107 					// TODO: should not printf in RT callback:
    98 					fwprintf(stderr, L"Error: MIDI message was too large → skipping event. Maximum allowed size: %lu bytes.\n", sizeof (MidiMessage::buffer));
   108 					fwprintf(stderr, L"Error: MIDI message was too large → skipping event. Maximum allowed size: %lu bytes.\n", sizeof (MidiMessage::buffer));
    99 				} else if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) {
   109 				} else if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) {
   100 					MidiMessage m;
   110 					MidiMessage m;
   101 					m.time = event.time;
   111 					m.time = lastFrame - startFrame + event.time;
       
   112 					m.time = m.time * 1000 * 1000 / jack_get_sample_rate(jackClient);
   102 					m.size = event.size;
   113 					m.size = event.size;
   103 					memcpy(m.buffer, event.buffer, event.size);
   114 					memcpy(m.buffer, event.buffer, event.size);
   104 					jack_ringbuffer_write(ringBuffer, (const char *) &m, sizeof (MidiMessage));
   115 					jack_ringbuffer_write(ringBuffer, (const char *) &m, sizeof (MidiMessage));
   105 				} else {
   116 				} else {
   106 					// TODO: should not printf in RT callback:
   117 					// TODO: should not printf in RT callback:
   123 		}
   134 		}
   124 
   135 
   125 	} realTimeContext;
   136 	} realTimeContext;
   126 
   137 
   127 	static void writeRecord(std::shared_ptr<relpipe::writer::RelationalWriter> writer,
   138 	static void writeRecord(std::shared_ptr<relpipe::writer::RelationalWriter> writer,
   128 			relpipe::common::type::StringX eventType, relpipe::common::type::Integer channel,
   139 			relpipe::common::type::StringX eventType,
       
   140 			relpipe::common::type::Integer time,
       
   141 			relpipe::common::type::Integer channel,
   129 			relpipe::common::type::Boolean noteOn, relpipe::common::type::Integer pitch, relpipe::common::type::Integer velocity,
   142 			relpipe::common::type::Boolean noteOn, relpipe::common::type::Integer pitch, relpipe::common::type::Integer velocity,
   130 			relpipe::common::type::Integer controllerId, relpipe::common::type::Integer value,
   143 			relpipe::common::type::Integer controllerId, relpipe::common::type::Integer value,
   131 			relpipe::common::type::StringX raw) {
   144 			relpipe::common::type::StringX raw) {
   132 		writer->writeAttribute(eventType);
   145 		writer->writeAttribute(eventType);
       
   146 		writer->writeAttribute(&time, typeid (time));
   133 		writer->writeAttribute(&channel, typeid (channel));
   147 		writer->writeAttribute(&channel, typeid (channel));
   134 		writer->writeAttribute(&noteOn, typeid (noteOn));
   148 		writer->writeAttribute(&noteOn, typeid (noteOn));
   135 		writer->writeAttribute(&pitch, typeid (pitch));
   149 		writer->writeAttribute(&pitch, typeid (pitch));
   136 		writer->writeAttribute(&velocity, typeid (velocity));
   150 		writer->writeAttribute(&velocity, typeid (velocity));
   137 		writer->writeAttribute(&controllerId, typeid (controllerId));
   151 		writer->writeAttribute(&controllerId, typeid (controllerId));
   143 		if (event->size == 0) {
   157 		if (event->size == 0) {
   144 			return;
   158 			return;
   145 		} else {
   159 		} else {
   146 			uint8_t type = event->buffer[0] & 0xF0;
   160 			uint8_t type = event->buffer[0] & 0xF0;
   147 			uint8_t channel = event->buffer[0] & 0x0F;
   161 			uint8_t channel = event->buffer[0] & 0x0F;
   148 
   162 			jack_time_t time = event->time;
   149 			// TODO: write timestamp, message number
       
   150 
   163 
   151 			if ((type == 0x90 || type == 0x80) && event->size == 3) {
   164 			if ((type == 0x90 || type == 0x80) && event->size == 3) {
   152 				writeRecord(writer, L"note", channel, type == 0x90, event->buffer[1], event->buffer[2], 0, 0, toHex(event));
   165 				writeRecord(writer, L"note", time, channel, type == 0x90, event->buffer[1], event->buffer[2], 0, 0, toHex(event));
   153 			} else if (type == 0xB0 && event->size == 3) {
   166 			} else if (type == 0xB0 && event->size == 3) {
   154 				writeRecord(writer, L"control", channel, false, 0, 0, event->buffer[1], event->buffer[2], toHex(event));
   167 				writeRecord(writer, L"control", time, channel, false, 0, 0, event->buffer[1], event->buffer[2], toHex(event));
   155 			} else if (event->buffer[0] == 0xF0) {
   168 			} else if (event->buffer[0] == 0xF0) {
   156 				writeRecord(writer, L"sysex", channel, false, 0, 0, 0, 0, toHex(event));
   169 				writeRecord(writer, L"sysex", time, channel, false, 0, 0, 0, 0, toHex(event));
   157 			} else {
   170 			} else {
   158 				writeRecord(writer, L"unknown", channel, false, 0, 0, 0, 0, toHex(event));
   171 				writeRecord(writer, L"unknown", time, channel, false, 0, 0, 0, 0, toHex(event));
   159 			}
   172 			}
   160 		}
   173 		}
   161 	}
   174 	}
   162 
   175 
   163 	relpipe::common::type::StringX toHex(MidiMessage* event) {
   176 	relpipe::common::type::StringX toHex(MidiMessage* event) {
   365 		if (configuration.listConnections) listConnections(writer);
   378 		if (configuration.listConnections) listConnections(writer);
   366 		if (configuration.listProperties) listProperties(writer);
   379 		if (configuration.listProperties) listProperties(writer);
   367 		if (!configuration.listMidiMessages) return;
   380 		if (!configuration.listMidiMessages) return;
   368 
   381 
   369 		metadata.push_back({L"event", TypeId::STRING});
   382 		metadata.push_back({L"event", TypeId::STRING});
       
   383 		metadata.push_back({L"time", TypeId::INTEGER});
   370 		metadata.push_back({L"channel", TypeId::INTEGER});
   384 		metadata.push_back({L"channel", TypeId::INTEGER});
   371 		metadata.push_back({L"note_on", TypeId::BOOLEAN});
   385 		metadata.push_back({L"note_on", TypeId::BOOLEAN});
   372 		metadata.push_back({L"note_pitch", TypeId::INTEGER});
   386 		metadata.push_back({L"note_pitch", TypeId::INTEGER});
   373 		metadata.push_back({L"note_velocity", TypeId::INTEGER});
   387 		metadata.push_back({L"note_velocity", TypeId::INTEGER});
   374 		metadata.push_back({L"controller_id", TypeId::INTEGER});
   388 		metadata.push_back({L"controller_id", TypeId::INTEGER});