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