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