1 /*
2 * Copyright © 2012 David Richards
3 * Copyright © 2013 Sebastien Alaiwan
4 * Copyright © 2016 Damien Zammit
5 *
6 * This program is made available under an ISC-style license. See the
7 * accompanying file LICENSE for details.
8 */
9 #define _DEFAULT_SOURCE
10 #define _BSD_SOURCE
11 #include "cubeb-internal.h"
12 #include "cubeb/cubeb.h"
13 #include "cubeb_resampler.h"
14 #include "cubeb_utils.h"
15 #include <dlfcn.h>
16 #include <limits.h>
17 #include <math.h>
18 #include <pthread.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <jack/jack.h>
24 #include <jack/statistics.h>
25
26 #ifdef DISABLE_LIBJACK_DLOPEN
27 #define WRAP(x) x
28 #else
29 #define WRAP(x) (*api_##x)
30 #define JACK_API_VISIT(X) \
31 X(jack_activate) \
32 X(jack_client_close) \
33 X(jack_client_open) \
34 X(jack_connect) \
35 X(jack_free) \
36 X(jack_get_ports) \
37 X(jack_get_sample_rate) \
38 X(jack_get_xrun_delayed_usecs) \
39 X(jack_get_buffer_size) \
40 X(jack_port_get_buffer) \
41 X(jack_port_name) \
42 X(jack_port_register) \
43 X(jack_port_unregister) \
44 X(jack_port_get_latency_range) \
45 X(jack_set_process_callback) \
46 X(jack_set_xrun_callback) \
47 X(jack_set_graph_order_callback) \
48 X(jack_set_error_function) \
49 X(jack_set_info_function)
50
51 #define IMPORT_FUNC(x) static decltype(x) * api_##x;
52 JACK_API_VISIT(IMPORT_FUNC);
53 #undef IMPORT_FUNC
54 #endif
55
56 #define JACK_DEFAULT_IN "JACK capture"
57 #define JACK_DEFAULT_OUT "JACK playback"
58
59 static const int MAX_STREAMS = 16;
60 static const int MAX_CHANNELS = 8;
61 static const int FIFO_SIZE = 4096 * sizeof(float);
62
63 enum devstream {
64 NONE = 0,
65 IN_ONLY,
66 OUT_ONLY,
67 DUPLEX,
68 };
69
70 enum cbjack_connect_ports_options {
71 CBJACK_CP_OPTIONS_NONE = 0x0,
72 CBJACK_CP_OPTIONS_SKIP_OUTPUT = 0x1,
73 CBJACK_CP_OPTIONS_SKIP_INPUT = 0x2,
74 };
75
76 static void
s16ne_to_float(float * dst,const int16_t * src,size_t n)77 s16ne_to_float(float * dst, const int16_t * src, size_t n)
78 {
79 for (size_t i = 0; i < n; i++)
80 *(dst++) = (float)((float)*(src++) / 32767.0f);
81 }
82
83 static void
float_to_s16ne(int16_t * dst,float * src,size_t n)84 float_to_s16ne(int16_t * dst, float * src, size_t n)
85 {
86 for (size_t i = 0; i < n; i++) {
87 if (*src > 1.f)
88 *src = 1.f;
89 if (*src < -1.f)
90 *src = -1.f;
91 *(dst++) = (int16_t)((int16_t)(*(src++) * 32767));
92 }
93 }
94
95 extern "C" {
96 /*static*/ int
97 jack_init(cubeb ** context, char const * context_name);
98 }
99 static char const *
100 cbjack_get_backend_id(cubeb * context);
101 static int
102 cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
103 static int
104 cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params,
105 uint32_t * latency_frames);
106 static int
107 cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
108 static int
109 cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
110 static void
111 cbjack_destroy(cubeb * context);
112 static void
113 cbjack_interleave_capture(cubeb_stream * stream, float ** in,
114 jack_nframes_t nframes, bool format_mismatch);
115 static void
116 cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream,
117 short ** bufs_in, float ** bufs_out,
118 jack_nframes_t nframes);
119 static void
120 cbjack_deinterleave_playback_refill_float(cubeb_stream * stream,
121 float ** bufs_in, float ** bufs_out,
122 jack_nframes_t nframes);
123 static int
124 cbjack_stream_device_destroy(cubeb_stream * stream, cubeb_device * device);
125 static int
126 cbjack_stream_get_current_device(cubeb_stream * stm,
127 cubeb_device ** const device);
128 static int
129 cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
130 cubeb_device_collection * collection);
131 static int
132 cbjack_device_collection_destroy(cubeb * context,
133 cubeb_device_collection * collection);
134 static int
135 cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
136 char const * stream_name, cubeb_devid input_device,
137 cubeb_stream_params * input_stream_params,
138 cubeb_devid output_device,
139 cubeb_stream_params * output_stream_params,
140 unsigned int latency_frames,
141 cubeb_data_callback data_callback,
142 cubeb_state_callback state_callback, void * user_ptr);
143 static void
144 cbjack_stream_destroy(cubeb_stream * stream);
145 static int
146 cbjack_stream_start(cubeb_stream * stream);
147 static int
148 cbjack_stream_stop(cubeb_stream * stream);
149 static int
150 cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
151 static int
152 cbjack_stream_set_volume(cubeb_stream * stm, float volume);
153
154 static struct cubeb_ops const cbjack_ops = {
155 .init = jack_init,
156 .get_backend_id = cbjack_get_backend_id,
157 .get_max_channel_count = cbjack_get_max_channel_count,
158 .get_min_latency = cbjack_get_min_latency,
159 .get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
160 .enumerate_devices = cbjack_enumerate_devices,
161 .device_collection_destroy = cbjack_device_collection_destroy,
162 .destroy = cbjack_destroy,
163 .stream_init = cbjack_stream_init,
164 .stream_destroy = cbjack_stream_destroy,
165 .stream_start = cbjack_stream_start,
166 .stream_stop = cbjack_stream_stop,
167 .stream_get_position = cbjack_stream_get_position,
168 .stream_get_latency = cbjack_get_latency,
169 .stream_get_input_latency = NULL,
170 .stream_set_volume = cbjack_stream_set_volume,
171 .stream_set_name = NULL,
172 .stream_get_current_device = cbjack_stream_get_current_device,
173 .stream_device_destroy = cbjack_stream_device_destroy,
174 .stream_register_device_changed_callback = NULL,
175 .register_device_collection_changed = NULL};
176
177 struct cubeb_stream {
178 /* Note: Must match cubeb_stream layout in cubeb.c. */
179 cubeb * context;
180 void * user_ptr;
181 /**/
182
183 /**< Mutex for each stream */
184 pthread_mutex_t mutex;
185
186 bool in_use; /**< Set to false iff the stream is free */
187 bool ports_ready; /**< Set to true iff the JACK ports are ready */
188
189 cubeb_data_callback data_callback;
190 cubeb_state_callback state_callback;
191 cubeb_stream_params in_params;
192 cubeb_stream_params out_params;
193
194 cubeb_resampler * resampler;
195
196 uint64_t position;
197 bool pause;
198 float ratio;
199 enum devstream devs;
200 char stream_name[256];
201 jack_port_t * output_ports[MAX_CHANNELS];
202 jack_port_t * input_ports[MAX_CHANNELS];
203 float volume;
204 };
205
206 struct cubeb {
207 struct cubeb_ops const * ops;
208 void * libjack;
209
210 /**< Mutex for whole context */
211 pthread_mutex_t mutex;
212
213 /**< Audio buffers, converted to float */
214 float in_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS];
215 float out_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS];
216
217 /**< Audio buffer, at the sampling rate of the output */
218 float in_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3];
219 int16_t in_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3];
220 float out_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3];
221 int16_t out_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3];
222
223 cubeb_stream streams[MAX_STREAMS];
224 unsigned int active_streams;
225
226 cubeb_device_collection_changed_callback collection_changed_callback;
227
228 bool active;
229 unsigned int jack_sample_rate;
230 unsigned int jack_latency;
231 unsigned int jack_xruns;
232 unsigned int jack_buffer_size;
233 unsigned int fragment_size;
234 unsigned int output_bytes_per_frame;
235 jack_client_t * jack_client;
236 };
237
238 static int
load_jack_lib(cubeb * context)239 load_jack_lib(cubeb * context)
240 {
241 #ifndef DISABLE_LIBJACK_DLOPEN
242 #ifdef __APPLE__
243 context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY);
244 context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY);
245 #elif defined(__WIN32__)
246 #ifdef _WIN64
247 context->libjack = LoadLibrary("libjack64.dll");
248 #else
249 context->libjack = LoadLibrary("libjack.dll");
250 #endif
251 #else
252 context->libjack = dlopen("libjack.so.0", RTLD_LAZY);
253 if (!context->libjack) {
254 context->libjack = dlopen("libjack.so", RTLD_LAZY);
255 }
256 #endif
257 if (!context->libjack) {
258 return CUBEB_ERROR;
259 }
260
261 #define LOAD(x) \
262 { \
263 api_##x = (decltype(x) *)dlsym(context->libjack, #x); \
264 if (!api_##x) { \
265 dlclose(context->libjack); \
266 return CUBEB_ERROR; \
267 } \
268 }
269
270 JACK_API_VISIT(LOAD);
271 #undef LOAD
272 #endif
273 return CUBEB_OK;
274 }
275
276 static void
cbjack_connect_port_out(cubeb_stream * stream,const size_t out_port,const char * const phys_in_port)277 cbjack_connect_port_out(cubeb_stream * stream, const size_t out_port,
278 const char * const phys_in_port)
279 {
280 const char * src_port = WRAP(jack_port_name)(stream->output_ports[out_port]);
281
282 WRAP(jack_connect)(stream->context->jack_client, src_port, phys_in_port);
283 }
284
285 static void
cbjack_connect_port_in(cubeb_stream * stream,const char * const phys_out_port,size_t in_port)286 cbjack_connect_port_in(cubeb_stream * stream, const char * const phys_out_port,
287 size_t in_port)
288 {
289 const char * src_port = WRAP(jack_port_name)(stream->input_ports[in_port]);
290
291 WRAP(jack_connect)(stream->context->jack_client, phys_out_port, src_port);
292 }
293
294 static int
cbjack_connect_ports(cubeb_stream * stream,enum cbjack_connect_ports_options options)295 cbjack_connect_ports(cubeb_stream * stream,
296 enum cbjack_connect_ports_options options)
297 {
298 int r = CUBEB_ERROR;
299 const char ** phys_in_ports =
300 WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
301 JackPortIsInput | JackPortIsPhysical);
302 const char ** phys_out_ports =
303 WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
304 JackPortIsOutput | JackPortIsPhysical);
305
306 if (phys_in_ports == NULL || *phys_in_ports == NULL ||
307 options & CBJACK_CP_OPTIONS_SKIP_OUTPUT) {
308 goto skipplayback;
309 }
310
311 // Connect outputs to playback
312 for (unsigned int c = 0;
313 c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) {
314 cbjack_connect_port_out(stream, c, phys_in_ports[c]);
315 }
316
317 // Special case playing mono source in stereo
318 if (stream->out_params.channels == 1 && phys_in_ports[1] != NULL) {
319 cbjack_connect_port_out(stream, 0, phys_in_ports[1]);
320 }
321
322 r = CUBEB_OK;
323
324 skipplayback:
325 if (phys_out_ports == NULL || *phys_out_ports == NULL ||
326 options & CBJACK_CP_OPTIONS_SKIP_INPUT) {
327 goto end;
328 }
329 // Connect inputs to capture
330 for (unsigned int c = 0;
331 c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) {
332 cbjack_connect_port_in(stream, phys_out_ports[c], c);
333 }
334 r = CUBEB_OK;
335 end:
336 if (phys_out_ports) {
337 WRAP(jack_free)(phys_out_ports);
338 }
339 if (phys_in_ports) {
340 WRAP(jack_free)(phys_in_ports);
341 }
342 return r;
343 }
344
345 static int
cbjack_xrun_callback(void * arg)346 cbjack_xrun_callback(void * arg)
347 {
348 cubeb * ctx = (cubeb *)arg;
349
350 float delay = WRAP(jack_get_xrun_delayed_usecs)(ctx->jack_client);
351 float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) /
352 ctx->jack_buffer_size);
353
354 ctx->jack_xruns += (unsigned int)fragments;
355 return 0;
356 }
357
358 static int
cbjack_graph_order_callback(void * arg)359 cbjack_graph_order_callback(void * arg)
360 {
361 cubeb * ctx = (cubeb *)arg;
362 int i;
363 jack_latency_range_t latency_range;
364 jack_nframes_t port_latency, max_latency = 0;
365
366 for (int j = 0; j < MAX_STREAMS; j++) {
367 cubeb_stream * stm = &ctx->streams[j];
368
369 if (!stm->in_use)
370 continue;
371 if (!stm->ports_ready)
372 continue;
373
374 for (i = 0; i < (int)stm->out_params.channels; ++i) {
375 WRAP(jack_port_get_latency_range)
376 (stm->output_ports[i], JackPlaybackLatency, &latency_range);
377 port_latency = latency_range.max;
378 if (port_latency > max_latency)
379 max_latency = port_latency;
380 }
381 /* Cap minimum latency to 128 frames */
382 if (max_latency < 128)
383 max_latency = 128;
384 }
385
386 ctx->jack_latency = max_latency;
387
388 return 0;
389 }
390
391 static int
cbjack_process(jack_nframes_t nframes,void * arg)392 cbjack_process(jack_nframes_t nframes, void * arg)
393 {
394 cubeb * ctx = (cubeb *)arg;
395 unsigned int t_jack_xruns = ctx->jack_xruns;
396 int i;
397
398 ctx->jack_xruns = 0;
399
400 for (int j = 0; j < MAX_STREAMS; j++) {
401 cubeb_stream * stm = &ctx->streams[j];
402 float * bufs_out[stm->out_params.channels];
403 float * bufs_in[stm->in_params.channels];
404
405 if (!stm->in_use)
406 continue;
407
408 // handle xruns by skipping audio that should have been played
409 stm->position += t_jack_xruns * ctx->fragment_size * stm->ratio;
410
411 if (!stm->ports_ready)
412 continue;
413
414 if (stm->devs & OUT_ONLY) {
415 // get jack output buffers
416 for (i = 0; i < (int)stm->out_params.channels; i++)
417 bufs_out[i] =
418 (float *)WRAP(jack_port_get_buffer)(stm->output_ports[i], nframes);
419 }
420 if (stm->devs & IN_ONLY) {
421 // get jack input buffers
422 for (i = 0; i < (int)stm->in_params.channels; i++)
423 bufs_in[i] =
424 (float *)WRAP(jack_port_get_buffer)(stm->input_ports[i], nframes);
425 }
426 if (stm->pause) {
427 // paused, play silence on output
428 if (stm->devs & OUT_ONLY) {
429 for (unsigned int c = 0; c < stm->out_params.channels; c++) {
430 float * buffer_out = bufs_out[c];
431 for (long f = 0; f < nframes; f++) {
432 buffer_out[f] = 0.f;
433 }
434 }
435 }
436 if (stm->devs & IN_ONLY) {
437 // paused, capture silence
438 for (unsigned int c = 0; c < stm->in_params.channels; c++) {
439 float * buffer_in = bufs_in[c];
440 for (long f = 0; f < nframes; f++) {
441 buffer_in[f] = 0.f;
442 }
443 }
444 }
445 } else {
446
447 // try to lock stream mutex
448 if (pthread_mutex_trylock(&stm->mutex) == 0) {
449
450 int16_t * in_s16ne =
451 stm->context->in_resampled_interleaved_buffer_s16ne;
452 float * in_float = stm->context->in_resampled_interleaved_buffer_float;
453
454 // unpaused, play audio
455 if (stm->devs == DUPLEX) {
456 if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
457 cbjack_interleave_capture(stm, bufs_in, nframes, true);
458 cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out,
459 nframes);
460 } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
461 cbjack_interleave_capture(stm, bufs_in, nframes, false);
462 cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out,
463 nframes);
464 }
465 } else if (stm->devs == IN_ONLY) {
466 if (stm->in_params.format == CUBEB_SAMPLE_S16NE) {
467 cbjack_interleave_capture(stm, bufs_in, nframes, true);
468 cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr,
469 nframes);
470 } else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
471 cbjack_interleave_capture(stm, bufs_in, nframes, false);
472 cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr,
473 nframes);
474 }
475 } else if (stm->devs == OUT_ONLY) {
476 if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
477 cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out,
478 nframes);
479 } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
480 cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out,
481 nframes);
482 }
483 }
484 // unlock stream mutex
485 pthread_mutex_unlock(&stm->mutex);
486
487 } else {
488 // could not lock mutex
489 // output silence
490 if (stm->devs & OUT_ONLY) {
491 for (unsigned int c = 0; c < stm->out_params.channels; c++) {
492 float * buffer_out = bufs_out[c];
493 for (long f = 0; f < nframes; f++) {
494 buffer_out[f] = 0.f;
495 }
496 }
497 }
498 if (stm->devs & IN_ONLY) {
499 // capture silence
500 for (unsigned int c = 0; c < stm->in_params.channels; c++) {
501 float * buffer_in = bufs_in[c];
502 for (long f = 0; f < nframes; f++) {
503 buffer_in[f] = 0.f;
504 }
505 }
506 }
507 }
508 }
509 }
510 return 0;
511 }
512
513 static void
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream,float ** in,float ** bufs_out,jack_nframes_t nframes)514 cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in,
515 float ** bufs_out,
516 jack_nframes_t nframes)
517 {
518 float * out_interleaved_buffer = nullptr;
519
520 float * inptr = (in != NULL) ? *in : nullptr;
521 float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr;
522
523 long needed_frames = (bufs_out != NULL) ? nframes : 0;
524 long done_frames = 0;
525 long input_frames_count = (in != NULL) ? nframes : 0;
526
527 done_frames = cubeb_resampler_fill(
528 stream->resampler, inptr, &input_frames_count,
529 (bufs_out != NULL)
530 ? stream->context->out_resampled_interleaved_buffer_float
531 : NULL,
532 needed_frames);
533
534 out_interleaved_buffer =
535 stream->context->out_resampled_interleaved_buffer_float;
536
537 if (outptr) {
538 // convert interleaved output buffers to contiguous buffers
539 for (unsigned int c = 0; c < stream->out_params.channels; c++) {
540 float * buffer = bufs_out[c];
541 for (long f = 0; f < done_frames; f++) {
542 buffer[f] =
543 out_interleaved_buffer[(f * stream->out_params.channels) + c] *
544 stream->volume;
545 }
546 if (done_frames < needed_frames) {
547 // draining
548 for (long f = done_frames; f < needed_frames; f++) {
549 buffer[f] = 0.f;
550 }
551 }
552 if (done_frames == 0) {
553 // stop, but first zero out the existing buffer
554 for (long f = 0; f < needed_frames; f++) {
555 buffer[f] = 0.f;
556 }
557 }
558 }
559 }
560
561 if (done_frames >= 0 && done_frames < needed_frames) {
562 // set drained
563 stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
564 // stop stream
565 cbjack_stream_stop(stream);
566 }
567 if (done_frames > 0 && done_frames <= needed_frames) {
568 // advance stream position
569 stream->position += done_frames * stream->ratio;
570 }
571 if (done_frames < 0 || done_frames > needed_frames) {
572 // stream error
573 stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR);
574 }
575 }
576
577 static void
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream,short ** in,float ** bufs_out,jack_nframes_t nframes)578 cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in,
579 float ** bufs_out,
580 jack_nframes_t nframes)
581 {
582 float * out_interleaved_buffer = nullptr;
583
584 short * inptr = (in != NULL) ? *in : nullptr;
585 float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr;
586
587 long needed_frames = (bufs_out != NULL) ? nframes : 0;
588 long done_frames = 0;
589 long input_frames_count = (in != NULL) ? nframes : 0;
590
591 done_frames = cubeb_resampler_fill(
592 stream->resampler, inptr, &input_frames_count,
593 (bufs_out != NULL)
594 ? stream->context->out_resampled_interleaved_buffer_s16ne
595 : NULL,
596 needed_frames);
597
598 s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float,
599 stream->context->out_resampled_interleaved_buffer_s16ne,
600 done_frames * stream->out_params.channels);
601
602 out_interleaved_buffer =
603 stream->context->out_resampled_interleaved_buffer_float;
604
605 if (outptr) {
606 // convert interleaved output buffers to contiguous buffers
607 for (unsigned int c = 0; c < stream->out_params.channels; c++) {
608 float * buffer = bufs_out[c];
609 for (long f = 0; f < done_frames; f++) {
610 buffer[f] =
611 out_interleaved_buffer[(f * stream->out_params.channels) + c] *
612 stream->volume;
613 }
614 if (done_frames < needed_frames) {
615 // draining
616 for (long f = done_frames; f < needed_frames; f++) {
617 buffer[f] = 0.f;
618 }
619 }
620 if (done_frames == 0) {
621 // stop, but first zero out the existing buffer
622 for (long f = 0; f < needed_frames; f++) {
623 buffer[f] = 0.f;
624 }
625 }
626 }
627 }
628
629 if (done_frames >= 0 && done_frames < needed_frames) {
630 // set drained
631 stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
632 // stop stream
633 cbjack_stream_stop(stream);
634 }
635 if (done_frames > 0 && done_frames <= needed_frames) {
636 // advance stream position
637 stream->position += done_frames * stream->ratio;
638 }
639 if (done_frames < 0 || done_frames > needed_frames) {
640 // stream error
641 stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR);
642 }
643 }
644
645 static void
cbjack_interleave_capture(cubeb_stream * stream,float ** in,jack_nframes_t nframes,bool format_mismatch)646 cbjack_interleave_capture(cubeb_stream * stream, float ** in,
647 jack_nframes_t nframes, bool format_mismatch)
648 {
649 float * in_buffer = stream->context->in_float_interleaved_buffer;
650
651 for (unsigned int c = 0; c < stream->in_params.channels; c++) {
652 for (long f = 0; f < nframes; f++) {
653 in_buffer[(f * stream->in_params.channels) + c] =
654 in[c][f] * stream->volume;
655 }
656 }
657 if (format_mismatch) {
658 float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne,
659 in_buffer, nframes * stream->in_params.channels);
660 } else {
661 memset(stream->context->in_resampled_interleaved_buffer_float, 0,
662 (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float));
663 memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer,
664 (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float));
665 }
666 }
667
668 static void
silent_jack_error_callback(char const *)669 silent_jack_error_callback(char const * /*msg*/)
670 {
671 }
672
673 /*static*/ int
jack_init(cubeb ** context,char const * context_name)674 jack_init(cubeb ** context, char const * context_name)
675 {
676 int r;
677
678 *context = NULL;
679
680 cubeb * ctx = (cubeb *)calloc(1, sizeof(*ctx));
681 if (ctx == NULL) {
682 return CUBEB_ERROR;
683 }
684
685 r = load_jack_lib(ctx);
686 if (r != 0) {
687 cbjack_destroy(ctx);
688 return CUBEB_ERROR;
689 }
690
691 WRAP(jack_set_error_function)(silent_jack_error_callback);
692 WRAP(jack_set_info_function)(silent_jack_error_callback);
693
694 ctx->ops = &cbjack_ops;
695
696 ctx->mutex = PTHREAD_MUTEX_INITIALIZER;
697 for (r = 0; r < MAX_STREAMS; r++) {
698 ctx->streams[r].mutex = PTHREAD_MUTEX_INITIALIZER;
699 }
700
701 const char * jack_client_name = "cubeb";
702 if (context_name)
703 jack_client_name = context_name;
704
705 ctx->jack_client =
706 WRAP(jack_client_open)(jack_client_name, JackNoStartServer, NULL);
707
708 if (ctx->jack_client == NULL) {
709 cbjack_destroy(ctx);
710 return CUBEB_ERROR;
711 }
712
713 ctx->jack_xruns = 0;
714
715 WRAP(jack_set_process_callback)(ctx->jack_client, cbjack_process, ctx);
716 WRAP(jack_set_xrun_callback)(ctx->jack_client, cbjack_xrun_callback, ctx);
717 WRAP(jack_set_graph_order_callback)
718 (ctx->jack_client, cbjack_graph_order_callback, ctx);
719
720 if (WRAP(jack_activate)(ctx->jack_client)) {
721 cbjack_destroy(ctx);
722 return CUBEB_ERROR;
723 }
724
725 ctx->jack_sample_rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
726 ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate;
727
728 ctx->active = true;
729 *context = ctx;
730
731 return CUBEB_OK;
732 }
733
734 static char const *
cbjack_get_backend_id(cubeb *)735 cbjack_get_backend_id(cubeb * /*context*/)
736 {
737 return "jack";
738 }
739
740 static int
cbjack_get_max_channel_count(cubeb *,uint32_t * max_channels)741 cbjack_get_max_channel_count(cubeb * /*ctx*/, uint32_t * max_channels)
742 {
743 *max_channels = MAX_CHANNELS;
744 return CUBEB_OK;
745 }
746
747 static int
cbjack_get_latency(cubeb_stream * stm,unsigned int * latency_ms)748 cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms)
749 {
750 *latency_ms = stm->context->jack_latency;
751 return CUBEB_OK;
752 }
753
754 static int
cbjack_get_min_latency(cubeb * ctx,cubeb_stream_params,uint32_t * latency_ms)755 cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/,
756 uint32_t * latency_ms)
757 {
758 *latency_ms = ctx->jack_latency;
759 return CUBEB_OK;
760 }
761
762 static int
cbjack_get_preferred_sample_rate(cubeb * ctx,uint32_t * rate)763 cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
764 {
765 if (!ctx->jack_client) {
766 jack_client_t * testclient =
767 WRAP(jack_client_open)("test-samplerate", JackNoStartServer, NULL);
768 if (!testclient) {
769 return CUBEB_ERROR;
770 }
771
772 *rate = WRAP(jack_get_sample_rate)(testclient);
773 WRAP(jack_client_close)(testclient);
774
775 } else {
776 *rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
777 }
778 return CUBEB_OK;
779 }
780
781 static void
cbjack_destroy(cubeb * context)782 cbjack_destroy(cubeb * context)
783 {
784 context->active = false;
785
786 if (context->jack_client != NULL)
787 WRAP(jack_client_close)(context->jack_client);
788
789 if (context->libjack)
790 dlclose(context->libjack);
791
792 free(context);
793 }
794
795 static cubeb_stream *
context_alloc_stream(cubeb * context,char const * stream_name)796 context_alloc_stream(cubeb * context, char const * stream_name)
797 {
798 for (int i = 0; i < MAX_STREAMS; i++) {
799 if (!context->streams[i].in_use) {
800 cubeb_stream * stm = &context->streams[i];
801 stm->in_use = true;
802 snprintf(stm->stream_name, 255, "%s_%u", stream_name, i);
803 return stm;
804 }
805 }
806 return NULL;
807 }
808
809 static int
cbjack_stream_init(cubeb * context,cubeb_stream ** stream,char const * stream_name,cubeb_devid input_device,cubeb_stream_params * input_stream_params,cubeb_devid output_device,cubeb_stream_params * output_stream_params,unsigned int,cubeb_data_callback data_callback,cubeb_state_callback state_callback,void * user_ptr)810 cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
811 char const * stream_name, cubeb_devid input_device,
812 cubeb_stream_params * input_stream_params,
813 cubeb_devid output_device,
814 cubeb_stream_params * output_stream_params,
815 unsigned int /*latency_frames*/,
816 cubeb_data_callback data_callback,
817 cubeb_state_callback state_callback, void * user_ptr)
818 {
819 int stream_actual_rate = 0;
820 int jack_rate = WRAP(jack_get_sample_rate)(context->jack_client);
821
822 if (output_stream_params &&
823 (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
824 output_stream_params->format != CUBEB_SAMPLE_S16NE)) {
825 return CUBEB_ERROR_INVALID_FORMAT;
826 }
827
828 if (input_stream_params &&
829 (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
830 input_stream_params->format != CUBEB_SAMPLE_S16NE)) {
831 return CUBEB_ERROR_INVALID_FORMAT;
832 }
833
834 if ((input_device && input_device != JACK_DEFAULT_IN) ||
835 (output_device && output_device != JACK_DEFAULT_OUT)) {
836 return CUBEB_ERROR_NOT_SUPPORTED;
837 }
838
839 // Loopback is unsupported
840 if ((input_stream_params &&
841 (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
842 (output_stream_params &&
843 (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
844 return CUBEB_ERROR_NOT_SUPPORTED;
845 }
846
847 *stream = NULL;
848
849 // Find a free stream.
850 pthread_mutex_lock(&context->mutex);
851 cubeb_stream * stm = context_alloc_stream(context, stream_name);
852
853 // No free stream?
854 if (stm == NULL) {
855 pthread_mutex_unlock(&context->mutex);
856 return CUBEB_ERROR;
857 }
858
859 // unlock context mutex
860 pthread_mutex_unlock(&context->mutex);
861
862 // Lock active stream
863 pthread_mutex_lock(&stm->mutex);
864
865 stm->ports_ready = false;
866 stm->user_ptr = user_ptr;
867 stm->context = context;
868 stm->devs = NONE;
869 if (output_stream_params && !input_stream_params) {
870 stm->out_params = *output_stream_params;
871 stream_actual_rate = stm->out_params.rate;
872 stm->out_params.rate = jack_rate;
873 stm->devs = OUT_ONLY;
874 if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
875 context->output_bytes_per_frame = sizeof(float);
876 } else {
877 context->output_bytes_per_frame = sizeof(short);
878 }
879 }
880 if (input_stream_params && output_stream_params) {
881 stm->in_params = *input_stream_params;
882 stm->out_params = *output_stream_params;
883 stream_actual_rate = stm->out_params.rate;
884 stm->in_params.rate = jack_rate;
885 stm->out_params.rate = jack_rate;
886 stm->devs = DUPLEX;
887 if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
888 context->output_bytes_per_frame = sizeof(float);
889 stm->in_params.format = CUBEB_SAMPLE_FLOAT32NE;
890 } else {
891 context->output_bytes_per_frame = sizeof(short);
892 stm->in_params.format = CUBEB_SAMPLE_S16NE;
893 }
894 } else if (input_stream_params && !output_stream_params) {
895 stm->in_params = *input_stream_params;
896 stream_actual_rate = stm->in_params.rate;
897 stm->in_params.rate = jack_rate;
898 stm->devs = IN_ONLY;
899 if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
900 context->output_bytes_per_frame = sizeof(float);
901 } else {
902 context->output_bytes_per_frame = sizeof(short);
903 }
904 }
905
906 stm->ratio = (float)stream_actual_rate / (float)jack_rate;
907
908 stm->data_callback = data_callback;
909 stm->state_callback = state_callback;
910 stm->position = 0;
911 stm->volume = 1.0f;
912 context->jack_buffer_size = WRAP(jack_get_buffer_size)(context->jack_client);
913 context->fragment_size = context->jack_buffer_size;
914
915 if (stm->devs == NONE) {
916 pthread_mutex_unlock(&stm->mutex);
917 return CUBEB_ERROR;
918 }
919
920 stm->resampler = NULL;
921
922 if (stm->devs == DUPLEX) {
923 stm->resampler = cubeb_resampler_create(
924 stm, &stm->in_params, &stm->out_params, stream_actual_rate,
925 stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP);
926 } else if (stm->devs == IN_ONLY) {
927 stm->resampler = cubeb_resampler_create(
928 stm, &stm->in_params, nullptr, stream_actual_rate, stm->data_callback,
929 stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP);
930 } else if (stm->devs == OUT_ONLY) {
931 stm->resampler = cubeb_resampler_create(
932 stm, nullptr, &stm->out_params, stream_actual_rate, stm->data_callback,
933 stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP);
934 }
935
936 if (!stm->resampler) {
937 stm->in_use = false;
938 pthread_mutex_unlock(&stm->mutex);
939 return CUBEB_ERROR;
940 }
941
942 if (stm->devs == DUPLEX || stm->devs == OUT_ONLY) {
943 for (unsigned int c = 0; c < stm->out_params.channels; c++) {
944 char portname[256];
945 snprintf(portname, 255, "%s_out_%d", stm->stream_name, c);
946 stm->output_ports[c] = WRAP(jack_port_register)(
947 stm->context->jack_client, portname, JACK_DEFAULT_AUDIO_TYPE,
948 JackPortIsOutput, 0);
949 if (!(output_stream_params->prefs &
950 CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
951 if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_INPUT) !=
952 CUBEB_OK) {
953 pthread_mutex_unlock(&stm->mutex);
954 cbjack_stream_destroy(stm);
955 return CUBEB_ERROR;
956 }
957 }
958 }
959 }
960
961 if (stm->devs == DUPLEX || stm->devs == IN_ONLY) {
962 for (unsigned int c = 0; c < stm->in_params.channels; c++) {
963 char portname[256];
964 snprintf(portname, 255, "%s_in_%d", stm->stream_name, c);
965 stm->input_ports[c] =
966 WRAP(jack_port_register)(stm->context->jack_client, portname,
967 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
968 if (!(input_stream_params->prefs &
969 CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
970 if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_OUTPUT) !=
971 CUBEB_OK) {
972 pthread_mutex_unlock(&stm->mutex);
973 cbjack_stream_destroy(stm);
974 return CUBEB_ERROR;
975 }
976 }
977 }
978 }
979
980 *stream = stm;
981
982 stm->ports_ready = true;
983 stm->pause = true;
984 pthread_mutex_unlock(&stm->mutex);
985
986 return CUBEB_OK;
987 }
988
989 static void
cbjack_stream_destroy(cubeb_stream * stream)990 cbjack_stream_destroy(cubeb_stream * stream)
991 {
992 pthread_mutex_lock(&stream->mutex);
993 stream->ports_ready = false;
994
995 if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) {
996 for (unsigned int c = 0; c < stream->out_params.channels; c++) {
997 if (stream->output_ports[c]) {
998 WRAP(jack_port_unregister)
999 (stream->context->jack_client, stream->output_ports[c]);
1000 stream->output_ports[c] = NULL;
1001 }
1002 }
1003 }
1004
1005 if (stream->devs == DUPLEX || stream->devs == IN_ONLY) {
1006 for (unsigned int c = 0; c < stream->in_params.channels; c++) {
1007 if (stream->input_ports[c]) {
1008 WRAP(jack_port_unregister)
1009 (stream->context->jack_client, stream->input_ports[c]);
1010 stream->input_ports[c] = NULL;
1011 }
1012 }
1013 }
1014
1015 if (stream->resampler) {
1016 cubeb_resampler_destroy(stream->resampler);
1017 stream->resampler = NULL;
1018 }
1019 stream->in_use = false;
1020 pthread_mutex_unlock(&stream->mutex);
1021 }
1022
1023 static int
cbjack_stream_start(cubeb_stream * stream)1024 cbjack_stream_start(cubeb_stream * stream)
1025 {
1026 stream->pause = false;
1027 stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED);
1028 return CUBEB_OK;
1029 }
1030
1031 static int
cbjack_stream_stop(cubeb_stream * stream)1032 cbjack_stream_stop(cubeb_stream * stream)
1033 {
1034 stream->pause = true;
1035 stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED);
1036 return CUBEB_OK;
1037 }
1038
1039 static int
cbjack_stream_get_position(cubeb_stream * stream,uint64_t * position)1040 cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position)
1041 {
1042 *position = stream->position;
1043 return CUBEB_OK;
1044 }
1045
1046 static int
cbjack_stream_set_volume(cubeb_stream * stm,float volume)1047 cbjack_stream_set_volume(cubeb_stream * stm, float volume)
1048 {
1049 stm->volume = volume;
1050 return CUBEB_OK;
1051 }
1052
1053 static int
cbjack_stream_get_current_device(cubeb_stream * stm,cubeb_device ** const device)1054 cbjack_stream_get_current_device(cubeb_stream * stm,
1055 cubeb_device ** const device)
1056 {
1057 *device = (cubeb_device *)calloc(1, sizeof(cubeb_device));
1058 if (*device == NULL)
1059 return CUBEB_ERROR;
1060
1061 const char * j_in = JACK_DEFAULT_IN;
1062 const char * j_out = JACK_DEFAULT_OUT;
1063 const char * empty = "";
1064
1065 if (stm->devs == DUPLEX) {
1066 (*device)->input_name = strdup(j_in);
1067 (*device)->output_name = strdup(j_out);
1068 } else if (stm->devs == IN_ONLY) {
1069 (*device)->input_name = strdup(j_in);
1070 (*device)->output_name = strdup(empty);
1071 } else if (stm->devs == OUT_ONLY) {
1072 (*device)->input_name = strdup(empty);
1073 (*device)->output_name = strdup(j_out);
1074 }
1075
1076 return CUBEB_OK;
1077 }
1078
1079 static int
cbjack_stream_device_destroy(cubeb_stream *,cubeb_device * device)1080 cbjack_stream_device_destroy(cubeb_stream * /*stream*/, cubeb_device * device)
1081 {
1082 if (device->input_name)
1083 free(device->input_name);
1084 if (device->output_name)
1085 free(device->output_name);
1086 free(device);
1087 return CUBEB_OK;
1088 }
1089
1090 static int
cbjack_enumerate_devices(cubeb * context,cubeb_device_type type,cubeb_device_collection * collection)1091 cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
1092 cubeb_device_collection * collection)
1093 {
1094 if (!context)
1095 return CUBEB_ERROR;
1096
1097 uint32_t rate;
1098 cbjack_get_preferred_sample_rate(context, &rate);
1099
1100 cubeb_device_info * devices = new cubeb_device_info[2];
1101 if (!devices)
1102 return CUBEB_ERROR;
1103 PodZero(devices, 2);
1104 collection->count = 0;
1105
1106 if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
1107 cubeb_device_info * cur = &devices[collection->count];
1108 cur->device_id = JACK_DEFAULT_OUT;
1109 cur->devid = (cubeb_devid)cur->device_id;
1110 cur->friendly_name = JACK_DEFAULT_OUT;
1111 cur->group_id = JACK_DEFAULT_OUT;
1112 cur->vendor_name = JACK_DEFAULT_OUT;
1113 cur->type = CUBEB_DEVICE_TYPE_OUTPUT;
1114 cur->state = CUBEB_DEVICE_STATE_ENABLED;
1115 cur->preferred = CUBEB_DEVICE_PREF_ALL;
1116 cur->format = CUBEB_DEVICE_FMT_F32NE;
1117 cur->default_format = CUBEB_DEVICE_FMT_F32NE;
1118 cur->max_channels = MAX_CHANNELS;
1119 cur->min_rate = rate;
1120 cur->max_rate = rate;
1121 cur->default_rate = rate;
1122 cur->latency_lo = 0;
1123 cur->latency_hi = 0;
1124 collection->count += 1;
1125 }
1126
1127 if (type & CUBEB_DEVICE_TYPE_INPUT) {
1128 cubeb_device_info * cur = &devices[collection->count];
1129 cur->device_id = JACK_DEFAULT_IN;
1130 cur->devid = (cubeb_devid)cur->device_id;
1131 cur->friendly_name = JACK_DEFAULT_IN;
1132 cur->group_id = JACK_DEFAULT_IN;
1133 cur->vendor_name = JACK_DEFAULT_IN;
1134 cur->type = CUBEB_DEVICE_TYPE_INPUT;
1135 cur->state = CUBEB_DEVICE_STATE_ENABLED;
1136 cur->preferred = CUBEB_DEVICE_PREF_ALL;
1137 cur->format = CUBEB_DEVICE_FMT_F32NE;
1138 cur->default_format = CUBEB_DEVICE_FMT_F32NE;
1139 cur->max_channels = MAX_CHANNELS;
1140 cur->min_rate = rate;
1141 cur->max_rate = rate;
1142 cur->default_rate = rate;
1143 cur->latency_lo = 0;
1144 cur->latency_hi = 0;
1145 collection->count += 1;
1146 }
1147
1148 collection->device = devices;
1149
1150 return CUBEB_OK;
1151 }
1152
1153 static int
cbjack_device_collection_destroy(cubeb *,cubeb_device_collection * collection)1154 cbjack_device_collection_destroy(cubeb * /*ctx*/,
1155 cubeb_device_collection * collection)
1156 {
1157 XASSERT(collection);
1158 delete[] collection->device;
1159 return CUBEB_OK;
1160 }
1161