Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/cubeb/src/cubeb.c
4246 views
1
/*
2
* Copyright © 2013 Mozilla Foundation
3
*
4
* This program is made available under an ISC-style license. See the
5
* accompanying file LICENSE for details.
6
*/
7
#undef NDEBUG
8
#include "cubeb/cubeb.h"
9
#include "cubeb-internal.h"
10
#include <assert.h>
11
#include <stddef.h>
12
#include <stdlib.h>
13
#include <string.h>
14
15
#define NELEMS(x) ((int)(sizeof(x) / sizeof(x[0])))
16
17
struct cubeb {
18
struct cubeb_ops * ops;
19
};
20
21
struct cubeb_stream {
22
/*
23
* Note: All implementations of cubeb_stream must keep the following
24
* layout.
25
*/
26
struct cubeb * context;
27
void * user_ptr;
28
};
29
30
#if defined(USE_PULSE)
31
int
32
pulse_init(cubeb ** context, char const * context_name);
33
#endif
34
#if defined(USE_JACK)
35
int
36
jack_init(cubeb ** context, char const * context_name);
37
#endif
38
#if defined(USE_ALSA)
39
int
40
alsa_init(cubeb ** context, char const * context_name);
41
#endif
42
#if defined(USE_AUDIOUNIT)
43
int
44
audiounit_init(cubeb ** context, char const * context_name);
45
#endif
46
#if defined(USE_WINMM)
47
int
48
winmm_init(cubeb ** context, char const * context_name);
49
#endif
50
#if defined(USE_WASAPI)
51
int
52
wasapi_init(cubeb ** context, char const * context_name);
53
#endif
54
#if defined(USE_SNDIO)
55
int
56
sndio_init(cubeb ** context, char const * context_name);
57
#endif
58
#if defined(USE_OSS)
59
int
60
oss_init(cubeb ** context, char const * context_name);
61
#endif
62
63
static int
64
validate_stream_params(cubeb_stream_params * input_stream_params,
65
cubeb_stream_params * output_stream_params)
66
{
67
XASSERT(input_stream_params || output_stream_params);
68
if (output_stream_params) {
69
if (output_stream_params->rate < 1000 ||
70
output_stream_params->rate > 768000 ||
71
output_stream_params->channels < 1 ||
72
output_stream_params->channels > UINT8_MAX) {
73
return CUBEB_ERROR_INVALID_FORMAT;
74
}
75
}
76
if (input_stream_params) {
77
if (input_stream_params->rate < 1000 ||
78
input_stream_params->rate > 768000 ||
79
input_stream_params->channels < 1 ||
80
input_stream_params->channels > UINT8_MAX) {
81
return CUBEB_ERROR_INVALID_FORMAT;
82
}
83
}
84
// Rate and sample format must be the same for input and output, if using a
85
// duplex stream
86
if (input_stream_params && output_stream_params) {
87
if (input_stream_params->rate != output_stream_params->rate ||
88
input_stream_params->format != output_stream_params->format) {
89
return CUBEB_ERROR_INVALID_FORMAT;
90
}
91
}
92
93
cubeb_stream_params * params =
94
input_stream_params ? input_stream_params : output_stream_params;
95
96
switch (params->format) {
97
case CUBEB_SAMPLE_S16LE:
98
case CUBEB_SAMPLE_S16BE:
99
case CUBEB_SAMPLE_FLOAT32LE:
100
case CUBEB_SAMPLE_FLOAT32BE:
101
return CUBEB_OK;
102
}
103
104
return CUBEB_ERROR_INVALID_FORMAT;
105
}
106
107
static int
108
validate_latency(int latency)
109
{
110
if (latency < 1 || latency > 96000) {
111
return CUBEB_ERROR_INVALID_PARAMETER;
112
}
113
return CUBEB_OK;
114
}
115
116
int
117
cubeb_init(cubeb ** context, char const * context_name,
118
char const * backend_name)
119
{
120
int (*init_oneshot)(cubeb **, char const *) = NULL;
121
122
if (backend_name != NULL) {
123
if (!strcmp(backend_name, "pulse")) {
124
#if defined(USE_PULSE)
125
init_oneshot = pulse_init;
126
#endif
127
} else if (!strcmp(backend_name, "jack")) {
128
#if defined(USE_JACK)
129
init_oneshot = jack_init;
130
#endif
131
} else if (!strcmp(backend_name, "alsa")) {
132
#if defined(USE_ALSA)
133
init_oneshot = alsa_init;
134
#endif
135
} else if (!strcmp(backend_name, "audiounit")) {
136
#if defined(USE_AUDIOUNIT)
137
init_oneshot = audiounit_init;
138
#endif
139
} else if (!strcmp(backend_name, "wasapi")) {
140
#if defined(USE_WASAPI)
141
init_oneshot = wasapi_init;
142
#endif
143
} else if (!strcmp(backend_name, "winmm")) {
144
#if defined(USE_WINMM)
145
init_oneshot = winmm_init;
146
#endif
147
} else if (!strcmp(backend_name, "sndio")) {
148
#if defined(USE_SNDIO)
149
init_oneshot = sndio_init;
150
#endif
151
} else if (!strcmp(backend_name, "oss")) {
152
#if defined(USE_OSS)
153
init_oneshot = oss_init;
154
#endif
155
} else {
156
/* Already set */
157
}
158
}
159
160
int (*default_init[])(cubeb **, char const *) = {
161
/*
162
* init_oneshot must be at the top to allow user
163
* to override all other choices
164
*/
165
init_oneshot,
166
#if defined(USE_PULSE)
167
pulse_init,
168
#endif
169
#if defined(USE_JACK)
170
jack_init,
171
#endif
172
#if defined(USE_SNDIO)
173
sndio_init,
174
#endif
175
#if defined(USE_ALSA)
176
alsa_init,
177
#endif
178
#if defined(USE_OSS)
179
oss_init,
180
#endif
181
#if defined(USE_AUDIOUNIT)
182
audiounit_init,
183
#endif
184
#if defined(USE_WASAPI)
185
wasapi_init,
186
#endif
187
#if defined(USE_WINMM)
188
winmm_init,
189
#endif
190
#if defined(USE_SUN)
191
sun_init,
192
#endif
193
};
194
int i;
195
196
if (!context) {
197
return CUBEB_ERROR_INVALID_PARAMETER;
198
}
199
200
#define OK(fn) assert((*context)->ops->fn)
201
for (i = 0; i < NELEMS(default_init); ++i) {
202
if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) {
203
/* Assert that the minimal API is implemented. */
204
OK(get_backend_id);
205
OK(destroy);
206
OK(stream_init);
207
OK(stream_destroy);
208
OK(stream_start);
209
OK(stream_stop);
210
OK(stream_get_position);
211
return CUBEB_OK;
212
}
213
}
214
return CUBEB_ERROR;
215
}
216
217
const char**
218
cubeb_get_backend_names()
219
{
220
static const char* backend_names[] = {
221
#if defined(USE_PULSE)
222
"pulse",
223
#endif
224
#if defined(USE_JACK)
225
"jack",
226
#endif
227
#if defined(USE_ALSA)
228
"alsa",
229
#endif
230
#if defined(USE_AUDIOUNIT)
231
"audiounit",
232
#endif
233
#if defined(USE_WASAPI)
234
"wasapi",
235
#endif
236
#if defined(USE_WINMM)
237
"winmm",
238
#endif
239
#if defined(USE_SNDIO)
240
"sndio",
241
#endif
242
#if defined(USE_OSS)
243
"oss",
244
#endif
245
NULL,
246
};
247
248
return backend_names;
249
}
250
251
char const *
252
cubeb_get_backend_id(cubeb * context)
253
{
254
if (!context) {
255
return NULL;
256
}
257
258
return context->ops->get_backend_id(context);
259
}
260
261
int
262
cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
263
{
264
if (!context || !max_channels) {
265
return CUBEB_ERROR_INVALID_PARAMETER;
266
}
267
268
if (!context->ops->get_max_channel_count) {
269
return CUBEB_ERROR_NOT_SUPPORTED;
270
}
271
272
return context->ops->get_max_channel_count(context, max_channels);
273
}
274
275
int
276
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params,
277
uint32_t * latency_ms)
278
{
279
if (!context || !params || !latency_ms) {
280
return CUBEB_ERROR_INVALID_PARAMETER;
281
}
282
283
if (!context->ops->get_min_latency) {
284
return CUBEB_ERROR_NOT_SUPPORTED;
285
}
286
287
return context->ops->get_min_latency(context, *params, latency_ms);
288
}
289
290
int
291
cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
292
{
293
if (!context || !rate) {
294
return CUBEB_ERROR_INVALID_PARAMETER;
295
}
296
297
if (!context->ops->get_preferred_sample_rate) {
298
return CUBEB_ERROR_NOT_SUPPORTED;
299
}
300
301
return context->ops->get_preferred_sample_rate(context, rate);
302
}
303
304
int
305
cubeb_get_supported_input_processing_params(
306
cubeb * context, cubeb_input_processing_params * params)
307
{
308
if (!context || !params) {
309
return CUBEB_ERROR_INVALID_PARAMETER;
310
}
311
312
if (!context->ops->get_supported_input_processing_params) {
313
return CUBEB_ERROR_NOT_SUPPORTED;
314
}
315
316
return context->ops->get_supported_input_processing_params(context, params);
317
}
318
319
void
320
cubeb_destroy(cubeb * context)
321
{
322
if (!context) {
323
return;
324
}
325
326
context->ops->destroy(context);
327
328
cubeb_set_log_callback(CUBEB_LOG_DISABLED, NULL);
329
}
330
331
int
332
cubeb_stream_init(cubeb * context, cubeb_stream ** stream,
333
char const * stream_name, cubeb_devid input_device,
334
cubeb_stream_params * input_stream_params,
335
cubeb_devid output_device,
336
cubeb_stream_params * output_stream_params,
337
unsigned int latency, cubeb_data_callback data_callback,
338
cubeb_state_callback state_callback, void * user_ptr)
339
{
340
int r;
341
342
if (!context || !stream || !data_callback || !state_callback) {
343
return CUBEB_ERROR_INVALID_PARAMETER;
344
}
345
346
if ((r = validate_stream_params(input_stream_params, output_stream_params)) !=
347
CUBEB_OK ||
348
(r = validate_latency(latency)) != CUBEB_OK) {
349
return r;
350
}
351
352
r = context->ops->stream_init(context, stream, stream_name, input_device,
353
input_stream_params, output_device,
354
output_stream_params, latency, data_callback,
355
state_callback, user_ptr);
356
357
if (r == CUBEB_ERROR_INVALID_FORMAT) {
358
LOG("Invalid format, %p %p %d %d", output_stream_params,
359
input_stream_params,
360
output_stream_params && output_stream_params->format,
361
input_stream_params && input_stream_params->format);
362
}
363
364
return r;
365
}
366
367
void
368
cubeb_stream_destroy(cubeb_stream * stream)
369
{
370
if (!stream) {
371
return;
372
}
373
374
stream->context->ops->stream_destroy(stream);
375
}
376
377
int
378
cubeb_stream_start(cubeb_stream * stream)
379
{
380
if (!stream) {
381
return CUBEB_ERROR_INVALID_PARAMETER;
382
}
383
384
return stream->context->ops->stream_start(stream);
385
}
386
387
int
388
cubeb_stream_stop(cubeb_stream * stream)
389
{
390
if (!stream) {
391
return CUBEB_ERROR_INVALID_PARAMETER;
392
}
393
394
return stream->context->ops->stream_stop(stream);
395
}
396
397
int
398
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
399
{
400
if (!stream || !position) {
401
return CUBEB_ERROR_INVALID_PARAMETER;
402
}
403
404
return stream->context->ops->stream_get_position(stream, position);
405
}
406
407
int
408
cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
409
{
410
if (!stream || !latency) {
411
return CUBEB_ERROR_INVALID_PARAMETER;
412
}
413
414
if (!stream->context->ops->stream_get_latency) {
415
return CUBEB_ERROR_NOT_SUPPORTED;
416
}
417
418
return stream->context->ops->stream_get_latency(stream, latency);
419
}
420
421
int
422
cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency)
423
{
424
if (!stream || !latency) {
425
return CUBEB_ERROR_INVALID_PARAMETER;
426
}
427
428
if (!stream->context->ops->stream_get_input_latency) {
429
return CUBEB_ERROR_NOT_SUPPORTED;
430
}
431
432
return stream->context->ops->stream_get_input_latency(stream, latency);
433
}
434
435
int
436
cubeb_stream_set_volume(cubeb_stream * stream, float volume)
437
{
438
if (!stream || volume > 1.0 || volume < 0.0) {
439
return CUBEB_ERROR_INVALID_PARAMETER;
440
}
441
442
if (!stream->context->ops->stream_set_volume) {
443
return CUBEB_ERROR_NOT_SUPPORTED;
444
}
445
446
return stream->context->ops->stream_set_volume(stream, volume);
447
}
448
449
int
450
cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name)
451
{
452
if (!stream || !stream_name) {
453
return CUBEB_ERROR_INVALID_PARAMETER;
454
}
455
456
if (!stream->context->ops->stream_set_name) {
457
return CUBEB_ERROR_NOT_SUPPORTED;
458
}
459
460
return stream->context->ops->stream_set_name(stream, stream_name);
461
}
462
463
int
464
cubeb_stream_get_current_device(cubeb_stream * stream,
465
cubeb_device ** const device)
466
{
467
if (!stream || !device) {
468
return CUBEB_ERROR_INVALID_PARAMETER;
469
}
470
471
if (!stream->context->ops->stream_get_current_device) {
472
return CUBEB_ERROR_NOT_SUPPORTED;
473
}
474
475
return stream->context->ops->stream_get_current_device(stream, device);
476
}
477
478
int
479
cubeb_stream_set_input_mute(cubeb_stream * stream, int mute)
480
{
481
if (!stream) {
482
return CUBEB_ERROR_INVALID_PARAMETER;
483
}
484
485
if (!stream->context->ops->stream_set_input_mute) {
486
return CUBEB_ERROR_NOT_SUPPORTED;
487
}
488
489
return stream->context->ops->stream_set_input_mute(stream, mute);
490
}
491
492
int
493
cubeb_stream_set_input_processing_params(cubeb_stream * stream,
494
cubeb_input_processing_params params)
495
{
496
if (!stream) {
497
return CUBEB_ERROR_INVALID_PARAMETER;
498
}
499
500
if (!stream->context->ops->stream_set_input_processing_params) {
501
return CUBEB_ERROR_NOT_SUPPORTED;
502
}
503
504
return stream->context->ops->stream_set_input_processing_params(stream,
505
params);
506
}
507
508
int
509
cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
510
{
511
if (!stream || !device) {
512
return CUBEB_ERROR_INVALID_PARAMETER;
513
}
514
515
if (!stream->context->ops->stream_device_destroy) {
516
return CUBEB_ERROR_NOT_SUPPORTED;
517
}
518
519
return stream->context->ops->stream_device_destroy(stream, device);
520
}
521
522
int
523
cubeb_stream_register_device_changed_callback(
524
cubeb_stream * stream,
525
cubeb_device_changed_callback device_changed_callback)
526
{
527
if (!stream) {
528
return CUBEB_ERROR_INVALID_PARAMETER;
529
}
530
531
if (!stream->context->ops->stream_register_device_changed_callback) {
532
return CUBEB_ERROR_NOT_SUPPORTED;
533
}
534
535
return stream->context->ops->stream_register_device_changed_callback(
536
stream, device_changed_callback);
537
}
538
539
void *
540
cubeb_stream_user_ptr(cubeb_stream * stream)
541
{
542
if (!stream) {
543
return NULL;
544
}
545
546
return stream->user_ptr;
547
}
548
549
static void
550
log_device(cubeb_device_info * device_info)
551
{
552
char devfmts[128] = "";
553
const char *devtype, *devstate, *devdeffmt;
554
555
switch (device_info->type) {
556
case CUBEB_DEVICE_TYPE_INPUT:
557
devtype = "input";
558
break;
559
case CUBEB_DEVICE_TYPE_OUTPUT:
560
devtype = "output";
561
break;
562
case CUBEB_DEVICE_TYPE_UNKNOWN:
563
default:
564
devtype = "unknown?";
565
break;
566
};
567
568
switch (device_info->state) {
569
case CUBEB_DEVICE_STATE_DISABLED:
570
devstate = "disabled";
571
break;
572
case CUBEB_DEVICE_STATE_UNPLUGGED:
573
devstate = "unplugged";
574
break;
575
case CUBEB_DEVICE_STATE_ENABLED:
576
devstate = "enabled";
577
break;
578
default:
579
devstate = "unknown?";
580
break;
581
};
582
583
switch (device_info->default_format) {
584
case CUBEB_DEVICE_FMT_S16LE:
585
devdeffmt = "S16LE";
586
break;
587
case CUBEB_DEVICE_FMT_S16BE:
588
devdeffmt = "S16BE";
589
break;
590
case CUBEB_DEVICE_FMT_F32LE:
591
devdeffmt = "F32LE";
592
break;
593
case CUBEB_DEVICE_FMT_F32BE:
594
devdeffmt = "F32BE";
595
break;
596
default:
597
devdeffmt = "unknown?";
598
break;
599
};
600
601
if (device_info->format & CUBEB_DEVICE_FMT_S16LE) {
602
strcat(devfmts, " S16LE");
603
}
604
if (device_info->format & CUBEB_DEVICE_FMT_S16BE) {
605
strcat(devfmts, " S16BE");
606
}
607
if (device_info->format & CUBEB_DEVICE_FMT_F32LE) {
608
strcat(devfmts, " F32LE");
609
}
610
if (device_info->format & CUBEB_DEVICE_FMT_F32BE) {
611
strcat(devfmts, " F32BE");
612
}
613
614
LOG("DeviceID: \"%s\"%s\n"
615
"\tName:\t\"%s\"\n"
616
"\tGroup:\t\"%s\"\n"
617
"\tVendor:\t\"%s\"\n"
618
"\tType:\t%s\n"
619
"\tState:\t%s\n"
620
"\tMaximum channels:\t%u\n"
621
"\tFormat:\t%s (0x%x) (default: %s)\n"
622
"\tRate:\t[%u, %u] (default: %u)\n"
623
"\tLatency: lo %u frames, hi %u frames",
624
device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
625
device_info->friendly_name, device_info->group_id,
626
device_info->vendor_name, devtype, devstate, device_info->max_channels,
627
(devfmts[0] == '\0') ? devfmts : devfmts + 1,
628
(unsigned int)device_info->format, devdeffmt, device_info->min_rate,
629
device_info->max_rate, device_info->default_rate, device_info->latency_lo,
630
device_info->latency_hi);
631
}
632
633
int
634
cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype,
635
cubeb_device_collection * collection)
636
{
637
int rv;
638
if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
639
return CUBEB_ERROR_INVALID_PARAMETER;
640
if (context == NULL || collection == NULL)
641
return CUBEB_ERROR_INVALID_PARAMETER;
642
if (!context->ops->enumerate_devices)
643
return CUBEB_ERROR_NOT_SUPPORTED;
644
645
rv = context->ops->enumerate_devices(context, devtype, collection);
646
647
if (cubeb_log_get_callback()) {
648
for (size_t i = 0; i < collection->count; i++) {
649
log_device(&collection->device[i]);
650
}
651
}
652
653
return rv;
654
}
655
656
int
657
cubeb_device_collection_destroy(cubeb * context,
658
cubeb_device_collection * collection)
659
{
660
int r;
661
662
if (context == NULL || collection == NULL)
663
return CUBEB_ERROR_INVALID_PARAMETER;
664
665
if (!context->ops->device_collection_destroy)
666
return CUBEB_ERROR_NOT_SUPPORTED;
667
668
if (!collection->device)
669
return CUBEB_OK;
670
671
r = context->ops->device_collection_destroy(context, collection);
672
if (r == CUBEB_OK) {
673
collection->device = NULL;
674
collection->count = 0;
675
}
676
677
return r;
678
}
679
680
int
681
cubeb_register_device_collection_changed(
682
cubeb * context, cubeb_device_type devtype,
683
cubeb_device_collection_changed_callback callback, void * user_ptr)
684
{
685
if (context == NULL ||
686
(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
687
return CUBEB_ERROR_INVALID_PARAMETER;
688
689
if (!context->ops->register_device_collection_changed) {
690
return CUBEB_ERROR_NOT_SUPPORTED;
691
}
692
693
return context->ops->register_device_collection_changed(context, devtype,
694
callback, user_ptr);
695
}
696
697
int
698
cubeb_set_log_callback(cubeb_log_level log_level,
699
cubeb_log_callback log_callback)
700
{
701
if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
702
return CUBEB_ERROR_INVALID_FORMAT;
703
}
704
705
if (!log_callback && log_level != CUBEB_LOG_DISABLED) {
706
return CUBEB_ERROR_INVALID_PARAMETER;
707
}
708
709
if (cubeb_log_get_callback() && log_callback) {
710
return CUBEB_ERROR_NOT_SUPPORTED;
711
}
712
713
cubeb_log_set(log_level, log_callback);
714
715
return CUBEB_OK;
716
}
717
718