2 Copyright (C) 2010 Devin Anderson
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * This program is used to measure MIDI latency and jitter. It writes MIDI
22 * messages to one port and calculates how long it takes before it reads the
23 * same MIDI message over another port. It was written to calculate the
24 * latency and jitter of hardware and JACK hardware drivers, but might have
25 * other practical applications.
27 * The latency results of the program include the latency introduced by the
28 * JACK system. Because JACK has sample accurate MIDI, the same latency
29 * imposed on audio is also imposed on MIDI going through the system. Make
30 * sure you take this into account before complaining to me or (*especially*)
31 * other JACK developers about reported MIDI latency.
33 * The jitter results are a little more interesting. The program attempts to
34 * calculate 'average jitter' and 'peak jitter', as defined here:
36 * http://openmuse.org/transport/fidelity.html
38 * It also outputs a jitter plot, which gives you a more specific idea about
39 * the MIDI jitter for the ports you're testing. This is useful for catching
40 * extreme jitter values, and for analyzing the amount of truth in the
41 * technical specifications for your MIDI interface(s). :)
43 * This program is loosely based on 'alsa-midi-latency-test' in the ALSA test
46 * To port this program to non-POSIX platforms, you'll have to include
47 * implementations for semaphores and command-line argument handling.
60 #include <jack/jack.h>
61 #include <jack/midiport.h>
67 #include <semaphore.h>
70 #define ABS(x) (((x) >= 0) ? (x) : (-(x)))
73 typedef HANDLE semaphore_t
;
75 typedef sem_t
*semaphore_t
;
78 const char *ERROR_MSG_TIMEOUT
= "timed out while waiting for MIDI message";
79 const char *ERROR_RESERVE
= "could not reserve MIDI event on port buffer";
80 const char *ERROR_SHUTDOWN
= "the JACK server has been shutdown";
82 const char *SOURCE_EVENT_RESERVE
= "jack_midi_event_reserve";
83 const char *SOURCE_PROCESS
= "handle_process";
84 const char *SOURCE_SHUTDOWN
= "handle_shutdown";
85 const char *SOURCE_SIGNAL_SEMAPHORE
= "signal_semaphore";
86 const char *SOURCE_WAIT_SEMAPHORE
= "wait_semaphore";
90 jack_client_t
*client
;
91 semaphore_t connect_semaphore
;
92 volatile int connections_established
;
93 const char *error_message
;
94 const char *error_source
;
95 jack_nframes_t highest_latency
;
96 jack_time_t highest_latency_time
;
97 jack_latency_range_t in_latency_range
;
99 semaphore_t init_semaphore
;
100 jack_nframes_t last_activity
;
101 jack_time_t last_activity_time
;
102 jack_time_t
*latency_time_values
;
103 jack_nframes_t
*latency_values
;
104 jack_nframes_t lowest_latency
;
105 jack_time_t lowest_latency_time
;
106 jack_midi_data_t
*message_1
;
107 jack_midi_data_t
*message_2
;
108 int messages_received
;
111 jack_latency_range_t out_latency_range
;
112 jack_port_t
*out_port
;
113 semaphore_t process_semaphore
;
114 volatile sig_atomic_t process_state
;
116 jack_port_t
*remote_in_port
;
117 jack_port_t
*remote_out_port
;
119 const char *target_in_port_name
;
120 const char *target_out_port_name
;
122 jack_nframes_t total_latency
;
123 jack_time_t total_latency_time
;
124 int unexpected_messages
;
128 char semaphore_error_msg
[1024];
132 output_error(const char *source
, const char *message
);
138 set_process_error(const char *source
, const char *message
);
141 signal_semaphore(semaphore_t semaphore
);
144 update_connection(jack_port_t
*remote_port
, int connected
,
145 jack_port_t
*local_port
, jack_port_t
*current_port
,
146 const char *target_name
);
149 wait_semaphore(semaphore_t semaphore
, int block
);
152 create_semaphore(int id
)
154 semaphore_t semaphore
;
157 semaphore
= CreateSemaphore(NULL
, 0, 2, NULL
);
158 #elif defined (__APPLE__)
160 sprintf(name
, "midi_sem_%d", id
);
161 semaphore
= sem_open(name
, O_CREAT
, 0777, 0);
162 if (semaphore
== (sem_t
*) SEM_FAILED
) {
166 semaphore
= malloc(sizeof(sem_t
));
167 if (semaphore
!= NULL
) {
168 if (sem_init(semaphore
, 0, 0)) {
179 destroy_semaphore(semaphore_t semaphore
, int id
)
183 CloseHandle(semaphore
);
185 sem_destroy(semaphore
);
189 sprintf(name
, "midi_sem_%d", id
);
190 sem_close(semaphore
);
201 die(const char *source
, const char *error_message
)
203 output_error(source
, error_message
);
209 get_semaphore_error(void)
213 DWORD error
= GetLastError();
214 if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
, NULL
, error
,
215 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
216 semaphore_error_msg
, 1024, NULL
)) {
217 snprintf(semaphore_error_msg
, 1023, "Unknown OS error code '%ld'",
220 return semaphore_error_msg
;
222 return strerror(errno
);
228 handle_info(const char *message
)
234 handle_port_connection_change(jack_port_id_t port_id_1
,
235 jack_port_id_t port_id_2
, int connected
,
240 if ((remote_in_port
!= NULL
) && (remote_out_port
!= NULL
)) {
243 port_1
= jack_port_by_id(client
, port_id_1
);
244 port_2
= jack_port_by_id(client
, port_id_2
);
246 /* The 'update_connection' call is not RT-safe. It calls
247 'jack_port_get_connections' and 'jack_free'. This might be a problem
248 with JACK 1, as this callback runs in the process thread in JACK 1. */
250 if (port_1
== in_port
) {
251 remote_in_port
= update_connection(port_2
, connected
, in_port
,
253 target_in_port_name
);
254 } else if (port_2
== in_port
) {
255 remote_in_port
= update_connection(port_1
, connected
, in_port
,
257 target_in_port_name
);
258 } else if (port_1
== out_port
) {
259 remote_out_port
= update_connection(port_2
, connected
, out_port
,
261 target_out_port_name
);
262 } else if (port_2
== out_port
) {
263 remote_out_port
= update_connection(port_1
, connected
, out_port
,
265 target_out_port_name
);
267 if ((remote_in_port
!= NULL
) && (remote_out_port
!= NULL
)) {
268 connections_established
= 1;
269 if (! signal_semaphore(connect_semaphore
)) {
271 die("post_semaphore", get_semaphore_error());
273 if (! signal_semaphore(init_semaphore
)) {
275 die("post_semaphore", get_semaphore_error());
281 handle_process(jack_nframes_t frames
, void *arg
)
283 jack_midi_data_t
*buffer
;
284 jack_midi_event_t event
;
285 jack_nframes_t event_count
;
286 jack_nframes_t event_time
;
287 jack_nframes_t frame
;
289 jack_nframes_t last_frame_time
;
290 jack_midi_data_t
*message
;
291 jack_time_t microseconds
;
294 jack_midi_clear_buffer(jack_port_get_buffer(out_port
, frames
));
295 switch (process_state
) {
298 /* State: initializing */
299 switch (wait_semaphore(init_semaphore
, 0)) {
301 set_process_error(SOURCE_WAIT_SEMAPHORE
, get_semaphore_error());
302 /* Fallthrough on purpose */
308 messages_received
= 0;
312 total_latency_time
= 0;
313 unexpected_messages
= 0;
315 jack_port_get_latency_range(remote_in_port
, JackCaptureLatency
,
317 jack_port_get_latency_range(remote_out_port
, JackPlaybackLatency
,
322 /* State: processing */
323 port_buffer
= jack_port_get_buffer(in_port
, frames
);
324 event_count
= jack_midi_get_event_count(port_buffer
);
325 last_frame_time
= jack_last_frame_time(client
);
326 for (i
= 0; i
< event_count
; i
++) {
327 jack_midi_event_get(&event
, port_buffer
, i
);
328 message
= (messages_received
% 2) ? message_2
: message_1
;
329 if ((event
.size
== message_size
) &&
330 (! memcmp(message
, event
.buffer
,
331 message_size
* sizeof(jack_midi_data_t
)))) {
334 unexpected_messages
++;
336 microseconds
= jack_frames_to_time(client
, last_frame_time
) -
338 if ((microseconds
/ 1000000) >= timeout
) {
339 set_process_error(SOURCE_PROCESS
, ERROR_MSG_TIMEOUT
);
343 event_time
= last_frame_time
+ event
.time
;
344 frame
= event_time
- last_activity
;
345 time
= jack_frames_to_time(client
, event_time
) - last_activity_time
;
346 if ((! highest_latency
) || (frame
> highest_latency
)) {
347 highest_latency
= frame
;
348 highest_latency_time
= time
;
350 if ((! lowest_latency
) || (frame
< lowest_latency
)) {
351 lowest_latency
= frame
;
352 lowest_latency_time
= time
;
354 latency_time_values
[messages_received
] = time
;
355 latency_values
[messages_received
] = frame
;
356 total_latency
+= frame
;
357 total_latency_time
+= time
;
359 if (messages_received
== samples
) {
361 if (! signal_semaphore(process_semaphore
)) {
363 die(SOURCE_SIGNAL_SEMAPHORE
, get_semaphore_error());
368 frame
= (jack_nframes_t
) ((((double) rand()) / RAND_MAX
) * frames
);
369 if (frame
>= frames
) {
372 port_buffer
= jack_port_get_buffer(out_port
, frames
);
373 buffer
= jack_midi_event_reserve(port_buffer
, frame
, message_size
);
374 if (buffer
== NULL
) {
375 set_process_error(SOURCE_EVENT_RESERVE
, ERROR_RESERVE
);
378 message
= (messages_sent
% 2) ? message_2
: message_1
;
379 memcpy(buffer
, message
, message_size
* sizeof(jack_midi_data_t
));
380 last_activity
= jack_last_frame_time(client
) + frame
;
381 last_activity_time
= jack_frames_to_time(client
, last_activity
);
385 /* State: finished - do nothing */
387 /* State: error - do nothing */
389 /* State: signalled - do nothing */
396 handle_shutdown(void *arg
)
398 set_process_error(SOURCE_SHUTDOWN
, ERROR_SHUTDOWN
);
402 handle_signal(int sig
)
405 if (! signal_semaphore(connect_semaphore
)) {
407 die(SOURCE_SIGNAL_SEMAPHORE
, get_semaphore_error());
409 if (! signal_semaphore(process_semaphore
)) {
411 die(SOURCE_SIGNAL_SEMAPHORE
, get_semaphore_error());
416 handle_xrun(void *arg
)
423 output_error(const char *source
, const char *message
)
425 fprintf(stderr
, "%s: %s: %s\n", program_name
, source
, message
);
431 fprintf(stderr
, "Usage: %s [options] [out-port-name in-port-name]\n\n"
432 "\t-h, --help print program usage\n"
433 "\t-m, --message-size=size set size of MIDI messages to send "
435 "\t-s, --samples=n number of MIDI messages to send "
437 "\t-t, --timeout=seconds message timeout (default: 5)\n\n",
442 parse_positive_number_arg(char *s
, char *name
)
445 unsigned long result
;
447 result
= strtoul(s
, &end_ptr
, 10);
449 die(name
, strerror(errno
));
452 die(name
, "argument value cannot be empty");
454 if (*end_ptr
!= '\0') {
455 die(name
, "invalid value");
458 die(name
, "must be a positive number");
464 register_signal_handler(void (*func
)(int))
468 if (signal(SIGABRT
, func
) == SIG_ERR
) {
472 if (signal(SIGQUIT
, func
) == SIG_ERR
) {
475 if (signal(SIGHUP
, func
) == SIG_ERR
) {
480 if (signal(SIGINT
, func
) == SIG_ERR
) {
483 if (signal(SIGTERM
, func
) == SIG_ERR
) {
490 set_process_error(const char *source
, const char *message
)
492 error_source
= source
;
493 error_message
= message
;
495 if (! signal_semaphore(process_semaphore
)) {
497 output_error(source
, message
);
498 die(SOURCE_SIGNAL_SEMAPHORE
, get_semaphore_error());
503 signal_semaphore(semaphore_t semaphore
)
507 return ReleaseSemaphore(semaphore
, 1, NULL
);
509 return ! sem_post(semaphore
);
515 update_connection(jack_port_t
*remote_port
, int connected
,
516 jack_port_t
*local_port
, jack_port_t
*current_port
,
517 const char *target_name
)
525 if (! strcmp(target_name
, jack_port_name(remote_port
))) {
530 switch (jack_port_get_aliases(remote_port
, aliases
)) {
533 die("jack_port_get_aliases", "Failed to get port aliases");
535 if (! strcmp(target_name
, alias2
)) {
538 /* Fallthrough on purpose */
540 if (! strcmp(target_name
, alias1
)) {
543 /* Fallthrough on purpose */
547 /* This shouldn't happen. */
552 if (! strcmp(jack_port_name(remote_port
), jack_port_name(current_port
))) {
553 const char **port_names
;
557 port_names
= jack_port_get_connections(local_port
);
558 if (port_names
== NULL
) {
562 /* If a connected port is disconnected and other ports are still
563 connected, then we take the first port name in the array and use it
564 as our remote port. It's a dumb implementation. */
565 current_port
= jack_port_by_name(client
, port_names
[0]);
566 jack_free(port_names
);
567 if (current_port
== NULL
) {
569 die("jack_port_by_name", "failed to get port by name");
576 wait_semaphore(semaphore_t semaphore
, int block
)
580 DWORD result
= WaitForSingleObject(semaphore
, block
? INFINITE
: 0);
590 while (sem_wait(semaphore
)) {
591 if (errno
!= EINTR
) {
596 while (sem_trywait(semaphore
)) {
613 main(int argc
, char **argv
)
615 int jitter_plot
[101];
616 int latency_plot
[101];
618 struct option long_options
[] = {
619 {"help", 0, NULL
, 'h'},
620 {"message-size", 1, NULL
, 'm'},
621 {"samples", 1, NULL
, 's'},
622 {"timeout", 1, NULL
, 't'}
624 size_t name_arg_count
;
626 char *option_string
= "hm:s:t:";
628 connections_established
= 0;
629 error_message
= NULL
;
631 program_name
= argv
[0];
638 signed char c
= getopt_long(argc
, argv
, option_string
, long_options
,
645 message_size
= parse_positive_number_arg(optarg
, "message-size");
648 samples
= parse_positive_number_arg(optarg
, "samples");
651 timeout
= parse_positive_number_arg(optarg
, "timeout");
657 die(s
, "invalid switch");
664 goto parse_port_names
;
666 /* end of switch :) */
671 name_arg_count
= argc
- optind
;
672 switch (name_arg_count
) {
674 target_in_port_name
= argv
[optind
+ 1];
675 target_out_port_name
= argv
[optind
];
678 target_in_port_name
= 0;
679 target_out_port_name
= 0;
685 name_size
= jack_port_name_size();
686 alias1
= malloc(name_size
* sizeof(char));
687 if (alias1
== NULL
) {
688 error_message
= strerror(errno
);
689 error_source
= "malloc";
692 alias2
= malloc(name_size
* sizeof(char));
693 if (alias2
== NULL
) {
694 error_message
= strerror(errno
);
695 error_source
= "malloc";
698 latency_values
= malloc(sizeof(jack_nframes_t
) * samples
);
699 if (latency_values
== NULL
) {
700 error_message
= strerror(errno
);
701 error_source
= "malloc";
704 latency_time_values
= malloc(sizeof(jack_time_t
) * samples
);
705 if (latency_time_values
== NULL
) {
706 error_message
= strerror(errno
);
707 error_source
= "malloc";
708 goto free_latency_values
;
710 message_1
= malloc(message_size
* sizeof(jack_midi_data_t
));
711 if (message_1
== NULL
) {
712 error_message
= strerror(errno
);
713 error_source
= "malloc";
714 goto free_latency_time_values
;
716 message_2
= malloc(message_size
* sizeof(jack_midi_data_t
));
717 if (message_2
== NULL
) {
718 error_message
= strerror(errno
);
719 error_source
= "malloc";
722 switch (message_size
) {
743 memset(message_1
+ 1, 0,
744 (message_size
- 2) * sizeof(jack_midi_data_t
));
745 message_1
[message_size
- 1] = 0xf7;
747 memset(message_2
+ 1, 0x7f,
748 (message_size
- 2) * sizeof(jack_midi_data_t
));
749 message_2
[message_size
- 1] = 0xf7;
751 client
= jack_client_open(program_name
, JackNullOption
, NULL
);
752 if (client
== NULL
) {
753 error_message
= "failed to open JACK client";
754 error_source
= "jack_client_open";
757 in_port
= jack_port_register(client
, "in", JACK_DEFAULT_MIDI_TYPE
,
759 if (in_port
== NULL
) {
760 error_message
= "failed to register MIDI-in port";
761 error_source
= "jack_port_register";
764 out_port
= jack_port_register(client
, "out", JACK_DEFAULT_MIDI_TYPE
,
765 JackPortIsOutput
, 0);
766 if (out_port
== NULL
) {
767 error_message
= "failed to register MIDI-out port";
768 error_source
= "jack_port_register";
769 goto unregister_in_port
;
771 if (jack_set_process_callback(client
, handle_process
, NULL
)) {
772 error_message
= "failed to set process callback";
773 error_source
= "jack_set_process_callback";
774 goto unregister_out_port
;
776 if (jack_set_xrun_callback(client
, handle_xrun
, NULL
)) {
777 error_message
= "failed to set xrun callback";
778 error_source
= "jack_set_xrun_callback";
779 goto unregister_out_port
;
781 if (jack_set_port_connect_callback(client
, handle_port_connection_change
,
783 error_message
= "failed to set port connection callback";
784 error_source
= "jack_set_port_connect_callback";
785 goto unregister_out_port
;
787 jack_on_shutdown(client
, handle_shutdown
, NULL
);
788 jack_set_info_function(handle_info
);
791 connect_semaphore
= create_semaphore(0);
792 if (connect_semaphore
== NULL
) {
793 error_message
= get_semaphore_error();
794 error_source
= "create_semaphore";
795 goto unregister_out_port
;
797 init_semaphore
= create_semaphore(1);
798 if (init_semaphore
== NULL
) {
799 error_message
= get_semaphore_error();
800 error_source
= "create_semaphore";
801 goto destroy_connect_semaphore
;;
803 process_semaphore
= create_semaphore(2);
804 if (process_semaphore
== NULL
) {
805 error_message
= get_semaphore_error();
806 error_source
= "create_semaphore";
807 goto destroy_init_semaphore
;
809 if (jack_activate(client
)) {
810 error_message
= "could not activate client";
811 error_source
= "jack_activate";
812 goto destroy_process_semaphore
;
814 if (name_arg_count
) {
815 if (jack_connect(client
, jack_port_name(out_port
),
816 target_out_port_name
)) {
817 error_message
= "could not connect MIDI out port";
818 error_source
= "jack_connect";
819 goto deactivate_client
;
821 if (jack_connect(client
, target_in_port_name
,
822 jack_port_name(in_port
))) {
823 error_message
= "could not connect MIDI in port";
824 error_source
= "jack_connect";
825 goto deactivate_client
;
828 if (! register_signal_handler(handle_signal
)) {
829 error_message
= strerror(errno
);
830 error_source
= "register_signal_handler";
831 goto deactivate_client
;
833 printf("Waiting for connections ...\n");
834 if (wait_semaphore(connect_semaphore
, 1) == -1) {
835 error_message
= get_semaphore_error();
836 error_source
= "wait_semaphore";
837 goto deactivate_client
;
839 if (connections_established
) {
840 printf("Waiting for test completion ...\n\n");
841 if (wait_semaphore(process_semaphore
, 1) == -1) {
842 error_message
= get_semaphore_error();
843 error_source
= "wait_semaphore";
844 goto deactivate_client
;
847 if (! register_signal_handler(SIG_DFL
)) {
848 error_message
= strerror(errno
);
849 error_source
= "register_signal_handler";
850 goto deactivate_client
;
852 if (process_state
== 2) {
853 double average_latency
= ((double) total_latency
) / samples
;
854 double average_latency_time
= total_latency_time
/ samples
;
856 double latency_plot_offset
=
857 floor(((double) lowest_latency_time
) / 100.0) / 10.0;
858 double sample_rate
= (double) jack_get_sample_rate(client
);
859 jack_nframes_t total_jitter
= 0;
860 jack_time_t total_jitter_time
= 0;
861 for (i
= 0; i
<= 100; i
++) {
865 for (i
= 0; i
< samples
; i
++) {
866 double latency_time_value
= (double) latency_time_values
[i
];
867 double latency_plot_time
=
868 (latency_time_value
/ 1000.0) - latency_plot_offset
;
869 double jitter_time
= ABS(average_latency_time
-
871 if (latency_plot_time
>= 10.0) {
872 (latency_plot
[100])++;
874 (latency_plot
[(int) (latency_plot_time
* 10.0)])++;
876 if (jitter_time
>= 10000.0) {
877 (jitter_plot
[100])++;
879 (jitter_plot
[(int) (jitter_time
/ 100.0)])++;
881 total_jitter
+= ABS(average_latency
-
882 ((double) latency_values
[i
]));
883 total_jitter_time
+= jitter_time
;
885 printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n"
886 "Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n"
887 "Average latency: %.2f ms (%.2f frames)\n"
888 "Lowest latency: %.2f ms (%u frames)\n"
889 "Highest latency: %.2f ms (%u frames)\n"
890 "Peak MIDI jitter: %.2f ms (%u frames)\n"
891 "Average MIDI jitter: %.2f ms (%.2f frames)\n",
892 (out_latency_range
.min
/ sample_rate
) * 1000.0,
893 (out_latency_range
.max
/ sample_rate
) * 1000.0,
894 out_latency_range
.min
, out_latency_range
.max
,
895 (in_latency_range
.min
/ sample_rate
) * 1000.0,
896 (in_latency_range
.max
/ sample_rate
) * 1000.0,
897 in_latency_range
.min
, in_latency_range
.max
,
898 average_latency_time
/ 1000.0, average_latency
,
899 lowest_latency_time
/ 1000.0, lowest_latency
,
900 highest_latency_time
/ 1000.0, highest_latency
,
901 (highest_latency_time
- lowest_latency_time
) / 1000.0,
902 highest_latency
- lowest_latency
,
903 (total_jitter_time
/ 1000.0) / samples
,
904 ((double) total_jitter
) / samples
);
905 printf("\nJitter Plot:\n");
906 for (i
= 0; i
< 100; i
++) {
907 if (jitter_plot
[i
]) {
908 printf("%.1f - %.1f ms: %d\n", ((float) i
) / 10.0,
909 ((float) (i
+ 1)) / 10.0, jitter_plot
[i
]);
912 if (jitter_plot
[100]) {
913 printf(" > 10 ms: %d\n", jitter_plot
[100]);
915 printf("\nLatency Plot:\n");
916 for (i
= 0; i
< 100; i
++) {
917 if (latency_plot
[i
]) {
918 printf("%.1f - %.1f ms: %d\n",
919 latency_plot_offset
+ (((float) i
) / 10.0),
920 latency_plot_offset
+ (((float) (i
+ 1)) / 10.0),
924 if (latency_plot
[100]) {
925 printf(" > %.1f ms: %d\n", latency_plot_offset
+ 10.0,
930 jack_deactivate(client
);
931 printf("\nMessages sent: %d\nMessages received: %d\n", messages_sent
,
933 if (unexpected_messages
) {
934 printf("Unexpected messages received: %d\n", unexpected_messages
);
937 printf("Xruns: %d\n", xrun_count
);
939 destroy_process_semaphore
:
940 destroy_semaphore(process_semaphore
, 2);
941 destroy_init_semaphore
:
942 destroy_semaphore(init_semaphore
, 1);
943 destroy_connect_semaphore
:
944 destroy_semaphore(connect_semaphore
, 0);
946 jack_port_unregister(client
, out_port
);
948 jack_port_unregister(client
, in_port
);
950 jack_client_close(client
);
955 free_latency_time_values
:
956 free(latency_time_values
);
958 free(latency_values
);
963 if (error_message
!= NULL
) {
965 output_error(error_source
, error_message
);