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