]>
Commit | Line | Data |
---|---|---|
8cabb16f MB |
1 | /* |
2 | * jack output driver. This file is part of Shairport Sync. | |
3 | * Copyright (c) 2018 Mike Brady <mikebrady@iercom.net> | |
4 | * | |
5 | * All rights reserved. | |
6 | * | |
7 | * Permission to use, copy, modify, and distribute this software for any | |
8 | * purpose with or without fee is hereby granted, provided that the above | |
9 | * copyright notice and this permission notice appear in all copies. | |
10 | * | |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
18 | */ | |
19 | ||
20 | #include "audio.h" | |
21 | #include "common.h" | |
22 | #include <errno.h> | |
23 | #include <getopt.h> | |
24 | #include <limits.h> | |
25 | #include <math.h> | |
26 | #include <pthread.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <string.h> | |
31 | #include <unistd.h> | |
32 | ||
33 | #include <jack/jack.h> | |
34 | #include <jack/transport.h> | |
35 | ||
36 | enum ift_type { | |
37 | IFT_frame_left_sample = 0, | |
38 | IFT_frame_right_sample, | |
39 | } ift_type; | |
40 | ||
41 | // Four seconds buffer -- should be plenty | |
42 | #define buffer_size 44100 * 4 * 2 * 2 | |
43 | ||
44 | static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER; | |
6c6dda9c | 45 | static pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER; |
8cabb16f MB |
46 | |
47 | char *audio_lmb, *audio_umb, *audio_toq, *audio_eoq; | |
48 | size_t audio_occupancy; // this is in frames, not bytes. A frame is a left and | |
49 | // right sample, each 16 bits, hence 4 bytes | |
6c6dda9c MB |
50 | pthread_t *open_client_if_necessary_thread = NULL; |
51 | ||
8cabb16f MB |
52 | // static void help(void); |
53 | int init(int, char **); | |
54 | // static void onmove_cb(void *, int); | |
959aa08b | 55 | void jack_deinit(void); |
8cabb16f MB |
56 | void jack_start(int, int); |
57 | int play(void *, int); | |
58 | void jack_stop(void); | |
59 | int jack_is_running(void); | |
60 | // static void onmove_cb(void *, int); | |
61 | int jack_delay(long *); | |
62 | void jack_flush(void); | |
63 | ||
64 | audio_output audio_jack = {.name = "jack", | |
65 | .help = NULL, | |
66 | .init = &init, | |
959aa08b | 67 | .deinit = &jack_deinit, |
8cabb16f MB |
68 | .start = &jack_start, |
69 | .stop = &jack_stop, | |
70 | .is_running = &jack_is_running, | |
71 | .flush = &jack_flush, | |
72 | .delay = &jack_delay, | |
73 | .play = &play, | |
74 | .volume = NULL, | |
75 | .parameters = NULL, | |
76 | .mute = NULL}; | |
77 | ||
8cabb16f MB |
78 | jack_port_t *left_port; |
79 | jack_port_t *right_port; | |
959aa08b | 80 | |
8cabb16f | 81 | long offset = 0; |
8cabb16f MB |
82 | |
83 | int client_is_open; | |
84 | jack_client_t *client; | |
85 | jack_nframes_t sample_rate; | |
86 | ||
39545cdf | 87 | jack_latency_range_t latest_left_latency_range, latest_right_latency_range; |
959aa08b | 88 | int64_t time_of_latest_transfer; |
8cabb16f MB |
89 | |
90 | int play(void *buf, int samples) { | |
91 | // debug(1,"jack_play of %d samples.",samples); | |
92 | // copy the samples into the queue | |
93 | size_t bytes_to_transfer = samples * 2 * 2; | |
94 | size_t space_to_end_of_buffer = audio_umb - audio_eoq; | |
95 | if (space_to_end_of_buffer >= bytes_to_transfer) { | |
96 | memcpy(audio_eoq, buf, bytes_to_transfer); | |
97 | pthread_mutex_lock(&buffer_mutex); | |
98 | audio_occupancy += samples; | |
99 | audio_eoq += bytes_to_transfer; | |
100 | pthread_mutex_unlock(&buffer_mutex); | |
101 | } else { | |
102 | memcpy(audio_eoq, buf, space_to_end_of_buffer); | |
103 | buf += space_to_end_of_buffer; | |
104 | memcpy(audio_lmb, buf, bytes_to_transfer - space_to_end_of_buffer); | |
105 | pthread_mutex_lock(&buffer_mutex); | |
106 | audio_occupancy += samples; | |
107 | audio_eoq = audio_lmb + bytes_to_transfer - space_to_end_of_buffer; | |
108 | pthread_mutex_unlock(&buffer_mutex); | |
109 | } | |
8cabb16f MB |
110 | return 0; |
111 | } | |
112 | ||
113 | void deinterleave_and_convert_stream(const char *interleaved_frames, | |
959aa08b | 114 | const jack_default_audio_sample_t *jack_frame_buffer, |
8cabb16f MB |
115 | jack_nframes_t number_of_frames, enum ift_type side) { |
116 | jack_nframes_t i; | |
117 | short *ifp = (short *)interleaved_frames; | |
959aa08b | 118 | jack_default_audio_sample_t *fp = (jack_default_audio_sample_t *)jack_frame_buffer; |
8cabb16f MB |
119 | if (side == IFT_frame_right_sample) |
120 | ifp++; | |
121 | for (i = 0; i < number_of_frames; i++) { | |
122 | short sample = *ifp; | |
959aa08b | 123 | jack_default_audio_sample_t converted_value; |
8cabb16f MB |
124 | if (sample >= 0) |
125 | converted_value = (1.0 * sample) / SHRT_MAX; | |
126 | else | |
127 | converted_value = -(1.0 * sample) / SHRT_MIN; | |
128 | *fp = converted_value; | |
129 | ifp++; | |
130 | ifp++; | |
131 | fp++; | |
132 | } | |
133 | } | |
134 | ||
135 | int jack_stream_write_cb(jack_nframes_t nframes, __attribute__((unused)) void *arg) { | |
136 | ||
959aa08b MB |
137 | jack_default_audio_sample_t *left_buffer = |
138 | (jack_default_audio_sample_t *)jack_port_get_buffer(left_port, nframes); | |
139 | jack_default_audio_sample_t *right_buffer = | |
140 | (jack_default_audio_sample_t *)jack_port_get_buffer(right_port, nframes); | |
8cabb16f MB |
141 | |
142 | size_t frames_we_can_transfer = nframes; | |
143 | // lock | |
144 | pthread_mutex_lock(&buffer_mutex); | |
145 | if (audio_occupancy < frames_we_can_transfer) { | |
146 | frames_we_can_transfer = audio_occupancy; | |
147 | } | |
148 | ||
149 | // frames we can transfer will never be greater than the frames available | |
150 | ||
151 | if (frames_we_can_transfer * 2 * 2 <= (size_t)(audio_umb - audio_toq)) { | |
152 | // the bytes are all in a row in the audio buffer | |
153 | deinterleave_and_convert_stream(audio_toq, &left_buffer[0], frames_we_can_transfer, | |
154 | IFT_frame_left_sample); | |
155 | deinterleave_and_convert_stream(audio_toq, &right_buffer[0], frames_we_can_transfer, | |
156 | IFT_frame_right_sample); | |
157 | audio_toq += frames_we_can_transfer * 2 * 2; | |
158 | } else { | |
159 | // the bytes are in two places in the audio buffer | |
160 | size_t first_portion_to_write = (audio_umb - audio_toq) / (2 * 2); | |
161 | if (first_portion_to_write != 0) { | |
162 | deinterleave_and_convert_stream(audio_toq, &left_buffer[0], first_portion_to_write, | |
163 | IFT_frame_left_sample); | |
164 | deinterleave_and_convert_stream(audio_toq, &right_buffer[0], first_portion_to_write, | |
165 | IFT_frame_right_sample); | |
166 | } | |
167 | deinterleave_and_convert_stream(audio_lmb, &left_buffer[first_portion_to_write], | |
168 | frames_we_can_transfer - first_portion_to_write, | |
169 | IFT_frame_left_sample); | |
170 | deinterleave_and_convert_stream(audio_lmb, &right_buffer[first_portion_to_write], | |
171 | frames_we_can_transfer - first_portion_to_write, | |
172 | IFT_frame_right_sample); | |
173 | audio_toq = audio_lmb + (frames_we_can_transfer - first_portion_to_write) * 2 * 2; | |
174 | } | |
175 | // debug(1,"transferring %u frames",frames_we_can_transfer); | |
176 | audio_occupancy -= frames_we_can_transfer; | |
959aa08b MB |
177 | jack_port_get_latency_range(left_port, JackPlaybackLatency, &latest_left_latency_range); |
178 | jack_port_get_latency_range(right_port, JackPlaybackLatency, &latest_right_latency_range); | |
179 | time_of_latest_transfer = get_absolute_time_in_fp(); | |
8cabb16f MB |
180 | pthread_mutex_unlock(&buffer_mutex); |
181 | // unlock | |
959aa08b MB |
182 | |
183 | // now, if there are any more frames to put into the buffer, fill them with | |
8cabb16f MB |
184 | // silence |
185 | jack_nframes_t i; | |
186 | ||
187 | for (i = frames_we_can_transfer; i < nframes; i++) { | |
188 | left_buffer[i] = 0.0; | |
189 | right_buffer[i] = 0.0; | |
190 | } | |
8cabb16f MB |
191 | return 0; |
192 | } | |
193 | ||
959aa08b | 194 | void default_jack_error_callback(const char *desc) { debug(2, "jackd error: \"%s\"", desc); } |
8cabb16f | 195 | |
959aa08b | 196 | void default_jack_info_callback(const char *desc) { inform("jackd information: \"%s\"", desc); } |
8cabb16f MB |
197 | |
198 | int jack_is_running() { | |
959aa08b | 199 | int reply = -1; |
8cabb16f MB |
200 | // if the client is open and initialised, see if the status is "rolling" |
201 | if (client_is_open) { | |
202 | jack_position_t pos; | |
959aa08b MB |
203 | jack_transport_state_t transport_state = jack_transport_query(client, &pos); |
204 | if (transport_state == JackTransportRolling) { | |
205 | // check if either port has a zero latency -- if so, then it's disconnected. This might be too | |
206 | // much | |
207 | jack_latency_range_t left_latency_range, right_latency_range; | |
208 | jack_port_get_latency_range(left_port, JackPlaybackLatency, &left_latency_range); | |
209 | jack_port_get_latency_range(right_port, JackPlaybackLatency, &right_latency_range); | |
210 | ||
211 | if ((left_latency_range.min == 0) && (left_latency_range.max == 0) && | |
212 | (right_latency_range.min == 0) && (right_latency_range.max == 0)) { | |
213 | reply = -3; // not connected | |
214 | } else { | |
215 | reply = 0; | |
216 | } | |
217 | } else { | |
218 | reply = -2; // not rolling | |
219 | } | |
8cabb16f MB |
220 | } |
221 | return reply; | |
222 | } | |
223 | ||
959aa08b | 224 | int jack_client_open_if_needed(void) { |
6c6dda9c | 225 | pthread_mutex_lock(&client_mutex); |
959aa08b MB |
226 | if (client_is_open == 0) { |
227 | jack_status_t status; | |
228 | client = jack_client_open(config.jack_client_name, JackNoStartServer, &status); | |
229 | if (client) { | |
230 | jack_set_process_callback(client, jack_stream_write_cb, 0); | |
231 | left_port = jack_port_register(client, config.jack_left_channel_name, JACK_DEFAULT_AUDIO_TYPE, | |
232 | JackPortIsOutput, 0); | |
233 | right_port = jack_port_register(client, config.jack_right_channel_name, | |
234 | JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |
235 | sample_rate = jack_get_sample_rate(client); | |
236 | // debug(1, "jackaudio sample rate = %" PRId32 ".", sample_rate); | |
237 | if (sample_rate == 44100) { | |
238 | if (jack_activate(client)) { | |
239 | debug(1, "jackaudio cannot activate client"); | |
240 | } else { | |
241 | client_is_open = 1; | |
242 | debug(2, "jackaudio client opened."); | |
243 | } | |
244 | } else { | |
245 | inform( | |
246 | "jackaudio is running at the wrong speed (%d) for Shairport Sync, which must be 44100", | |
247 | sample_rate); | |
248 | } | |
249 | } | |
250 | } | |
6c6dda9c | 251 | pthread_mutex_unlock(&client_mutex); |
959aa08b MB |
252 | return client_is_open; |
253 | } | |
254 | ||
6c6dda9c MB |
255 | void jack_close(void) { |
256 | pthread_mutex_lock(&client_mutex); | |
959aa08b MB |
257 | if (client_is_open) { |
258 | if (jack_deactivate(client)) | |
259 | debug(1, "Error deactivating jack client"); | |
260 | if (jack_client_close(client)) | |
261 | debug(1, "Error closing jack client"); | |
6c6dda9c MB |
262 | client_is_open = 0; |
263 | } | |
264 | pthread_mutex_unlock(&client_mutex); | |
265 | } | |
266 | ||
267 | void jack_deinit() { | |
268 | jack_close(); | |
269 | if (open_client_if_necessary_thread) { | |
270 | pthread_cancel(*open_client_if_necessary_thread); | |
271 | free((char *)open_client_if_necessary_thread); | |
959aa08b MB |
272 | } |
273 | } | |
274 | ||
6c6dda9c MB |
275 | void *open_client_if_necessary_thread_function(void *arg) { |
276 | int *interval = (int *)arg; | |
277 | while (*interval != 0) { | |
278 | if (client_is_open == 0) { | |
279 | debug(1, "Try to open the jack client"); | |
280 | jack_client_open_if_needed(); | |
281 | } | |
282 | sleep(*interval); | |
39545cdf | 283 | } |
6c6dda9c MB |
284 | pthread_exit(NULL); |
285 | } | |
286 | ||
8cabb16f MB |
287 | int init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) { |
288 | config.audio_backend_latency_offset = 0; | |
289 | config.audio_backend_buffer_desired_length = 0.15; | |
6c6dda9c | 290 | config.jack_auto_client_open_interval = 1; // check every second |
8cabb16f MB |
291 | |
292 | // get settings from settings file first, allow them to be overridden by | |
293 | // command line options | |
294 | ||
295 | // do the "general" audio options. Note, these options are in the "general" stanza! | |
296 | parse_general_audio_options(); | |
959aa08b | 297 | |
8cabb16f MB |
298 | // other options would be picked up here... |
299 | ||
959aa08b MB |
300 | // now the specific options |
301 | if (config.cfg != NULL) { | |
302 | const char *str; | |
6c6dda9c | 303 | int value; |
959aa08b MB |
304 | /* Get the Client Name. */ |
305 | if (config_lookup_string(config.cfg, "jack.client_name", &str)) { | |
306 | config.jack_client_name = (char *)str; | |
307 | } | |
308 | /* Get the Left Channel Name. */ | |
309 | if (config_lookup_string(config.cfg, "jack.left_channel_name", &str)) { | |
310 | config.jack_left_channel_name = (char *)str; | |
311 | } | |
312 | /* Get the Right Channel Name. */ | |
313 | if (config_lookup_string(config.cfg, "jack.right_channel_name", &str)) { | |
314 | config.jack_right_channel_name = (char *)str; | |
315 | } | |
39545cdf MB |
316 | |
317 | /* See if we should attempt to connect to the jack server automatically, and, if so, how often | |
318 | * we should try. */ | |
6c6dda9c MB |
319 | if (config_lookup_int(config.cfg, "jack.auto_client_open_interval", &value)) { |
320 | if ((value < 0) || (value > 300)) | |
39545cdf MB |
321 | die("Invalid jack auto_client_open_interval \"%sd\". It should be between 0 and 300, " |
322 | "default is %d", | |
323 | value, config.jack_auto_client_open_interval); | |
6c6dda9c MB |
324 | else |
325 | config.jack_auto_client_open_interval = value; | |
326 | } | |
327 | ||
328 | /* See if we should close the client at then end of a play session. */ | |
39545cdf MB |
329 | config_set_lookup_bool(config.cfg, "jack.auto_client_disconnect", |
330 | &config.jack_auto_client_disconnect); | |
331 | ||
6c6dda9c | 332 | // now, start a thread to automatically open a client when there is a server. |
959aa08b MB |
333 | } |
334 | ||
335 | if (config.jack_client_name == NULL) | |
336 | config.jack_client_name = strdup("Shairport Sync"); | |
337 | if (config.jack_left_channel_name == NULL) | |
338 | config.jack_left_channel_name = strdup("left"); | |
339 | if (config.jack_right_channel_name == NULL) | |
340 | config.jack_right_channel_name = strdup("right"); | |
341 | ||
342 | jack_set_error_function(default_jack_error_callback); | |
8cabb16f | 343 | jack_set_info_function(default_jack_info_callback); |
8cabb16f MB |
344 | |
345 | // allocate space for the audio buffer | |
346 | audio_lmb = malloc(buffer_size); | |
347 | if (audio_lmb == NULL) | |
348 | die("Can't allocate %d bytes for jackaudio buffer.", buffer_size); | |
349 | audio_toq = audio_eoq = audio_lmb; | |
350 | audio_umb = audio_lmb + buffer_size; | |
351 | audio_occupancy = 0; // frames | |
959aa08b MB |
352 | |
353 | client_is_open = 0; | |
39545cdf | 354 | |
6c6dda9c MB |
355 | if (config.jack_auto_client_open_interval != 0) { |
356 | open_client_if_necessary_thread = malloc(sizeof(pthread_t)); | |
357 | if (open_client_if_necessary_thread == NULL) | |
358 | die("Couldn't allocate space for jack server scanner thread"); | |
39545cdf MB |
359 | pthread_create(open_client_if_necessary_thread, NULL, open_client_if_necessary_thread_function, |
360 | &config.jack_auto_client_open_interval); | |
6c6dda9c MB |
361 | } else { |
362 | jack_client_open_if_needed(); | |
363 | } | |
959aa08b | 364 | |
8cabb16f MB |
365 | return 0; |
366 | } | |
367 | ||
959aa08b MB |
368 | void jack_start(__attribute__((unused)) int i_sample_rate, |
369 | __attribute__((unused)) int i_sample_format) { | |
6c6dda9c | 370 | // debug(1, "jack start"); |
959aa08b MB |
371 | // see if the client is running. If not, try to open and initialise it |
372 | ||
373 | if (jack_client_open_if_needed() == 0) | |
374 | debug(1, "cannot open a jack client for a play session"); | |
8cabb16f MB |
375 | } |
376 | ||
377 | int jack_delay(long *the_delay) { | |
39545cdf | 378 | |
959aa08b | 379 | // without the mutex, we could get the time of what is the last transfer of data to a jack buffer, |
39545cdf MB |
380 | // but then a transfer could occur and we would get the buffer occupancy after another transfer |
381 | // had occurred | |
382 | // so we could "lose" a full transfer (e.g. 1024 frames @ 44,100 fps ~ 23.2 milliseconds) | |
383 | pthread_mutex_lock(&buffer_mutex); | |
959aa08b | 384 | int64_t time_now = get_absolute_time_in_fp(); |
39545cdf MB |
385 | int64_t delta = time_now - time_of_latest_transfer; // this is the time back to the last time data |
386 | // was transferred into a jack buffer | |
387 | size_t audio_occupancy_now = audio_occupancy; // this is the buffer occupancy before any | |
388 | // subsequent transfer because transfer is blocked | |
389 | // by the mutex | |
959aa08b MB |
390 | pthread_mutex_unlock(&buffer_mutex); |
391 | ||
8cabb16f | 392 | int64_t frames_processed_since_latest_latency_check = (delta * 44100) >> 32; |
959aa08b | 393 | |
8cabb16f | 394 | // debug(1,"delta: %" PRId64 " frames.",frames_processed_since_latest_latency_check); |
39545cdf | 395 | jack_nframes_t base_latency = (latest_left_latency_range.min + latest_left_latency_range.max) / 2; |
959aa08b | 396 | if (base_latency == 0) |
39545cdf MB |
397 | base_latency = (latest_right_latency_range.min + latest_right_latency_range.max) / 2; |
398 | *the_delay = base_latency + audio_occupancy_now - frames_processed_since_latest_latency_check; | |
8cabb16f MB |
399 | |
400 | // debug(1,"reporting a delay of %d frames",*the_delay); | |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | void jack_flush() { | |
959aa08b | 406 | // debug(1,"jack flush"); |
8cabb16f MB |
407 | // lock |
408 | pthread_mutex_lock(&buffer_mutex); | |
409 | audio_toq = audio_eoq = audio_lmb; | |
410 | audio_umb = audio_lmb + buffer_size; | |
411 | audio_occupancy = 0; // frames | |
412 | pthread_mutex_unlock(&buffer_mutex); | |
413 | // unlock | |
414 | } | |
415 | ||
6c6dda9c MB |
416 | void jack_stop(void) { |
417 | // debug(1, "jack stop"); | |
418 | if (config.jack_auto_client_disconnect) | |
419 | jack_close(); | |
420 | } |