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.

239 lines
5.6KB

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