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.

970 lines
29KB

  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 semaphores and command-line argument handling.
  43. */
  44. #include <assert.h>
  45. #include <errno.h>
  46. #include <math.h>
  47. #include <signal.h>
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <string.h>
  51. #include <getopt.h>
  52. #include <jack/jack.h>
  53. #include <jack/midiport.h>
  54. #ifdef WIN32
  55. #include <windows.h>
  56. #include <unistd.h>
  57. #else
  58. #include <semaphore.h>
  59. #endif
  60. #define ABS(x) (((x) >= 0) ? (x) : (-(x)))
  61. #ifdef WIN32
  62. typedef HANDLE semaphore_t;
  63. #else
  64. typedef sem_t *semaphore_t;
  65. #endif
  66. const char *ERROR_MSG_TIMEOUT = "timed out while waiting for MIDI message";
  67. const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer";
  68. const char *ERROR_SHUTDOWN = "the JACK server has been shutdown";
  69. const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve";
  70. const char *SOURCE_PROCESS = "handle_process";
  71. const char *SOURCE_SHUTDOWN = "handle_shutdown";
  72. const char *SOURCE_SIGNAL_SEMAPHORE = "signal_semaphore";
  73. const char *SOURCE_WAIT_SEMAPHORE = "wait_semaphore";
  74. char *alias1;
  75. char *alias2;
  76. jack_client_t *client;
  77. semaphore_t connect_semaphore;
  78. volatile int connections_established;
  79. const char *error_message;
  80. const char *error_source;
  81. jack_nframes_t highest_latency;
  82. jack_time_t highest_latency_time;
  83. jack_latency_range_t in_latency_range;
  84. jack_port_t *in_port;
  85. semaphore_t init_semaphore;
  86. jack_nframes_t last_activity;
  87. jack_time_t last_activity_time;
  88. jack_time_t *latency_time_values;
  89. jack_nframes_t *latency_values;
  90. jack_nframes_t lowest_latency;
  91. jack_time_t lowest_latency_time;
  92. jack_midi_data_t *message_1;
  93. jack_midi_data_t *message_2;
  94. int messages_received;
  95. int messages_sent;
  96. size_t message_size;
  97. jack_latency_range_t out_latency_range;
  98. jack_port_t *out_port;
  99. semaphore_t process_semaphore;
  100. volatile sig_atomic_t process_state;
  101. char *program_name;
  102. jack_port_t *remote_in_port;
  103. jack_port_t *remote_out_port;
  104. size_t samples;
  105. const char *target_in_port_name;
  106. const char *target_out_port_name;
  107. int timeout;
  108. jack_nframes_t total_latency;
  109. jack_time_t total_latency_time;
  110. int unexpected_messages;
  111. int xrun_count;
  112. #ifdef WIN32
  113. char semaphore_error_msg[1024];
  114. #endif
  115. static void
  116. output_error(const char *source, const char *message);
  117. static void
  118. output_usage(void);
  119. static void
  120. set_process_error(const char *source, const char *message);
  121. static int
  122. signal_semaphore(semaphore_t semaphore);
  123. static jack_port_t *
  124. update_connection(jack_port_t *remote_port, int connected,
  125. jack_port_t *local_port, jack_port_t *current_port,
  126. const char *target_name);
  127. static int
  128. wait_semaphore(semaphore_t semaphore, int block);
  129. static semaphore_t
  130. create_semaphore(int id)
  131. {
  132. semaphore_t semaphore;
  133. #ifdef WIN32
  134. semaphore = CreateSemaphore(NULL, 0, 2, NULL);
  135. #elif defined (__APPLE__)
  136. char name[128];
  137. sprintf(name, "midi_sem_%d", id);
  138. semaphore = sem_open(name, O_CREAT, 0777, 0);
  139. if (semaphore == (sem_t *) SEM_FAILED) {
  140. semaphore = NULL;
  141. }
  142. #else
  143. semaphore = malloc(sizeof(semaphore_t));
  144. if (semaphore != NULL) {
  145. if (sem_init(semaphore, 0, 0)) {
  146. free(semaphore);
  147. semaphore = NULL;
  148. }
  149. }
  150. #endif
  151. return semaphore;
  152. }
  153. static void
  154. destroy_semaphore(semaphore_t semaphore, int id)
  155. {
  156. #ifdef WIN32
  157. CloseHandle(semaphore);
  158. #else
  159. sem_destroy(semaphore);
  160. #ifdef __APPLE__
  161. {
  162. char name[128];
  163. sprintf(name, "midi_sem_%d", id);
  164. sem_close(semaphore);
  165. sem_unlink(name);
  166. }
  167. #else
  168. free(semaphore);
  169. #endif
  170. #endif
  171. }
  172. static void
  173. die(const char *source, const char *error_message)
  174. {
  175. output_error(source, error_message);
  176. output_usage();
  177. exit(EXIT_FAILURE);
  178. }
  179. static const char *
  180. get_semaphore_error(void)
  181. {
  182. #ifdef WIN32
  183. DWORD error = GetLastError();
  184. if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
  185. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  186. semaphore_error_msg, 1024, NULL)) {
  187. snprintf(semaphore_error_msg, 1023, "Unknown OS error code '%ld'",
  188. error);
  189. }
  190. return semaphore_error_msg;
  191. #else
  192. return strerror(errno);
  193. #endif
  194. }
  195. static void
  196. handle_info(const char *message)
  197. {
  198. /* Suppress info */
  199. }
  200. static void
  201. handle_port_connection_change(jack_port_id_t port_id_1,
  202. jack_port_id_t port_id_2, int connected,
  203. void *arg)
  204. {
  205. jack_port_t *port_1;
  206. jack_port_t *port_2;
  207. if ((remote_in_port != NULL) && (remote_out_port != NULL)) {
  208. return;
  209. }
  210. port_1 = jack_port_by_id(client, port_id_1);
  211. port_2 = jack_port_by_id(client, port_id_2);
  212. /* The 'update_connection' call is not RT-safe. It calls
  213. 'jack_port_get_connections' and 'jack_free'. This might be a problem
  214. with JACK 1, as this callback runs in the process thread in JACK 1. */
  215. if (port_1 == in_port) {
  216. remote_in_port = update_connection(port_2, connected, in_port,
  217. remote_in_port,
  218. target_in_port_name);
  219. } else if (port_2 == in_port) {
  220. remote_in_port = update_connection(port_1, connected, in_port,
  221. remote_in_port,
  222. target_in_port_name);
  223. } else if (port_1 == out_port) {
  224. remote_out_port = update_connection(port_2, connected, out_port,
  225. remote_out_port,
  226. target_out_port_name);
  227. } else if (port_2 == out_port) {
  228. remote_out_port = update_connection(port_1, connected, out_port,
  229. remote_out_port,
  230. target_out_port_name);
  231. }
  232. if ((remote_in_port != NULL) && (remote_out_port != NULL)) {
  233. connections_established = 1;
  234. if (! signal_semaphore(connect_semaphore)) {
  235. /* Sigh ... */
  236. die("post_semaphore", get_semaphore_error());
  237. }
  238. if (! signal_semaphore(init_semaphore)) {
  239. /* Sigh ... */
  240. die("post_semaphore", get_semaphore_error());
  241. }
  242. }
  243. }
  244. static int
  245. handle_process(jack_nframes_t frames, void *arg)
  246. {
  247. jack_midi_data_t *buffer;
  248. jack_midi_event_t event;
  249. jack_nframes_t event_count;
  250. jack_nframes_t event_time;
  251. jack_nframes_t frame;
  252. size_t i;
  253. jack_nframes_t last_frame_time;
  254. jack_midi_data_t *message;
  255. jack_time_t microseconds;
  256. void *port_buffer;
  257. jack_time_t time;
  258. jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames));
  259. switch (process_state) {
  260. case 0:
  261. /* State: initializing */
  262. switch (wait_semaphore(init_semaphore, 0)) {
  263. case -1:
  264. set_process_error(SOURCE_WAIT_SEMAPHORE, get_semaphore_error());
  265. /* Fallthrough on purpose */
  266. case 0:
  267. return 0;
  268. }
  269. highest_latency = 0;
  270. lowest_latency = 0;
  271. messages_received = 0;
  272. messages_sent = 0;
  273. process_state = 1;
  274. total_latency = 0;
  275. total_latency_time = 0;
  276. unexpected_messages = 0;
  277. xrun_count = 0;
  278. jack_port_get_latency_range(remote_in_port, JackCaptureLatency,
  279. &in_latency_range);
  280. jack_port_get_latency_range(remote_out_port, JackPlaybackLatency,
  281. &out_latency_range);
  282. goto send_message;
  283. case 1:
  284. /* State: processing */
  285. port_buffer = jack_port_get_buffer(in_port, frames);
  286. event_count = jack_midi_get_event_count(port_buffer);
  287. last_frame_time = jack_last_frame_time(client);
  288. for (i = 0; i < event_count; i++) {
  289. jack_midi_event_get(&event, port_buffer, i);
  290. message = (messages_received % 2) ? message_2 : message_1;
  291. if ((event.size == message_size) &&
  292. (! memcmp(message, event.buffer,
  293. message_size * sizeof(jack_midi_data_t)))) {
  294. goto found_message;
  295. }
  296. unexpected_messages++;
  297. }
  298. microseconds = jack_frames_to_time(client, last_frame_time) -
  299. last_activity_time;
  300. if ((microseconds / 1000000) >= timeout) {
  301. set_process_error(SOURCE_PROCESS, ERROR_MSG_TIMEOUT);
  302. }
  303. break;
  304. found_message:
  305. event_time = last_frame_time + event.time;
  306. frame = event_time - last_activity;
  307. time = jack_frames_to_time(client, event_time) - last_activity_time;
  308. if ((! highest_latency) || (frame > highest_latency)) {
  309. highest_latency = frame;
  310. highest_latency_time = time;
  311. }
  312. if ((! lowest_latency) || (frame < lowest_latency)) {
  313. lowest_latency = frame;
  314. lowest_latency_time = time;
  315. }
  316. latency_time_values[messages_received] = time;
  317. latency_values[messages_received] = frame;
  318. total_latency += frame;
  319. total_latency_time += time;
  320. messages_received++;
  321. if (messages_received == samples) {
  322. process_state = 2;
  323. if (! signal_semaphore(process_semaphore)) {
  324. /* Sigh ... */
  325. die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
  326. }
  327. break;
  328. }
  329. send_message:
  330. frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames);
  331. if (frame >= frames) {
  332. frame = frames - 1;
  333. }
  334. port_buffer = jack_port_get_buffer(out_port, frames);
  335. buffer = jack_midi_event_reserve(port_buffer, frame, message_size);
  336. if (buffer == NULL) {
  337. set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE);
  338. break;
  339. }
  340. message = (messages_sent % 2) ? message_2 : message_1;
  341. memcpy(buffer, message, message_size * sizeof(jack_midi_data_t));
  342. last_activity = jack_last_frame_time(client) + frame;
  343. last_activity_time = jack_frames_to_time(client, last_activity);
  344. messages_sent++;
  345. case 2:
  346. /* State: finished - do nothing */
  347. case -1:
  348. /* State: error - do nothing */
  349. case -2:
  350. /* State: signalled - do nothing */
  351. ;
  352. }
  353. return 0;
  354. }
  355. static void
  356. handle_shutdown(void *arg)
  357. {
  358. set_process_error(SOURCE_SHUTDOWN, ERROR_SHUTDOWN);
  359. }
  360. static void
  361. handle_signal(int sig)
  362. {
  363. process_state = -2;
  364. if (! signal_semaphore(connect_semaphore)) {
  365. /* Sigh ... */
  366. die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
  367. }
  368. if (! signal_semaphore(process_semaphore)) {
  369. /* Sigh ... */
  370. die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
  371. }
  372. }
  373. static int
  374. handle_xrun(void *arg)
  375. {
  376. xrun_count++;
  377. return 0;
  378. }
  379. static void
  380. output_error(const char *source, const char *message)
  381. {
  382. fprintf(stderr, "%s: %s: %s\n", program_name, source, message);
  383. }
  384. static void
  385. output_usage(void)
  386. {
  387. fprintf(stderr, "Usage: %s [options] [out-port-name in-port-name]\n\n"
  388. "\t-h, --help print program usage\n"
  389. "\t-m, --message-size=size set size of MIDI messages to send "
  390. "(default: 3)\n"
  391. "\t-s, --samples=n number of MIDI messages to send "
  392. "(default: 1024)\n"
  393. "\t-t, --timeout=seconds message timeout (default: 5)\n\n",
  394. program_name);
  395. }
  396. static unsigned long
  397. parse_positive_number_arg(char *s, char *name)
  398. {
  399. char *end_ptr;
  400. unsigned long result;
  401. errno = 0;
  402. result = strtoul(s, &end_ptr, 10);
  403. if (errno) {
  404. die(name, strerror(errno));
  405. }
  406. if (*s == '\0') {
  407. die(name, "argument value cannot be empty");
  408. }
  409. if (*end_ptr != '\0') {
  410. die(name, "invalid value");
  411. }
  412. if (! result) {
  413. die(name, "must be a positive number");
  414. }
  415. return result;
  416. }
  417. static int
  418. register_signal_handler(void (*func)(int))
  419. {
  420. #ifdef WIN32
  421. if (signal(SIGABRT, func) == SIG_ERR) {
  422. return 0;
  423. }
  424. #else
  425. if (signal(SIGQUIT, func) == SIG_ERR) {
  426. return 0;
  427. }
  428. if (signal(SIGHUP, func) == SIG_ERR) {
  429. return 0;
  430. }
  431. #endif
  432. if (signal(SIGINT, func) == SIG_ERR) {
  433. return 0;
  434. }
  435. if (signal(SIGTERM, func) == SIG_ERR) {
  436. return 0;
  437. }
  438. return 1;
  439. }
  440. static void
  441. set_process_error(const char *source, const char *message)
  442. {
  443. error_source = source;
  444. error_message = message;
  445. process_state = -1;
  446. if (! signal_semaphore(process_semaphore)) {
  447. /* Sigh ... */
  448. output_error(source, message);
  449. die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
  450. }
  451. }
  452. static int
  453. signal_semaphore(semaphore_t semaphore)
  454. {
  455. #ifdef WIN32
  456. return ReleaseSemaphore(semaphore, 1, NULL);
  457. #else
  458. return ! sem_post(semaphore);
  459. #endif
  460. }
  461. static jack_port_t *
  462. update_connection(jack_port_t *remote_port, int connected,
  463. jack_port_t *local_port, jack_port_t *current_port,
  464. const char *target_name)
  465. {
  466. if (connected) {
  467. if (current_port) {
  468. return current_port;
  469. }
  470. if (target_name) {
  471. char *aliases[2];
  472. if (! strcmp(target_name, jack_port_name(remote_port))) {
  473. return remote_port;
  474. }
  475. aliases[0] = alias1;
  476. aliases[1] = alias2;
  477. switch (jack_port_get_aliases(remote_port, aliases)) {
  478. case -1:
  479. /* Sigh ... */
  480. die("jack_port_get_aliases", "Failed to get port aliases");
  481. case 2:
  482. if (! strcmp(target_name, alias2)) {
  483. return remote_port;
  484. }
  485. /* Fallthrough on purpose */
  486. case 1:
  487. if (! strcmp(target_name, alias1)) {
  488. return remote_port;
  489. }
  490. /* Fallthrough on purpose */
  491. case 0:
  492. return NULL;
  493. }
  494. /* This shouldn't happen. */
  495. assert(0);
  496. }
  497. return remote_port;
  498. }
  499. if (! strcmp(jack_port_name(remote_port), jack_port_name(current_port))) {
  500. const char **port_names;
  501. if (target_name) {
  502. return NULL;
  503. }
  504. port_names = jack_port_get_connections(local_port);
  505. if (port_names == NULL) {
  506. return NULL;
  507. }
  508. /* If a connected port is disconnected and other ports are still
  509. connected, then we take the first port name in the array and use it
  510. as our remote port. It's a dumb implementation. */
  511. current_port = jack_port_by_name(client, port_names[0]);
  512. jack_free(port_names);
  513. if (current_port == NULL) {
  514. /* Sigh */
  515. die("jack_port_by_name", "failed to get port by name");
  516. }
  517. }
  518. return current_port;
  519. }
  520. static int
  521. wait_semaphore(semaphore_t semaphore, int block)
  522. {
  523. #ifdef WIN32
  524. DWORD result = WaitForSingleObject(semaphore, block ? INFINITE : 0);
  525. switch (result) {
  526. case WAIT_OBJECT_0:
  527. return 1;
  528. case WAIT_TIMEOUT:
  529. return 0;
  530. }
  531. return -1;
  532. #else
  533. if (block) {
  534. while (sem_wait(semaphore)) {
  535. if (errno != EINTR) {
  536. return -1;
  537. }
  538. }
  539. } else {
  540. while (sem_trywait(semaphore)) {
  541. switch (errno) {
  542. case EAGAIN:
  543. return 0;
  544. case EINTR:
  545. continue;
  546. default:
  547. return -1;
  548. }
  549. }
  550. }
  551. return 1;
  552. #endif
  553. }
  554. int
  555. main(int argc, char **argv)
  556. {
  557. int jitter_plot[101];
  558. int latency_plot[101];
  559. int long_index = 0;
  560. struct option long_options[] = {
  561. {"help", 0, NULL, 'h'},
  562. {"message-size", 1, NULL, 'm'},
  563. {"samples", 1, NULL, 's'},
  564. {"timeout", 1, NULL, 't'}
  565. };
  566. size_t name_arg_count;
  567. size_t name_size;
  568. char *option_string = "hm:s:t:";
  569. int show_usage = 0;
  570. connections_established = 0;
  571. error_message = NULL;
  572. message_size = 3;
  573. program_name = argv[0];
  574. remote_in_port = 0;
  575. remote_out_port = 0;
  576. samples = 1024;
  577. timeout = 5;
  578. for (;;) {
  579. signed char c = getopt_long(argc, argv, option_string, long_options,
  580. &long_index);
  581. switch (c) {
  582. case 'h':
  583. show_usage = 1;
  584. break;
  585. case 'm':
  586. message_size = parse_positive_number_arg(optarg, "message-size");
  587. break;
  588. case 's':
  589. samples = parse_positive_number_arg(optarg, "samples");
  590. break;
  591. case 't':
  592. timeout = parse_positive_number_arg(optarg, "timeout");
  593. break;
  594. default:
  595. {
  596. char *s = "'- '";
  597. s[2] = c;
  598. die(s, "invalid switch");
  599. }
  600. case -1:
  601. if (show_usage) {
  602. output_usage();
  603. exit(EXIT_SUCCESS);
  604. }
  605. goto parse_port_names;
  606. case 1:
  607. /* end of switch :) */
  608. ;
  609. }
  610. }
  611. parse_port_names:
  612. name_arg_count = argc - optind;
  613. switch (name_arg_count) {
  614. case 2:
  615. target_in_port_name = argv[optind + 1];
  616. target_out_port_name = argv[optind];
  617. break;
  618. case 0:
  619. target_in_port_name = 0;
  620. target_out_port_name = 0;
  621. break;
  622. default:
  623. output_usage();
  624. return EXIT_FAILURE;
  625. }
  626. name_size = jack_port_name_size();
  627. alias1 = malloc(name_size * sizeof(char));
  628. if (alias1 == NULL) {
  629. error_message = strerror(errno);
  630. error_source = "malloc";
  631. goto show_error;
  632. }
  633. alias2 = malloc(name_size * sizeof(char));
  634. if (alias2 == NULL) {
  635. error_message = strerror(errno);
  636. error_source = "malloc";
  637. goto free_alias1;
  638. }
  639. latency_values = malloc(sizeof(jack_nframes_t) * samples);
  640. if (latency_values == NULL) {
  641. error_message = strerror(errno);
  642. error_source = "malloc";
  643. goto free_alias2;
  644. }
  645. latency_time_values = malloc(sizeof(jack_time_t) * samples);
  646. if (latency_time_values == NULL) {
  647. error_message = strerror(errno);
  648. error_source = "malloc";
  649. goto free_latency_values;
  650. }
  651. message_1 = malloc(message_size * sizeof(jack_midi_data_t));
  652. if (message_1 == NULL) {
  653. error_message = strerror(errno);
  654. error_source = "malloc";
  655. goto free_latency_time_values;
  656. }
  657. message_2 = malloc(message_size * sizeof(jack_midi_data_t));
  658. if (message_2 == NULL) {
  659. error_message = strerror(errno);
  660. error_source = "malloc";
  661. goto free_message_1;
  662. }
  663. switch (message_size) {
  664. case 1:
  665. message_1[0] = 0xf6;
  666. message_2[0] = 0xfe;
  667. break;
  668. case 2:
  669. message_1[0] = 0xc0;
  670. message_1[1] = 0x00;
  671. message_2[0] = 0xd0;
  672. message_2[1] = 0x7f;
  673. break;
  674. case 3:
  675. message_1[0] = 0x80;
  676. message_1[1] = 0x00;
  677. message_1[2] = 0x00;
  678. message_2[0] = 0x90;
  679. message_2[1] = 0x7f;
  680. message_2[2] = 0x7f;
  681. break;
  682. default:
  683. message_1[0] = 0xf0;
  684. memset(message_1 + 1, 0,
  685. (message_size - 2) * sizeof(jack_midi_data_t));
  686. message_1[message_size - 1] = 0xf7;
  687. message_2[0] = 0xf0;
  688. memset(message_2 + 1, 0x7f,
  689. (message_size - 2) * sizeof(jack_midi_data_t));
  690. message_2[message_size - 1] = 0xf7;
  691. }
  692. client = jack_client_open(program_name, JackNullOption, NULL);
  693. if (client == NULL) {
  694. error_message = "failed to open JACK client";
  695. error_source = "jack_client_open";
  696. goto free_message_2;
  697. }
  698. in_port = jack_port_register(client, "in", JACK_DEFAULT_MIDI_TYPE,
  699. JackPortIsInput, 0);
  700. if (in_port == NULL) {
  701. error_message = "failed to register MIDI-in port";
  702. error_source = "jack_port_register";
  703. goto close_client;
  704. }
  705. out_port = jack_port_register(client, "out", JACK_DEFAULT_MIDI_TYPE,
  706. JackPortIsOutput, 0);
  707. if (out_port == NULL) {
  708. error_message = "failed to register MIDI-out port";
  709. error_source = "jack_port_register";
  710. goto unregister_in_port;
  711. }
  712. if (jack_set_process_callback(client, handle_process, NULL)) {
  713. error_message = "failed to set process callback";
  714. error_source = "jack_set_process_callback";
  715. goto unregister_out_port;
  716. }
  717. if (jack_set_xrun_callback(client, handle_xrun, NULL)) {
  718. error_message = "failed to set xrun callback";
  719. error_source = "jack_set_xrun_callback";
  720. goto unregister_out_port;
  721. }
  722. if (jack_set_port_connect_callback(client, handle_port_connection_change,
  723. NULL)) {
  724. error_message = "failed to set port connection callback";
  725. error_source = "jack_set_port_connect_callback";
  726. goto unregister_out_port;
  727. }
  728. jack_on_shutdown(client, handle_shutdown, NULL);
  729. jack_set_info_function(handle_info);
  730. process_state = 0;
  731. connect_semaphore = create_semaphore(0);
  732. if (connect_semaphore == NULL) {
  733. error_message = get_semaphore_error();
  734. error_source = "create_semaphore";
  735. goto unregister_out_port;
  736. }
  737. init_semaphore = create_semaphore(1);
  738. if (init_semaphore == NULL) {
  739. error_message = get_semaphore_error();
  740. error_source = "create_semaphore";
  741. goto destroy_connect_semaphore;;
  742. }
  743. process_semaphore = create_semaphore(2);
  744. if (process_semaphore == NULL) {
  745. error_message = get_semaphore_error();
  746. error_source = "create_semaphore";
  747. goto destroy_init_semaphore;
  748. }
  749. if (jack_activate(client)) {
  750. error_message = "could not activate client";
  751. error_source = "jack_activate";
  752. goto destroy_process_semaphore;
  753. }
  754. if (name_arg_count) {
  755. if (jack_connect(client, jack_port_name(out_port),
  756. target_out_port_name)) {
  757. error_message = "could not connect MIDI out port";
  758. error_source = "jack_connect";
  759. goto deactivate_client;
  760. }
  761. if (jack_connect(client, target_in_port_name,
  762. jack_port_name(in_port))) {
  763. error_message = "could not connect MIDI in port";
  764. error_source = "jack_connect";
  765. goto deactivate_client;
  766. }
  767. }
  768. if (! register_signal_handler(handle_signal)) {
  769. error_message = strerror(errno);
  770. error_source = "register_signal_handler";
  771. goto deactivate_client;
  772. }
  773. printf("Waiting for connections ...\n");
  774. if (wait_semaphore(connect_semaphore, 1) == -1) {
  775. error_message = get_semaphore_error();
  776. error_source = "wait_semaphore";
  777. goto deactivate_client;
  778. }
  779. if (connections_established) {
  780. printf("Waiting for test completion ...\n\n");
  781. if (wait_semaphore(process_semaphore, 1) == -1) {
  782. error_message = get_semaphore_error();
  783. error_source = "wait_semaphore";
  784. goto deactivate_client;
  785. }
  786. }
  787. if (! register_signal_handler(SIG_DFL)) {
  788. error_message = strerror(errno);
  789. error_source = "register_signal_handler";
  790. goto deactivate_client;
  791. }
  792. if (process_state == 2) {
  793. double average_latency = ((double) total_latency) / samples;
  794. double average_latency_time = total_latency_time / samples;
  795. size_t i;
  796. double latency_plot_offset =
  797. floor(((double) lowest_latency_time) / 100.0) / 10.0;
  798. double sample_rate = (double) jack_get_sample_rate(client);
  799. jack_nframes_t total_jitter = 0;
  800. jack_time_t total_jitter_time = 0;
  801. for (i = 0; i <= 100; i++) {
  802. jitter_plot[i] = 0;
  803. latency_plot[i] = 0;
  804. }
  805. for (i = 0; i < samples; i++) {
  806. double latency_time_value = (double) latency_time_values[i];
  807. double latency_plot_time =
  808. (latency_time_value / 1000.0) - latency_plot_offset;
  809. double jitter_time = ABS(average_latency_time -
  810. latency_time_value);
  811. if (latency_plot_time >= 10.0) {
  812. (latency_plot[100])++;
  813. } else {
  814. (latency_plot[(int) (latency_plot_time * 10.0)])++;
  815. }
  816. if (jitter_time >= 10000.0) {
  817. (jitter_plot[100])++;
  818. } else {
  819. (jitter_plot[(int) (jitter_time / 100.0)])++;
  820. }
  821. total_jitter += ABS(average_latency -
  822. ((double) latency_values[i]));
  823. total_jitter_time += jitter_time;
  824. }
  825. printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n"
  826. "Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n"
  827. "Average latency: %.2f ms (%.2f frames)\n"
  828. "Lowest latency: %.2f ms (%u frames)\n"
  829. "Highest latency: %.2f ms (%u frames)\n"
  830. "Peak MIDI jitter: %.2f ms (%u frames)\n"
  831. "Average MIDI jitter: %.2f ms (%.2f frames)\n",
  832. (out_latency_range.min / sample_rate) * 1000.0,
  833. (out_latency_range.max / sample_rate) * 1000.0,
  834. out_latency_range.min, out_latency_range.max,
  835. (in_latency_range.min / sample_rate) * 1000.0,
  836. (in_latency_range.max / sample_rate) * 1000.0,
  837. in_latency_range.min, in_latency_range.max,
  838. average_latency_time / 1000.0, average_latency,
  839. lowest_latency_time / 1000.0, lowest_latency,
  840. highest_latency_time / 1000.0, highest_latency,
  841. (highest_latency_time - lowest_latency_time) / 1000.0,
  842. highest_latency - lowest_latency,
  843. (total_jitter_time / 1000.0) / samples,
  844. ((double) total_jitter) / samples);
  845. printf("\nJitter Plot:\n");
  846. for (i = 0; i < 100; i++) {
  847. if (jitter_plot[i]) {
  848. printf("%.1f - %.1f ms: %d\n", ((float) i) / 10.0,
  849. ((float) (i + 1)) / 10.0, jitter_plot[i]);
  850. }
  851. }
  852. if (jitter_plot[100]) {
  853. printf(" > 10 ms: %d\n", jitter_plot[100]);
  854. }
  855. printf("\nLatency Plot:\n");
  856. for (i = 0; i < 100; i++) {
  857. if (latency_plot[i]) {
  858. printf("%.1f - %.1f ms: %d\n",
  859. latency_plot_offset + (((float) i) / 10.0),
  860. latency_plot_offset + (((float) (i + 1)) / 10.0),
  861. latency_plot[i]);
  862. }
  863. }
  864. if (latency_plot[100]) {
  865. printf(" > %.1f ms: %d\n", latency_plot_offset + 10.0,
  866. latency_plot[100]);
  867. }
  868. }
  869. deactivate_client:
  870. jack_deactivate(client);
  871. printf("\nMessages sent: %d\nMessages received: %d\n", messages_sent,
  872. messages_received);
  873. if (unexpected_messages) {
  874. printf("Unexpected messages received: %d\n", unexpected_messages);
  875. }
  876. if (xrun_count) {
  877. printf("Xruns: %d\n", xrun_count);
  878. }
  879. destroy_process_semaphore:
  880. destroy_semaphore(process_semaphore, 2);
  881. destroy_init_semaphore:
  882. destroy_semaphore(init_semaphore, 1);
  883. destroy_connect_semaphore:
  884. destroy_semaphore(connect_semaphore, 0);
  885. unregister_out_port:
  886. jack_port_unregister(client, out_port);
  887. unregister_in_port:
  888. jack_port_unregister(client, in_port);
  889. close_client:
  890. jack_client_close(client);
  891. free_message_2:
  892. free(message_2);
  893. free_message_1:
  894. free(message_1);
  895. free_latency_time_values:
  896. free(latency_time_values);
  897. free_latency_values:
  898. free(latency_values);
  899. free_alias2:
  900. free(alias2);
  901. free_alias1:
  902. free(alias1);
  903. if (error_message != NULL) {
  904. show_error:
  905. output_error(error_source, error_message);
  906. exit(EXIT_FAILURE);
  907. }
  908. return EXIT_SUCCESS;
  909. }