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