JACK tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
5.0KB

  1. // gcc -o jack_midi_dump -Wall midi_dump.c -ljack -pthread
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <assert.h>
  6. #include <jack/jack.h>
  7. #include <jack/midiport.h>
  8. #include <jack/ringbuffer.h>
  9. #ifndef WIN32
  10. #include <signal.h>
  11. #include <pthread.h>
  12. #include <sys/mman.h>
  13. #endif
  14. #ifndef MAX
  15. #define MAX(a,b) ( (a) < (b) ? (b) : (a) )
  16. #endif
  17. static jack_port_t* port;
  18. static jack_ringbuffer_t *rb = NULL;
  19. static pthread_mutex_t msg_thread_lock = PTHREAD_MUTEX_INITIALIZER;
  20. static pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
  21. static int keeprunning = 1;
  22. static uint64_t monotonic_cnt = 0;
  23. #define RBSIZE 512
  24. typedef struct {
  25. uint8_t buffer[128];
  26. uint32_t size;
  27. uint32_t tme_rel;
  28. uint64_t tme_mon;
  29. } midimsg;
  30. static void
  31. describe (midimsg* event)
  32. {
  33. if (event->size == 0) {
  34. return;
  35. }
  36. uint8_t type = event->buffer[0] & 0xf0;
  37. uint8_t channel = event->buffer[0] & 0xf;
  38. switch (type) {
  39. case 0x90:
  40. assert (event->size == 3);
  41. printf (" note on (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]);
  42. break;
  43. case 0x80:
  44. assert (event->size == 3);
  45. printf (" note off (channel %2d): pitch %3d, velocity %3d", channel, event->buffer[1], event->buffer[2]);
  46. break;
  47. case 0xb0:
  48. assert (event->size == 3);
  49. printf (" control change (channel %2d): controller %3d, value %3d", channel, event->buffer[1], event->buffer[2]);
  50. break;
  51. default:
  52. break;
  53. }
  54. }
  55. int
  56. process (jack_nframes_t frames, void* arg)
  57. {
  58. void* buffer;
  59. jack_nframes_t N;
  60. jack_nframes_t i;
  61. buffer = jack_port_get_buffer (port, frames);
  62. assert (buffer);
  63. N = jack_midi_get_event_count (buffer);
  64. for (i = 0; i < N; ++i) {
  65. jack_midi_event_t event;
  66. int r;
  67. r = jack_midi_event_get (&event, buffer, i);
  68. if (r == 0 && jack_ringbuffer_write_space (rb) >= sizeof(midimsg)) {
  69. midimsg m;
  70. m.tme_mon = monotonic_cnt;
  71. m.tme_rel = event.time;
  72. m.size = event.size;
  73. memcpy (m.buffer, event.buffer, MAX(sizeof(m.buffer), event.size));
  74. jack_ringbuffer_write (rb, (void *) &m, sizeof(midimsg));
  75. }
  76. }
  77. monotonic_cnt += frames;
  78. if (pthread_mutex_trylock (&msg_thread_lock) == 0) {
  79. pthread_cond_signal (&data_ready);
  80. pthread_mutex_unlock (&msg_thread_lock);
  81. }
  82. return 0;
  83. }
  84. static void wearedone(int sig) {
  85. keeprunning = 0;
  86. }
  87. static void usage (int status) {
  88. printf ("jack_midi_dump - JACK MIDI Monitor.\n\n");
  89. printf ("Usage: jack_midi_dump [ OPTIONS ] [CLIENT-NAME]\n\n");
  90. printf ("Options:\n\
  91. -a use absoute timestamps relative to application start\n\
  92. -h display this help and exit\n\
  93. -r use relative timestamps to previous MIDI event\n\
  94. \n");
  95. printf ("\n\
  96. This tool listens for MIDI events on a JACK MIDI port and prints\n\
  97. the message to stdout.\n\
  98. \n\
  99. If no client name is given it defaults to 'midi-monitor'.\n\
  100. \n\
  101. See also: jackd(1)\n\
  102. \n");
  103. exit (status);
  104. }
  105. int
  106. main (int argc, char* argv[])
  107. {
  108. jack_client_t* client;
  109. char const default_name[] = "midi-monitor";
  110. char const * client_name;
  111. int time_format = 0;
  112. int r;
  113. int cn = 1;
  114. if (argc > 1) {
  115. if (!strcmp (argv[1], "-a")) { time_format = 1; cn = 2; }
  116. else if (!strcmp (argv[1], "-r")) { time_format = 2; cn = 2; }
  117. else if (!strcmp (argv[1], "-h")) { usage (EXIT_SUCCESS); }
  118. else if (argv[1][0] == '-') { usage (EXIT_FAILURE); }
  119. }
  120. if (argc > cn) {
  121. client_name = argv[cn];
  122. } else {
  123. client_name = default_name;
  124. }
  125. client = jack_client_open (client_name, JackNullOption, NULL);
  126. if (client == NULL) {
  127. fprintf (stderr, "Could not create JACK client.\n");
  128. exit (EXIT_FAILURE);
  129. }
  130. rb = jack_ringbuffer_create (RBSIZE * sizeof(midimsg));
  131. jack_set_process_callback (client, process, 0);
  132. port = jack_port_register (client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
  133. if (port == NULL) {
  134. fprintf (stderr, "Could not register port.\n");
  135. exit (EXIT_FAILURE);
  136. }
  137. #ifndef WIN32
  138. if (mlockall (MCL_CURRENT | MCL_FUTURE)) {
  139. fprintf (stderr, "Warning: Can not lock memory.\n");
  140. }
  141. #endif
  142. r = jack_activate (client);
  143. if (r != 0) {
  144. fprintf (stderr, "Could not activate client.\n");
  145. exit (EXIT_FAILURE);
  146. }
  147. #ifndef WIN32
  148. signal(SIGHUP, wearedone);
  149. signal(SIGINT, wearedone);
  150. #endif
  151. pthread_mutex_lock (&msg_thread_lock);
  152. uint64_t prev_event = 0;
  153. while (keeprunning) {
  154. const int mqlen = jack_ringbuffer_read_space (rb) / sizeof(midimsg);
  155. int i;
  156. for (i=0; i < mqlen; ++i) {
  157. size_t j;
  158. midimsg m;
  159. jack_ringbuffer_read(rb, (char*) &m, sizeof(midimsg));
  160. switch(time_format) {
  161. case 1:
  162. printf ("%7"PRId64":", m.tme_rel + m.tme_mon);
  163. break;
  164. case 2:
  165. printf ("%+6"PRId64":", m.tme_rel + m.tme_mon - prev_event);
  166. break;
  167. default:
  168. printf ("%4d:", m.tme_rel);
  169. break;
  170. }
  171. for (j = 0; j < m.size && j < sizeof(m.buffer); ++j) {
  172. printf (" %02x", m.buffer[j]);
  173. }
  174. describe (&m);
  175. printf("\n");
  176. prev_event = m.tme_rel + m.tme_mon;
  177. }
  178. fflush (stdout);
  179. pthread_cond_wait (&data_ready, &msg_thread_lock);
  180. }
  181. pthread_mutex_unlock (&msg_thread_lock);
  182. jack_deactivate (client);
  183. jack_client_close (client);
  184. jack_ringbuffer_free (rb);
  185. return 0;
  186. }