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