jack2 codebase
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.

230 lines
5.1KB

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