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