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