FIx doxygen and user facing and non-facing typos
[jack2.git] / example-clients / midi_dump.c
blob7a7ec2b3489a02a72893e2342e39db9edab57caa
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;
117 static void usage (int status) {
118 printf ("jack_midi_dump - JACK MIDI Monitor.\n\n");
119 printf ("Usage: jack_midi_dump [ OPTIONS ] [CLIENT-NAME]\n\n");
120 printf ("Options:\n\
121 -a use absolute timestamps relative to application start\n\
122 -h display this help and exit\n\
123 -r use relative timestamps to previous MIDI event\n\
124 \n");
125 printf ("\n\
126 This tool listens for MIDI events on a JACK MIDI port and prints\n\
127 the message to stdout.\n\
129 If no client name is given it defaults to 'midi-monitor'.\n\
131 See also: jackd(1)\n\
132 \n");
133 exit (status);
137 main (int argc, char* argv[])
139 jack_client_t* client;
140 char const default_name[] = "midi-monitor";
141 char const * client_name;
142 int time_format = 0;
143 int r;
145 int cn = 1;
147 if (argc > 1) {
148 if (!strcmp (argv[1], "-a")) { time_format = 1; cn = 2; }
149 else if (!strcmp (argv[1], "-r")) { time_format = 2; cn = 2; }
150 else if (!strcmp (argv[1], "-h")) { usage (EXIT_SUCCESS); }
151 else if (argv[1][0] == '-') { usage (EXIT_FAILURE); }
154 if (argc > cn) {
155 client_name = argv[cn];
156 } else {
157 client_name = default_name;
160 client = jack_client_open (client_name, JackNullOption, NULL);
161 if (client == NULL) {
162 fprintf (stderr, "Could not create JACK client.\n");
163 exit (EXIT_FAILURE);
166 rb = jack_ringbuffer_create (RBSIZE * sizeof(midimsg));
168 jack_set_process_callback (client, process, 0);
170 port = jack_port_register (client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
171 if (port == NULL) {
172 fprintf (stderr, "Could not register port.\n");
173 exit (EXIT_FAILURE);
176 #ifndef WIN32
177 if (mlockall (MCL_CURRENT | MCL_FUTURE)) {
178 fprintf (stderr, "Warning: Can not lock memory.\n");
180 #endif
182 r = jack_activate (client);
183 if (r != 0) {
184 fprintf (stderr, "Could not activate client.\n");
185 exit (EXIT_FAILURE);
188 #ifndef WIN32
189 signal(SIGHUP, wearedone);
190 signal(SIGINT, wearedone);
191 #endif
193 pthread_mutex_lock (&msg_thread_lock);
195 uint64_t prev_event = 0;
196 while (keeprunning) {
197 const int mqlen = jack_ringbuffer_read_space (rb) / sizeof(midimsg);
198 int i;
199 for (i=0; i < mqlen; ++i) {
200 size_t j;
201 midimsg m;
202 jack_ringbuffer_read(rb, (char*) &m, sizeof(midimsg));
204 switch(time_format) {
205 case 1:
206 printf ("%7"PRId64":", m.tme_rel + m.tme_mon);
207 break;
208 case 2:
209 printf ("%+6"PRId64":", m.tme_rel + m.tme_mon - prev_event);
210 break;
211 default:
212 printf ("%4d:", m.tme_rel);
213 break;
215 for (j = 0; j < m.size && j < sizeof(m.buffer); ++j) {
216 printf (" %02x", m.buffer[j]);
219 describe (&m);
220 printf("\n");
221 prev_event = m.tme_rel + m.tme_mon;
223 fflush (stdout);
224 pthread_cond_wait (&data_ready, &msg_thread_lock);
226 pthread_mutex_unlock (&msg_thread_lock);
228 jack_deactivate (client);
229 jack_client_close (client);
230 jack_ringbuffer_free (rb);
232 return 0;