pass MIDI messages through the ring buffer (queue) from one thread to another v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Wed, 30 Sep 2020 20:54:39 +0200
branchv_0
changeset 4 65dfbf0494a3
parent 3 baa8055c5b10
child 5 6be3464ccb2b
pass MIDI messages through the ring buffer (queue) from one thread to another
src/CMakeLists.txt
src/JackHandler.h
--- a/src/CMakeLists.txt	Wed Sep 30 00:34:41 2020 +0200
+++ b/src/CMakeLists.txt	Wed Sep 30 20:54:39 2020 +0200
@@ -34,7 +34,7 @@
 )
 
 # Link libraries:
-target_link_libraries(${EXECUTABLE_FILE} ${RELPIPE_LIBS_LIBRARIES})
+target_link_libraries(${EXECUTABLE_FILE} ${RELPIPE_LIBS_LIBRARIES} pthread)
 set_property(TARGET ${EXECUTABLE_FILE} PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
 
 install(TARGETS ${EXECUTABLE_FILE} DESTINATION bin)
--- a/src/JackHandler.h	Wed Sep 30 00:34:41 2020 +0200
+++ b/src/JackHandler.h	Wed Sep 30 20:54:39 2020 +0200
@@ -26,6 +26,7 @@
 #include <codecvt>
 #include <sys/mman.h>
 #include <signal.h>
+#include <unistd.h>
 
 #include <jack/jack.h>
 #include <jack/midiport.h>
@@ -53,6 +54,8 @@
 	jack_client_t* jackClient = nullptr;
 	jack_port_t* jackPort = nullptr;
 	jack_ringbuffer_t* ringBuffer = nullptr;
+	pthread_mutex_t messageThreadLock = PTHREAD_MUTEX_INITIALIZER;
+	pthread_cond_t dataReady = PTHREAD_COND_INITIALIZER;
 
 	std::atomic<bool> continueProcessing{true};
 
@@ -64,6 +67,15 @@
 		uint32_t time;
 	};
 
+	/**
+	 * The message being prepared before enqueing in the ringBuffer.
+	 * Not the message dequeued from the ringBuffer (different thread).
+	 */
+	MidiMessage currentMidiMessage;
+	size_t currentAttributeCount = 0;
+	size_t currentAttributeIndex = 0;
+
+
 public:
 
 	JackHandler(Configuration& configuration) : configuration(configuration) {
@@ -89,23 +101,60 @@
 	void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override {
 		// TODO: validate metadata and prepare attribute mappings (names and types are important, order does not matter)
 
+		currentAttributeCount = attributes.size();
 	}
 
 	void attribute(const relpipe::common::type::StringX& value) override {
 		// TODO: append to current message + if this is last attribute, put whole message to the ring buffer
 		// TODO: if (continueProcessing) {} ?
+
+		if (currentAttributeIndex < currentAttributeCount) {
+			// memcpy(currentMidiMessage.buffer, ….buffer, ….size);
+			// currentMidiMessage.size = …;
+			// currentMidiMessage.time = …;
+			currentAttributeIndex++;
+		} else {
+			if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) {
+				jack_ringbuffer_write(ringBuffer, (const char *) &currentMidiMessage, sizeof (MidiMessage));
+			} else {
+				fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n");
+			}
+
+			if (pthread_mutex_trylock(&messageThreadLock) == 0) {
+				pthread_cond_signal(&dataReady);
+				pthread_mutex_unlock(&messageThreadLock);
+			}
+
+			currentMidiMessage = MidiMessage();
+			currentAttributeIndex = 0;
+		}
+
 	}
 
 	void endOfPipe() {
 		// TODO: send optional (configurable) MIDI events
-		// TODO: wait until the ring buffer is empty
+
+		// Wait until the ring buffer is empty
+		while (continueProcessing && jack_ringbuffer_read_space(ringBuffer)) usleep(1000);
 	}
 
 	int dequeueMessages(jack_nframes_t frames) {
 
+		std::cout << "dequeueMessages(" << frames << ")" << std::endl; // TODO: remove debug message
 
-		// TODO: send events from the ring buffer to JACK + correct timing
-		std::cout << "dequeueMessages(" << frames << ")" << std::endl;
+		// Process messages from the ring buffer queue:
+		pthread_mutex_lock(&messageThreadLock);
+		while (continueProcessing) { // TODO: is continueProcessing needed?
+			const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage);
+			for (size_t i = 0; i < queuedMessages; ++i) {
+				MidiMessage m;
+				jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage));
+				// TODO: send events from the ring buffer to JACK + correct timing
+				std::cout << "will process MidiMessage: " << &m << "" << std::endl; // TODO: remove debug message
+			}
+			pthread_cond_wait(&dataReady, &messageThreadLock);
+		}
+		pthread_mutex_unlock(&messageThreadLock);
 
 
 		return 0;