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