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