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