LADI JACK2: Switch to 2.minor versioning scheme
[jackdbus.git] / tools / midi_dump.c
blobef9148b348f61549c2809df4de85e138d9ef95a5
1 // gcc -o jack_midi_dump -Wall midi_dump.c -ljack -pthread
3 #include <stdio.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <assert.h>
7 #include <inttypes.h>
8 #include <jack/jack.h>
9 #include <jack/midiport.h>
10 #include <jack/ringbuffer.h>
12 #ifdef __MINGW32__
13 #include <pthread.h>
14 #endif
16 #ifndef WIN32
17 #include <signal.h>
18 #include <pthread.h>
19 #include <sys/mman.h>
20 #endif
22 static jack_port_t* port;
23 static jack_ringbuffer_t *rb = NULL;
24 static pthread_mutex_t msg_thread_lock = PTHREAD_MUTEX_INITIALIZER;
25 static pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
27 static int keeprunning = 1;
28 static uint64_t monotonic_cnt = 0;
30 #define RBSIZE 100
31 #define MSG_BUFFER_SIZE 4096
33 typedef struct {
34 uint8_t buffer[MSG_BUFFER_SIZE];
35 uint32_t size;
36 uint32_t tme_rel;
37 uint64_t tme_mon;
38 } midimsg;
40 static void
41 describe (midimsg* event)
43 if (event->size == 0) {
44 return;
47 uint8_t type = event->buffer[0] & 0xf0;
48 uint8_t channel = event->buffer[0] & 0xf;
50 switch (type) {
51 case 0x90:
52 assert (event->size == 3);
53 printf (" note on (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]);
54 break;
55 case 0x80:
56 assert (event->size == 3);
57 printf (" note off (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]);
58 break;
59 case 0xb0:
60 assert (event->size == 3);
61 printf (" control change (channel %2d): controller %3d, value %3d", channel, event->buffer[1], event->buffer[2]);
62 break;
63 default:
64 break;
68 int
69 process (jack_nframes_t frames, void* arg)
71 void* buffer;
72 jack_nframes_t N;
73 jack_nframes_t i;
75 buffer = jack_port_get_buffer (port, frames);
76 assert (buffer);
78 N = jack_midi_get_event_count (buffer);
79 for (i = 0; i < N; ++i) {
80 jack_midi_event_t event;
81 int r;
82 r = jack_midi_event_get (&event, buffer, i);
84 if (r != 0) {continue;}
86 if (event.size > MSG_BUFFER_SIZE) {
87 fprintf(stderr, "Error: MIDI message was too large, skipping event. Max. allowed size: %d bytes\n", MSG_BUFFER_SIZE);
89 else if (jack_ringbuffer_write_space (rb) >= sizeof(midimsg)) {
90 midimsg m;
91 m.tme_mon = monotonic_cnt;
92 m.tme_rel = event.time;
93 m.size = event.size;
94 memcpy (m.buffer, event.buffer, event.size);
95 jack_ringbuffer_write (rb, (void *) &m, sizeof(midimsg));
97 else {
98 fprintf (stderr, "Error: ringbuffer was full, skipping event.\n");
102 monotonic_cnt += frames;
104 if (pthread_mutex_trylock (&msg_thread_lock) == 0) {
105 pthread_cond_signal (&data_ready);
106 pthread_mutex_unlock (&msg_thread_lock);
109 return 0;
112 static void wearedone(int sig) {
113 fprintf(stderr, "Shutting down\n");
114 keeprunning = 0;
115 /* main loop might be blocked by data_ready when jack server dies. */
116 if (pthread_mutex_trylock (&msg_thread_lock) == 0) {
117 pthread_cond_signal (&data_ready);
118 pthread_mutex_unlock (&msg_thread_lock);
122 static void usage (int status) {
123 printf ("jack_midi_dump - JACK MIDI Monitor.\n\n");
124 printf ("Usage: jack_midi_dump [ OPTIONS ] [CLIENT-NAME]\n\n");
125 printf ("Options:\n\
126 -a use absolute timestamps relative to application start\n\
127 -h display this help and exit\n\
128 -r use relative timestamps to previous MIDI event\n\
129 \n");
130 printf ("\n\
131 This tool listens for MIDI events on a JACK MIDI port and prints\n\
132 the message to stdout.\n\
134 If no client name is given it defaults to 'midi-monitor'.\n\
136 See also: jackd(1)\n\
137 \n");
138 exit (status);
142 main (int argc, char* argv[])
144 jack_client_t* client;
145 char const default_name[] = "midi-monitor";
146 char const * client_name;
147 int time_format = 0;
148 int r;
150 int cn = 1;
152 if (argc > 1) {
153 if (!strcmp (argv[1], "-a")) { time_format = 1; cn = 2; }
154 else if (!strcmp (argv[1], "-r")) { time_format = 2; cn = 2; }
155 else if (!strcmp (argv[1], "-h")) { usage (EXIT_SUCCESS); }
156 else if (argv[1][0] == '-') { usage (EXIT_FAILURE); }
159 if (argc > cn) {
160 client_name = argv[cn];
161 } else {
162 client_name = default_name;
165 client = jack_client_open (client_name, JackNullOption, NULL);
166 if (client == NULL) {
167 fprintf (stderr, "Could not create JACK client.\n");
168 exit (EXIT_FAILURE);
171 rb = jack_ringbuffer_create (RBSIZE * sizeof(midimsg));
173 jack_set_process_callback (client, process, 0);
175 port = jack_port_register (client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
176 if (port == NULL) {
177 fprintf (stderr, "Could not register port.\n");
178 exit (EXIT_FAILURE);
181 #ifndef WIN32
182 if (mlockall (MCL_CURRENT | MCL_FUTURE)) {
183 fprintf (stderr, "Warning: Can not lock memory.\n");
185 #endif
187 r = jack_activate (client);
188 if (r != 0) {
189 fprintf (stderr, "Could not activate client.\n");
190 exit (EXIT_FAILURE);
193 #ifndef WIN32
194 signal(SIGHUP, wearedone);
195 signal(SIGINT, wearedone);
196 #endif
198 pthread_mutex_lock (&msg_thread_lock);
200 uint64_t prev_event = 0;
201 while (keeprunning) {
202 const int mqlen = jack_ringbuffer_read_space (rb) / sizeof(midimsg);
203 int i;
204 for (i=0; i < mqlen; ++i) {
205 size_t j;
206 midimsg m;
207 jack_ringbuffer_read(rb, (char*) &m, sizeof(midimsg));
209 switch(time_format) {
210 case 1:
211 printf ("%7"PRId64":", m.tme_rel + m.tme_mon);
212 break;
213 case 2:
214 printf ("%+6"PRId64":", m.tme_rel + m.tme_mon - prev_event);
215 break;
216 default:
217 printf ("%4d:", m.tme_rel);
218 break;
220 for (j = 0; j < m.size && j < sizeof(m.buffer); ++j) {
221 printf (" %02x", m.buffer[j]);
224 describe (&m);
225 printf("\n");
226 prev_event = m.tme_rel + m.tme_mon;
228 fflush (stdout);
229 pthread_cond_wait (&data_ready, &msg_thread_lock);
231 pthread_mutex_unlock (&msg_thread_lock);
233 jack_deactivate (client);
234 jack_client_close (client);
235 jack_ringbuffer_free (rb);
237 return 0;