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.

643 lines
20KB

  1. /*
  2. Copyright (C) 2010 Devin Anderson
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2.1 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. /*
  16. * This program is used to measure MIDI latency and jitter. It writes MIDI
  17. * messages to one port and calculates how long it takes before it reads the
  18. * same MIDI message over another port. It was written to calculate the
  19. * latency and jitter of hardware and JACK hardware drivers, but might have
  20. * other practical applications.
  21. *
  22. * The latency results of the program include the latency introduced by the
  23. * JACK system. Because JACK has sample accurate MIDI, the same latency
  24. * imposed on audio is also imposed on MIDI going through the system. Make
  25. * sure you take this into account before complaining to me or (*especially*)
  26. * other JACK developers about reported MIDI latency.
  27. *
  28. * The jitter results are a little more interesting. The program attempts to
  29. * calculate 'average jitter' and 'peak jitter', as defined here:
  30. *
  31. * http://openmuse.org/transport/fidelity.html
  32. *
  33. * It also outputs a jitter plot, which gives you a more specific idea about
  34. * the MIDI jitter for the ports you're testing. This is useful for catching
  35. * extreme jitter values, and for analyzing the amount of truth in the
  36. * technical specifications for your MIDI interface(s). :)
  37. *
  38. * This program is loosely based on 'alsa-midi-latency-test' in the ALSA test
  39. * suite.
  40. *
  41. * To port this program to non-POSIX platforms, you'll have to include
  42. * implementations for mutexes, semaphores, and command-line argument handling.
  43. */
  44. #include <errno.h>
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <string.h>
  48. #include <getopt.h>
  49. #include <pthread.h>
  50. #include <semaphore.h>
  51. #include <jack/jack.h>
  52. #include <jack/midiport.h>
  53. #define ABS(x) (((x) >= 0) ? (x) : (-(x)))
  54. const char *ERROR_UNEXPECTED = "in port received unexpected MIDI message";
  55. const char *ERROR_UNEXPECTED_EXTRA = "received more than one MIDI message";
  56. const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer";
  57. const char *ERROR_TIMEOUT = "timed out while waiting for MIDI message";
  58. const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve";
  59. const char *SOURCE_PROCESS = "handle_process";
  60. const char *SOURCE_TRYLOCK = "pthread_mutex_trylock";
  61. const char *SOURCE_UNLOCK = "pthread_mutex_unlock";
  62. jack_client_t *client;
  63. const char *error_message;
  64. const char *error_source;
  65. jack_nframes_t highest_latency;
  66. jack_time_t highest_latency_time;
  67. jack_latency_range_t in_latency_range;
  68. jack_port_t *in_port;
  69. jack_nframes_t last_activity;
  70. jack_time_t last_activity_time;
  71. jack_time_t *latency_time_values;
  72. jack_nframes_t *latency_values;
  73. jack_nframes_t lowest_latency;
  74. jack_time_t lowest_latency_time;
  75. jack_midi_data_t *message_1;
  76. jack_midi_data_t *message_2;
  77. size_t messages_received;
  78. size_t messages_sent;
  79. size_t message_size;
  80. jack_latency_range_t out_latency_range;
  81. jack_port_t *out_port;
  82. int process_state;
  83. char *program_name;
  84. jack_port_t *remote_in_port;
  85. jack_port_t *remote_out_port;
  86. size_t samples;
  87. #ifdef __APPLE__
  88. sem_t* semaphore;
  89. #else
  90. sem_t semaphore;
  91. #endif
  92. pthread_mutex_t start_mutex;
  93. int timeout;
  94. jack_nframes_t total_latency;
  95. jack_time_t total_latency_time;
  96. size_t unexpected_messages;
  97. size_t xrun_count;
  98. static void
  99. output_error(const char *source, const char *message);
  100. static void
  101. output_usage();
  102. static void
  103. set_process_error(const char *source, const char *message);
  104. static void
  105. die(char *source, char *error_message)
  106. {
  107. output_error(source, error_message);
  108. output_usage();
  109. exit(EXIT_FAILURE);
  110. }
  111. static void
  112. handle_info(const char *message)
  113. {
  114. /* Suppress info */
  115. }
  116. static int
  117. handle_process(jack_nframes_t frames, void *arg)
  118. {
  119. jack_midi_data_t *buffer;
  120. int code;
  121. jack_midi_event_t event;
  122. jack_nframes_t event_count;
  123. jack_nframes_t event_time;
  124. jack_nframes_t frame;
  125. size_t i;
  126. jack_nframes_t last_frame_time;
  127. jack_midi_data_t *message;
  128. void *port_buffer;
  129. jack_time_t time;
  130. switch (process_state) {
  131. case 0:
  132. /* State: initializing */
  133. code = pthread_mutex_trylock(&start_mutex);
  134. if (code) {
  135. if (code != EBUSY) {
  136. set_process_error(SOURCE_TRYLOCK, strerror(code));
  137. }
  138. break;
  139. }
  140. code = pthread_mutex_unlock(&start_mutex);
  141. if (code) {
  142. set_process_error(SOURCE_UNLOCK, strerror(code));
  143. break;
  144. }
  145. highest_latency = 0;
  146. lowest_latency = 0;
  147. messages_received = 0;
  148. messages_sent = 0;
  149. process_state = 1;
  150. total_latency = 0;
  151. total_latency_time = 0;
  152. unexpected_messages = 0;
  153. xrun_count = 0;
  154. jack_port_get_latency_range(remote_in_port, JackCaptureLatency,
  155. &in_latency_range);
  156. jack_port_get_latency_range(remote_out_port, JackPlaybackLatency,
  157. &out_latency_range);
  158. goto send_message;
  159. case 1:
  160. /* State: processing */
  161. jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames));
  162. port_buffer = jack_port_get_buffer(in_port, frames);
  163. event_count = jack_midi_get_event_count(port_buffer);
  164. last_frame_time = jack_last_frame_time(client);
  165. for (i = 0; i < event_count; i++) {
  166. jack_midi_event_get(&event, port_buffer, i);
  167. message = (messages_received % 2) ? message_2 : message_1;
  168. if ((event.size == message_size) &&
  169. (! memcmp(message, event.buffer,
  170. message_size * sizeof(jack_midi_data_t)))) {
  171. goto found_message;
  172. }
  173. unexpected_messages++;
  174. }
  175. jack_time_t microseconds =
  176. jack_frames_to_time(client, last_frame_time) - last_activity_time;
  177. if ((microseconds / 1000000) >= timeout) {
  178. set_process_error(SOURCE_PROCESS, ERROR_TIMEOUT);
  179. }
  180. break;
  181. found_message:
  182. event_time = last_frame_time + event.time;
  183. frame = event_time - last_activity;
  184. time = jack_frames_to_time(client, event_time) - last_activity_time;
  185. if ((! highest_latency) || (frame > highest_latency)) {
  186. highest_latency = frame;
  187. highest_latency_time = time;
  188. }
  189. if ((! lowest_latency) || (frame < lowest_latency)) {
  190. lowest_latency = frame;
  191. lowest_latency_time = time;
  192. }
  193. latency_time_values[messages_received] = time;
  194. latency_values[messages_received] = frame;
  195. total_latency += frame;
  196. total_latency_time += time;
  197. messages_received++;
  198. if (messages_received == samples) {
  199. process_state = 2;
  200. #ifdef __APPLE__
  201. sem_post(semaphore);
  202. #else
  203. sem_post(&semaphore);
  204. #endif
  205. break;
  206. }
  207. send_message:
  208. frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames);
  209. if (frame >= frames) {
  210. frame = frames - 1;
  211. }
  212. port_buffer = jack_port_get_buffer(out_port, frames);
  213. jack_midi_clear_buffer(port_buffer);
  214. buffer = jack_midi_event_reserve(port_buffer, frame, message_size);
  215. if (buffer == NULL) {
  216. set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE);
  217. break;
  218. }
  219. message = (messages_sent % 2) ? message_2 : message_1;
  220. memcpy(buffer, message, message_size * sizeof(jack_midi_data_t));
  221. last_activity = jack_last_frame_time(client) + frame;
  222. last_activity_time = jack_frames_to_time(client, last_activity);
  223. messages_sent++;
  224. case 2:
  225. /* State: finished - do nothing */
  226. case -1:
  227. /* State: error - do nothing */
  228. ;
  229. }
  230. return 0;
  231. }
  232. static void
  233. handle_shutdown(void *arg)
  234. {
  235. set_process_error("handle_shutdown", "The JACK server has been shutdown");
  236. }
  237. static int
  238. handle_xrun(void *arg)
  239. {
  240. xrun_count++;
  241. return 0;
  242. }
  243. static void
  244. output_error(const char *source, const char *message)
  245. {
  246. fprintf(stderr, "%s: %s: %s\n", program_name, source, message);
  247. }
  248. static void
  249. output_usage()
  250. {
  251. fprintf(stderr, "Usage: %s [options] out-port-name in-port-name\n\n"
  252. "\t-h, --help print program usage\n"
  253. "\t-m, --message-size=size set size of MIDI messages to send\n"
  254. "\t-s, --samples=n number of MIDI messages to send\n"
  255. "\t-t, --timeout=seconds wait time before giving up on message\n"
  256. "\n", program_name);
  257. }
  258. static unsigned long
  259. parse_positive_number_arg(char *s, char *name)
  260. {
  261. char *end_ptr;
  262. unsigned long result;
  263. errno = 0;
  264. result = strtoul(s, &end_ptr, 10);
  265. if (errno) {
  266. die(name, strerror(errno));
  267. }
  268. if (*s == '\0') {
  269. die(name, "argument value cannot be empty");
  270. }
  271. if (*end_ptr != '\0') {
  272. die(name, "invalid value");
  273. }
  274. if (! result) {
  275. die(name, "must be a positive number");
  276. }
  277. return result;
  278. }
  279. static void
  280. set_process_error(const char *source, const char *message)
  281. {
  282. error_source = source;
  283. error_message = message;
  284. process_state = -1;
  285. #ifdef __APPLE__
  286. sem_post(semaphore);
  287. #else
  288. sem_post(&semaphore);
  289. #endif
  290. }
  291. int
  292. main(int argc, char **argv)
  293. {
  294. int code;
  295. size_t jitter_plot[101];
  296. int long_index = 0;
  297. struct option long_options[] = {
  298. {"help", 0, NULL, 'h'},
  299. {"message-size", 1, NULL, 'm'},
  300. {"samples", 1, NULL, 's'},
  301. {"timeout", 1, NULL, 't'}
  302. };
  303. char *option_string = "hm:s:t:";
  304. int show_usage = 0;
  305. error_message = NULL;
  306. message_size = 3;
  307. program_name = argv[0];
  308. samples = 1024;
  309. timeout = 5;
  310. for (;;) {
  311. char c = getopt_long(argc, argv, option_string, long_options,
  312. &long_index);
  313. switch (c) {
  314. case 'h':
  315. show_usage = 1;
  316. break;
  317. case 'm':
  318. message_size = parse_positive_number_arg(optarg, "message-size");
  319. break;
  320. case 's':
  321. samples = parse_positive_number_arg(optarg, "samples");
  322. break;
  323. case 't':
  324. timeout = parse_positive_number_arg(optarg, "timeout");
  325. break;
  326. default:
  327. {
  328. char *s = "'- '";
  329. s[2] = c;
  330. die(s, "invalid switch");
  331. }
  332. case -1:
  333. if (show_usage) {
  334. output_usage();
  335. exit(EXIT_SUCCESS);
  336. }
  337. goto parse_port_names;
  338. case 1:
  339. /* end of switch :) */
  340. ;
  341. }
  342. }
  343. parse_port_names:
  344. if ((argc - optind) != 2) {
  345. output_usage();
  346. return EXIT_FAILURE;
  347. }
  348. latency_values = malloc(sizeof(jack_nframes_t) * samples);
  349. if (latency_values == NULL) {
  350. error_message = strerror(errno);
  351. error_source = "malloc";
  352. goto show_error;
  353. }
  354. latency_time_values = malloc(sizeof(jack_time_t) * samples);
  355. if (latency_time_values == NULL) {
  356. error_message = strerror(errno);
  357. error_source = "malloc";
  358. goto free_latency_values;
  359. }
  360. message_1 = malloc(message_size * sizeof(jack_midi_data_t));
  361. if (message_1 == NULL) {
  362. error_message = strerror(errno);
  363. error_source = "malloc";
  364. goto free_latency_time_values;
  365. }
  366. message_2 = malloc(message_size * sizeof(jack_midi_data_t));
  367. if (message_2 == NULL) {
  368. error_message = strerror(errno);
  369. error_source = "malloc";
  370. goto free_message_1;
  371. }
  372. switch (message_size) {
  373. case 1:
  374. message_1[0] = 0xf6;
  375. message_2[0] = 0xfe;
  376. break;
  377. case 2:
  378. message_1[0] = 0xc0;
  379. message_1[1] = 0x00;
  380. message_2[0] = 0xd0;
  381. message_2[1] = 0x7f;
  382. break;
  383. case 3:
  384. message_1[0] = 0x80;
  385. message_1[1] = 0x00;
  386. message_1[2] = 0x00;
  387. message_2[0] = 0x90;
  388. message_2[1] = 0x7f;
  389. message_2[2] = 0x7f;
  390. break;
  391. default:
  392. message_1[0] = 0xf0;
  393. memset(message_1 + 1, 0,
  394. (message_size - 2) * sizeof(jack_midi_data_t));
  395. message_1[message_size - 1] = 0xf7;
  396. message_2[0] = 0xf0;
  397. memset(message_2 + 1, 0x7f,
  398. (message_size - 2) * sizeof(jack_midi_data_t));
  399. message_2[message_size - 1] = 0xf7;
  400. }
  401. client = jack_client_open(program_name, JackNullOption, NULL);
  402. if (client == NULL) {
  403. error_message = "failed to open JACK client";
  404. error_source = "jack_client_open";
  405. goto free_message_2;
  406. }
  407. remote_in_port = jack_port_by_name(client, argv[optind + 1]);
  408. if (remote_in_port == NULL) {
  409. error_message = "invalid port name";
  410. error_source = argv[optind + 1];
  411. goto close_client;
  412. }
  413. remote_out_port = jack_port_by_name(client, argv[optind]);
  414. if (remote_out_port == NULL) {
  415. error_message = "invalid port name";
  416. error_source = argv[optind];
  417. goto close_client;
  418. }
  419. in_port = jack_port_register(client, "in", JACK_DEFAULT_MIDI_TYPE,
  420. JackPortIsInput, 0);
  421. if (in_port == NULL) {
  422. error_message = "failed to register MIDI-in port";
  423. error_source = "jack_port_register";
  424. goto close_client;
  425. }
  426. out_port = jack_port_register(client, "out", JACK_DEFAULT_MIDI_TYPE,
  427. JackPortIsOutput, 0);
  428. if (out_port == NULL) {
  429. error_message = "failed to register MIDI-out port";
  430. error_source = "jack_port_register";
  431. goto unregister_in_port;
  432. }
  433. if (jack_set_process_callback(client, handle_process, NULL)) {
  434. error_message = "failed to set process callback";
  435. error_source = "jack_set_process_callback";
  436. goto unregister_out_port;
  437. }
  438. if (jack_set_xrun_callback(client, handle_xrun, NULL)) {
  439. error_message = "failed to set xrun callback";
  440. error_source = "jack_set_xrun_callback";
  441. goto unregister_out_port;
  442. }
  443. jack_on_shutdown(client, handle_shutdown, NULL);
  444. jack_set_info_function(handle_info);
  445. process_state = 0;
  446. #ifdef __APPLE__
  447. // sem_init is not implemented on OSX
  448. char name[128];
  449. sprintf(name, "midi_sem_%p", client);
  450. if ((semaphore = sem_open(name, O_CREAT, 0777, 0)) == (sem_t*)SEM_FAILED) {
  451. error_message = strerror(errno);
  452. error_source = "sem_open";
  453. goto unregister_out_port;
  454. }
  455. #else
  456. if (sem_init(&semaphore, 0, 0)) {
  457. error_message = strerror(errno);
  458. error_source = "sem_init";
  459. goto unregister_out_port;
  460. }
  461. #endif
  462. code = pthread_mutex_init(&start_mutex, NULL);
  463. if (code) {
  464. error_message = strerror(errno);
  465. error_source = "pthread_mutex_init";
  466. goto destroy_semaphore;
  467. }
  468. code = pthread_mutex_trylock(&start_mutex);
  469. if (code) {
  470. error_message = strerror(code);
  471. error_source = "pthread_mutex_trylock";
  472. goto destroy_mutex;
  473. }
  474. if (jack_activate(client)) {
  475. error_message = "could not activate client";
  476. error_source = "jack_activate";
  477. goto destroy_mutex;
  478. }
  479. if (jack_connect(client, jack_port_name(out_port),
  480. jack_port_name(remote_out_port))) {
  481. error_message = "could not connect MIDI out port";
  482. error_source = "jack_connect";
  483. goto deactivate_client;
  484. }
  485. if (jack_connect(client, jack_port_name(remote_in_port),
  486. jack_port_name(in_port))) {
  487. error_message = "could not connect MIDI in port";
  488. error_source = "jack_connect";
  489. goto deactivate_client;
  490. }
  491. code = pthread_mutex_unlock(&start_mutex);
  492. if (code) {
  493. error_message = strerror(code);
  494. error_source = "pthread_mutex_unlock";
  495. goto deactivate_client;
  496. }
  497. #ifdef __APPLE__
  498. while (sem_wait(semaphore) != 0) {
  499. #else
  500. while (sem_wait(&semaphore) != 0) {
  501. #endif
  502. if (errno != EINTR) {
  503. error_message = strerror(errno);
  504. error_source = "sem_wait";
  505. goto deactivate_client;
  506. }
  507. }
  508. if (process_state == 2) {
  509. double average_latency = ((double) total_latency) / samples;
  510. double average_latency_time = total_latency_time / samples;
  511. size_t i;
  512. double sample_rate = (double) jack_get_sample_rate(client);
  513. jack_nframes_t total_jitter = 0;
  514. jack_time_t total_jitter_time = 0;
  515. for (i = 0; i <= 100; i++) {
  516. jitter_plot[i] = 0;
  517. }
  518. for (i = 0; i < samples; i++) {
  519. double jitter_time = ABS(average_latency_time -
  520. ((double) latency_time_values[i]));
  521. if (jitter_time >= 10000.0) {
  522. (jitter_plot[100])++;
  523. } else {
  524. (jitter_plot[(int) (jitter_time / 100.0)])++;
  525. }
  526. total_jitter += ABS(average_latency -
  527. ((double) latency_values[i]));
  528. total_jitter_time += jitter_time;
  529. }
  530. printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n"
  531. "Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n"
  532. "Average latency: %.2f ms (%.2f frames)\n"
  533. "Best latency: %.2f ms (%u frames)\n"
  534. "Worst latency: %.2f ms (%u frames)\n"
  535. "Peak MIDI jitter: %.2f ms (%u frames)\n"
  536. "Average MIDI jitter: %.2f ms (%.2f frames)\n",
  537. (out_latency_range.min / sample_rate) * 1000.0,
  538. (out_latency_range.max / sample_rate) * 1000.0,
  539. out_latency_range.min, out_latency_range.max,
  540. (in_latency_range.min / sample_rate) * 1000.0,
  541. (in_latency_range.max / sample_rate) * 1000.0,
  542. in_latency_range.min, in_latency_range.max,
  543. average_latency_time / 1000.0, average_latency,
  544. lowest_latency_time / 1000.0, lowest_latency,
  545. highest_latency_time / 1000.0, highest_latency,
  546. (highest_latency_time - lowest_latency_time) / 1000.0,
  547. highest_latency - lowest_latency,
  548. (total_jitter_time / 1000.0) / samples,
  549. ((double) total_jitter) / samples);
  550. printf("\nJitter Plot:\n");
  551. for (i = 0; i < 100; i++) {
  552. if (jitter_plot[i]) {
  553. printf("%.1f - %.1f ms: %u\n", ((float) i) / 10.0,
  554. ((float) (i + 1)) / 10.0, jitter_plot[i]);
  555. }
  556. }
  557. if (jitter_plot[100]) {
  558. printf(" > 10 ms: %u\n", jitter_plot[100]);
  559. }
  560. }
  561. printf("\nMessages sent: %d\n"
  562. "Messages received: %d\n",
  563. messages_sent, messages_received);
  564. if (unexpected_messages) {
  565. printf("Unexpected messages received: %d\n", unexpected_messages);
  566. }
  567. if (xrun_count) {
  568. printf("Xruns: %d (messages may have been lost)\n", xrun_count);
  569. }
  570. deactivate_client:
  571. jack_deactivate(client);
  572. destroy_mutex:
  573. pthread_mutex_destroy(&start_mutex);
  574. destroy_semaphore:
  575. #ifdef __APPLE__
  576. sem_destroy(semaphore);
  577. sem_unlink(name);
  578. #else
  579. sem_destroy(&semaphore);
  580. #endif
  581. unregister_out_port:
  582. jack_port_unregister(client, out_port);
  583. unregister_in_port:
  584. jack_port_unregister(client, in_port);
  585. close_client:
  586. jack_client_close(client);
  587. free_message_2:
  588. free(message_2);
  589. free_message_1:
  590. free(message_1);
  591. free_latency_time_values:
  592. free(latency_time_values);
  593. free_latency_values:
  594. free(latency_values);
  595. if (error_message != NULL) {
  596. show_error:
  597. output_error(error_source, error_message);
  598. exit(EXIT_FAILURE);
  599. }
  600. return EXIT_SUCCESS;
  601. }