src/JackHandler.h
branchv_0
changeset 27 45a1742b9854
parent 25 e0627da77dad
equal deleted inserted replaced
26:a6d332fa816c 27:45a1742b9854
    56 	 * from the relpipe-reading thread to the jack-writing thread (callback).
    56 	 * from the relpipe-reading thread to the jack-writing thread (callback).
    57 	 */
    57 	 */
    58 	struct MidiMessage {
    58 	struct MidiMessage {
    59 		uint8_t buffer[4096] = {0};
    59 		uint8_t buffer[4096] = {0};
    60 		size_t size;
    60 		size_t size;
    61 		jack_nframes_t time;
    61 		/**
       
    62 		 * Time in micro seconds;
       
    63 		 * starts at the beginning of the playback.
       
    64 		 */
       
    65 		relpipe::common::type::Integer time;
    62 	};
    66 	};
    63 
    67 
    64 	/**
    68 	/**
    65 	 * JACK callbacks (called from the real-time thread)
    69 	 * JACK callbacks (called from the real-time thread)
    66 	 */
    70 	 */
    67 	class RealTimeContext {
    71 	class RealTimeContext {
       
    72 	private:
       
    73 		jack_nframes_t startFrame = 0;
    68 	public:
    74 	public:
    69 		jack_client_t* jackClient = nullptr;
    75 		jack_client_t* jackClient = nullptr;
    70 		jack_port_t* jackPort = nullptr;
    76 		jack_port_t* jackPort = nullptr;
    71 		jack_ringbuffer_t* ringBuffer = nullptr;
    77 		jack_ringbuffer_t* ringBuffer = nullptr;
    72 
    78 
    74 		pthread_cond_t processingDone = PTHREAD_COND_INITIALIZER;
    80 		pthread_cond_t processingDone = PTHREAD_COND_INITIALIZER;
    75 
    81 
    76 		const int RING_BUFFER_SIZE = 100;
    82 		const int RING_BUFFER_SIZE = 100;
    77 
    83 
    78 		int processCallback(jack_nframes_t frames) {
    84 		int processCallback(jack_nframes_t frames) {
    79 			const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage);
    85 			jack_nframes_t lastFrame = jack_last_frame_time(jackClient);
       
    86 
    80 			void* jackPortBuffer = jack_port_get_buffer(jackPort, frames); // jack_port_get_buffer() must be called outside the loop, otherwise it will multiply the MIDI events
    87 			void* jackPortBuffer = jack_port_get_buffer(jackPort, frames); // jack_port_get_buffer() must be called outside the loop, otherwise it will multiply the MIDI events
    81 			jack_midi_clear_buffer(jackPortBuffer); // TODO: clean buffer?
    88 			jack_midi_clear_buffer(jackPortBuffer);
    82 			for (size_t i = 0; i < queuedMessages && i < frames; i++) {
    89 			MidiMessage m;
    83 				// TODO: correct timing?
    90 			while (jack_ringbuffer_peek(ringBuffer, (char*) &m, sizeof (m)) == sizeof (m)) {
    84 				MidiMessage m;
    91 				if (startFrame == 0) startFrame = lastFrame;
    85 				jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage));
    92 
    86 				jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size);
    93 				jack_nframes_t eventFrame = std::max(0L, (m.time * jack_get_sample_rate(jackClient) / 1000 / 1000) - (lastFrame - startFrame));
    87 				if (midiData) memcpy(midiData, m.buffer, m.size);
    94 				// If std::max() does its job, the message comes from the past and missed its cycle → we will send it now rather than lose it completely.
    88 				else /* error: not enough space TODO: store and send later */;
    95 
       
    96 				if (eventFrame < frames) {
       
    97 					jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, eventFrame, m.size);
       
    98 					if (midiData) {
       
    99 						memcpy(midiData, m.buffer, m.size);
       
   100 						jack_ringbuffer_read_advance(ringBuffer, sizeof (m));
       
   101 					} // else = error: not enough space; will be kept in the ring buffer and sent in the next cycle
       
   102 				} else {
       
   103 					/**
       
   104 					 * This message does not belong to this cycle.
       
   105 					 * Its time will come later.
       
   106 					 * For now, it stays in the ring buffer.
       
   107 					 */
       
   108 					break;
       
   109 				}
    89 			}
   110 			}
    90 
   111 
    91 			if (pthread_mutex_trylock(&processingLock) == 0) {
   112 			if (pthread_mutex_trylock(&processingLock) == 0) {
    92 				pthread_cond_signal(&processingDone);
   113 				pthread_cond_signal(&processingDone);
    93 				pthread_mutex_unlock(&processingLock);
   114 				pthread_mutex_unlock(&processingLock);
   144 				else if (name == L"disconnect") return Event::DISCONNECT;
   165 				else if (name == L"disconnect") return Event::DISCONNECT;
   145 				else return Event::UNKNOWN;
   166 				else return Event::UNKNOWN;
   146 			}
   167 			}
   147 
   168 
   148 			Event event = Event::UNKNOWN;
   169 			Event event = Event::UNKNOWN;
       
   170 			relpipe::common::type::Integer time;
   149 			relpipe::common::type::Integer channel;
   171 			relpipe::common::type::Integer channel;
   150 			relpipe::common::type::Boolean noteOn;
   172 			relpipe::common::type::Boolean noteOn;
   151 			relpipe::common::type::Integer notePitch;
   173 			relpipe::common::type::Integer notePitch;
   152 			relpipe::common::type::Integer noteVelocity;
   174 			relpipe::common::type::Integer noteVelocity;
   153 			relpipe::common::type::Integer controllerId;
   175 			relpipe::common::type::Integer controllerId;
   247 		RelationContext::RecordContext& rec = rel.recordContext;
   269 		RelationContext::RecordContext& rec = rel.recordContext;
   248 
   270 
   249 		const auto attributeName = rel.attributes[rec.attributeIndex].getAttributeName();
   271 		const auto attributeName = rel.attributes[rec.attributeIndex].getAttributeName();
   250 
   272 
   251 		if (attributeName == L"event") rec.event = rec.parseEventType(value);
   273 		if (attributeName == L"event") rec.event = rec.parseEventType(value);
       
   274 		else if (attributeName == L"time") rec.time = std::stoll(value);
   252 		else if (attributeName == L"channel") rec.channel = std::stoi(value);
   275 		else if (attributeName == L"channel") rec.channel = std::stoi(value);
   253 		else if (attributeName == L"controller_id") rec.controllerId = std::stoi(value);
   276 		else if (attributeName == L"controller_id") rec.controllerId = std::stoi(value);
   254 		else if (attributeName == L"controller_value") rec.controllerValue = std::stoi(value);
   277 		else if (attributeName == L"controller_value") rec.controllerValue = std::stoi(value);
   255 		else if (attributeName == L"note_on") rec.noteOn = value == L"true";
   278 		else if (attributeName == L"note_on") rec.noteOn = value == L"true";
   256 		else if (attributeName == L"note_pitch") rec.notePitch = std::stoi(value);
   279 		else if (attributeName == L"note_pitch") rec.notePitch = std::stoi(value);
   261 
   284 
   262 		rec.attributeIndex++;
   285 		rec.attributeIndex++;
   263 
   286 
   264 		if (rec.attributeIndex == rel.attributes.size()) {
   287 		if (rec.attributeIndex == rel.attributes.size()) {
   265 
   288 
   266 			while (continueProcessing && jack_ringbuffer_write_space(realTimeContext.ringBuffer) < sizeof (MidiMessage)) waitForRTCycle(); // should not happen, the real-time thread should be faster; see also note in endOfPipe()
   289 			while (continueProcessing && jack_ringbuffer_write_space(realTimeContext.ringBuffer) < sizeof (MidiMessage)) waitForRTCycle(); // wait, if we are faster than the real-time thread
   267 			if (!continueProcessing) return;
   290 			if (!continueProcessing) return;
   268 
   291 
   269 			MidiMessage m;
   292 			MidiMessage m;
   270 
   293 
   271 			// TODO: correct timing?
   294 			m.time = rec.time;
   272 			m.time = 0;
       
   273 			m.size = 0;
   295 			m.size = 0;
   274 
   296 
   275 			if (rec.event == RelationContext::RecordContext::Event::NOTE) {
   297 			if (rec.event == RelationContext::RecordContext::Event::NOTE) {
   276 				m.size = 3;
   298 				m.size = 3;
   277 				m.buffer[0] = (rec.noteOn ? 0x90 : 0x80) | rec.channel;
   299 				m.buffer[0] = (rec.noteOn ? 0x90 : 0x80) | rec.channel;