|
|
@@ -1,40 +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 uint64_t monotonic_cnt = 0; |
|
|
|
|
|
|
|
#define RBSIZE 512 |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
uint8_t buffer[128]; |
|
|
|
uint32_t size; |
|
|
|
uint32_t tme_rel; |
|
|
|
uint64_t tme_mon; |
|
|
|
} midimsg; |
|
|
|
|
|
|
|
static void |
|
|
|
describe (jack_midi_event_t* event, char* buffer, size_t buflen) |
|
|
|
describe (midimsg* event) |
|
|
|
{ |
|
|
|
assert (buflen > 0); |
|
|
|
|
|
|
|
buffer[0] = '\0'; |
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -44,35 +69,60 @@ 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); |
|
|
|
|
|
|
|
|
|
|
|
N = jack_midi_get_event_count (buffer); |
|
|
|
for (i = 0; i < N; ++i) { |
|
|
|
jack_midi_event_t event; |
|
|
|
int r; |
|
|
|
|
|
|
|
|
|
|
|
r = jack_midi_event_get (&event, buffer, i); |
|
|
|
if (r == 0) { |
|
|
|
size_t j; |
|
|
|
|
|
|
|
printf ("%d:", event.time); |
|
|
|
for (j = 0; j < event.size; ++j) { |
|
|
|
printf (" %x", event.buffer[j]); |
|
|
|
} |
|
|
|
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)); |
|
|
|
|
|
|
|
describe (&event, description, sizeof (description)); |
|
|
|
printf (" %s", description); |
|
|
|
|
|
|
|
printf ("\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
monotonic_cnt += frames; |
|
|
|
|
|
|
|
if (pthread_mutex_trylock (&msg_thread_lock) == 0) { |
|
|
|
pthread_cond_signal (&data_ready); |
|
|
|
pthread_mutex_unlock (&msg_thread_lock); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void wearedone(int sig) { |
|
|
|
keeprunning = 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void usage (int status) { |
|
|
|
printf ("jack_midi_dump - JACK MIDI Monitor.\n\n"); |
|
|
|
printf ("Usage: jack_midi_dump [ OPTIONS ] [CLIENT-NAME]\n\n"); |
|
|
|
printf ("Options:\n\ |
|
|
|
-a use absoute timestamps relative to application start\n\ |
|
|
|
-h display this help and exit\n\ |
|
|
|
-r use relative timestamps to previous MIDI event\n\ |
|
|
|
\n"); |
|
|
|
printf ("\n\ |
|
|
|
This tool listens for MIDI events on a JACK MIDI port and prints\n\ |
|
|
|
the message to stdout.\n\ |
|
|
|
\n\ |
|
|
|
If no client name is given it defaults to 'midi-monitor'.\n\ |
|
|
|
\n\ |
|
|
|
See also: jackd(1)\n\ |
|
|
|
\n"); |
|
|
|
exit (status); |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
main (int argc, char* argv[]) |
|
|
@@ -80,10 +130,20 @@ 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; |
|
|
|
|
|
|
|
if (argc == 2) { |
|
|
|
client_name = argv[1]; |
|
|
|
int cn = 1; |
|
|
|
|
|
|
|
if (argc > 1) { |
|
|
|
if (!strcmp (argv[1], "-a")) { time_format = 1; cn = 2; } |
|
|
|
else if (!strcmp (argv[1], "-r")) { time_format = 2; cn = 2; } |
|
|
|
else if (!strcmp (argv[1], "-h")) { usage (EXIT_SUCCESS); } |
|
|
|
else if (argv[1][0] == '-') { usage (EXIT_FAILURE); } |
|
|
|
} |
|
|
|
|
|
|
|
if (argc > cn) { |
|
|
|
client_name = argv[cn]; |
|
|
|
} else { |
|
|
|
client_name = default_name; |
|
|
|
} |
|
|
@@ -94,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); |
|
|
@@ -102,13 +164,61 @@ 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"); |
|
|
|
exit (EXIT_FAILURE); |
|
|
|
} |
|
|
|
|
|
|
|
sleep (-1); |
|
|
|
#ifndef WIN32 |
|
|
|
signal(SIGHUP, wearedone); |
|
|
|
signal(SIGINT, wearedone); |
|
|
|
#endif |
|
|
|
|
|
|
|
pthread_mutex_lock (&msg_thread_lock); |
|
|
|
|
|
|
|
uint64_t prev_event = 0; |
|
|
|
while (keeprunning) { |
|
|
|
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; |
|
|
|
} |