[Jack-Devel] [PATCH 3/3] realtime-safe midi-dump (use ringbuffer)

PrevNext  Index
DateTue, 31 Dec 2013 13:56:47 +0100
From Robin Gareus <[hidden] at gareus dot org>
To[hidden] at lists dot jackaudio dot org
In-Reply-ToRobin Gareus [Jack-Devel] [PATCH 0/3] modernizing jack-midi-dump
Signed-off-by: Robin Gareus <[hidden]>
---
 example-clients/midi_dump.c |  151 +++++++++++++++++++++++++++----------------
 1 file changed, 94 insertions(+), 57 deletions(-)

diff --git a/example-clients/midi_dump.c b/example-clients/midi_dump.c
index 451c826..3785a44 100644
--- a/example-clients/midi_dump.c
+++ b/example-clients/midi_dump.c
@@ -1,51 +1,65 @@
+// gcc -o jack_midi_dump -Wall midi_dump.c -ljack -pthread
+
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <assert.h>
 #include <jack/jack.h>
 #include <jack/midiport.h>
+#include <jack/ringbuffer.h>
 
 #ifndef WIN32
 #include <signal.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ( (a) < (b) ? (b) : (a) )
 #endif
 
 static jack_port_t* port;
+static jack_ringbuffer_t *rb = NULL;
+static pthread_mutex_t msg_thread_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
 
 static int keeprunning = 1;
-static int time_format = 0;
-
 static uint64_t monotonic_cnt = 0;
-static uint64_t prev_event = 0;
 
-static void
-describe (jack_midi_event_t* event, char* buffer, size_t buflen)
-{
-	assert (buflen > 0);
+#define RBSIZE 512
 
-	buffer[0] = '\0';
+typedef struct {
+	uint8_t  buffer[128];
+	uint32_t size;
+	uint32_t tme_rel;
+	uint64_t tme_mon;
+} midimsg;
 
+static void
+describe (midimsg* event)
+{
 	if (event->size == 0) {
 		return;
 	}
 
-	int type = event->buffer[0] & 0xf0;
-	int channel = event->buffer[0] & 0xf;
+	uint8_t type = event->buffer[0] & 0xf0;
+	uint8_t channel = event->buffer[0] & 0xf;
 
 	switch (type) {
-	case 0x90:
-		assert (event->size == 3);
-		snprintf (buffer, buflen, "note on (channel %d): pitch %d, velocity %d", channel, event->buffer[1], event->buffer[2]);
-                break;
-	case 0x80:
-		assert (event->size == 3);
-		snprintf (buffer, buflen, "note off (channel %d): pitch %d, velocity %d", channel, event->buffer[1], event->buffer[2]);
-                break;
-	case 0xb0:
-		assert (event->size == 3);
-		snprintf (buffer, buflen, "control change (channel %d): controller %d, value %d", channel, event->buffer[1], event->buffer[2]);
-		break;
-        default:
-                break;
+		case 0x90:
+			assert (event->size == 3);
+			printf (" note on  (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]);
+			break;
+		case 0x80:
+			assert (event->size == 3);
+			printf (" note off (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]);
+			break;
+		case 0xb0:
+			assert (event->size == 3);
+			printf (" control change (channel %2d): controller %3d, value %3d", channel, event->buffer[1], event->buffer[2]);
+			break;
+		default:
+			break;
 	}
 }
 
@@ -55,7 +69,6 @@ process (jack_nframes_t frames, void* arg)
 	void* buffer;
 	jack_nframes_t N;
 	jack_nframes_t i;
-	char description[256];
 
 	buffer = jack_port_get_buffer (port, frames);
 	assert (buffer);
@@ -66,34 +79,25 @@ process (jack_nframes_t frames, void* arg)
 		int r;
 
 		r = jack_midi_event_get (&event, buffer, i);
-		if (r == 0) {
-			size_t j;
 
-			switch(time_format) {
-				case 1:
-					printf ("%7"PRId64":", event.time + monotonic_cnt);
-					break;
-				case 2:
-					printf ("%+6"PRId64":", event.time + monotonic_cnt - prev_event);
-					break;
-				default:
-					printf ("%4d:", event.time);
-					break;
-			}
-			for (j = 0; j < event.size; ++j) {
-				printf (" %x", event.buffer[j]);
-			}
-
-			describe (&event, description, sizeof (description));
-			printf (" %s", description);
+		if (r == 0 && jack_ringbuffer_write_space (rb) >= sizeof(midimsg)) {
+			midimsg m;
+			m.tme_mon = monotonic_cnt;
+			m.tme_rel = event.time;
+			m.size    = event.size;
+			memcpy (m.buffer, event.buffer, MAX(sizeof(m.buffer), event.size));
+			jack_ringbuffer_write (rb, (void *) &m, sizeof(midimsg));
 
-			printf ("\n");
-			prev_event = event.time + monotonic_cnt;
 		}
 	}
 
 	monotonic_cnt += frames;
 
+	if (pthread_mutex_trylock (&msg_thread_lock) == 0) {
+		pthread_cond_signal (&data_ready);
+		pthread_mutex_unlock (&msg_thread_lock);
+	}
+
 	return 0;
 }
 
@@ -113,14 +117,11 @@ static void usage (int status) {
 This tool listens for MIDI events on a JACK MIDI port and prints\n\
 the message to stdout.\n\
 \n\
-Note that this tool is not realtime-safe. The events are printed\n\
-directly from the process() callback!\n\
-\n\
 If no client name is given it defaults to 'midi-monitor'.\n\
 \n\
 See also: jackd(1)\n\
 \n");
-  exit (status);
+	exit (status);
 }
 
 int
@@ -129,6 +130,7 @@ main (int argc, char* argv[])
 	jack_client_t* client;
 	char const default_name[] = "midi-monitor";
 	char const * client_name;
+	int time_format = 0;
 	int r;
 
 	int cn = 1;
@@ -152,6 +154,8 @@ main (int argc, char* argv[])
 		exit (EXIT_FAILURE);
 	}
 
+	rb = jack_ringbuffer_create (RBSIZE * sizeof(midimsg));
+
 	jack_set_process_callback (client, process, 0);
 
 	port = jack_port_register (client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
@@ -160,6 +164,12 @@ main (int argc, char* argv[])
 		exit (EXIT_FAILURE);
 	}
 
+#ifndef WIN32
+	if (mlockall (MCL_CURRENT | MCL_FUTURE)) {
+		fprintf (stderr, "Warning: Can not lock memory.\n");
+	}
+#endif
+
 	r = jack_activate (client);
 	if (r != 0) {
 		fprintf (stderr, "Could not activate client.\n");
@@ -171,17 +181,44 @@ main (int argc, char* argv[])
 	signal(SIGINT, wearedone);
 #endif
 
-	/* run until interrupted */
+	pthread_mutex_lock (&msg_thread_lock);
+
+	uint64_t prev_event = 0;
 	while (keeprunning) {
-	#ifdef WIN32
-		Sleep(1000);
-	#else
-		sleep(1);
-	#endif
-	};
+		const int mqlen = jack_ringbuffer_read_space (rb) / sizeof(midimsg);
+		int i;
+		for (i=0; i < mqlen; ++i) {
+			size_t j;
+			midimsg m;
+			jack_ringbuffer_read(rb, (char*) &m, sizeof(midimsg));
+
+			switch(time_format) {
+				case 1:
+					printf ("%7"PRId64":", m.tme_rel + m.tme_mon);
+					break;
+				case 2:
+					printf ("%+6"PRId64":", m.tme_rel + m.tme_mon - prev_event);
+					break;
+				default:
+					printf ("%4d:", m.tme_rel);
+					break;
+			}
+			for (j = 0; j < m.size && j < sizeof(m.buffer); ++j) {
+				printf (" %02x", m.buffer[j]);
+			}
+
+			describe (&m);
+			printf("\n");
+			prev_event = m.tme_rel + m.tme_mon;
+		}
+		fflush (stdout);
+		pthread_cond_wait (&data_ready, &msg_thread_lock);
+	}
+	pthread_mutex_unlock (&msg_thread_lock);
 
 	jack_deactivate (client);
 	jack_client_close (client);
+	jack_ringbuffer_free (rb);
 
 	return 0;
 }
-- 
1.7.10.4
PrevNext  Index

1388494623.17276_0.ltw:2, <1388494607-1336-4-git-send-email-robin at gareus dot org>