tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cubeb.c (19255B)


      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
     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 > 768000 ||
     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 > 768000 ||
    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
    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
    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_AAUDIO)
    256    aaudio_init,
    257 #endif
    258 #if defined(USE_OPENSL)
    259    opensl_init,
    260 #endif
    261 #if defined(USE_AUDIOTRACK)
    262    audiotrack_init,
    263 #endif
    264 #if defined(USE_KAI)
    265    kai_init,
    266 #endif
    267  };
    268  int i;
    269 
    270  if (!context) {
    271    return CUBEB_ERROR_INVALID_PARAMETER;
    272  }
    273 
    274 #define OK(fn) assert((*context)->ops->fn)
    275  for (i = 0; i < NELEMS(default_init); ++i) {
    276    if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) {
    277      /* Assert that the minimal API is implemented. */
    278      OK(get_backend_id);
    279      OK(destroy);
    280      OK(stream_init);
    281      OK(stream_destroy);
    282      OK(stream_start);
    283      OK(stream_stop);
    284      OK(stream_get_position);
    285      return CUBEB_OK;
    286    }
    287  }
    288  return CUBEB_ERROR;
    289 }
    290 
    291 char const *
    292 cubeb_get_backend_id(cubeb * context)
    293 {
    294  if (!context) {
    295    return NULL;
    296  }
    297 
    298  return context->ops->get_backend_id(context);
    299 }
    300 
    301 cubeb_backend_names
    302 cubeb_get_backend_names()
    303 {
    304  static const char * const backend_names[] = {
    305 #if defined(USE_PULSE)
    306    "pulse",
    307 #endif
    308 #if defined(USE_PULSE_RUST)
    309    "pulse-rust",
    310 #endif
    311 #if defined(USE_JACK)
    312    "jack",
    313 #endif
    314 #if defined(USE_ALSA)
    315    "alsa",
    316 #endif
    317 #if defined(USE_AUDIOUNIT)
    318    "audiounit",
    319 #endif
    320 #if defined(USE_AUDIOUNIT_RUST)
    321    "audiounit-rust",
    322 #endif
    323 #if defined(USE_WASAPI)
    324    "wasapi",
    325 #endif
    326 #if defined(USE_WINMM)
    327    "winmm",
    328 #endif
    329 #if defined(USE_SNDIO)
    330    "sndio",
    331 #endif
    332 #if defined(USE_SUN)
    333    "sun",
    334 #endif
    335 #if defined(USE_OPENSL)
    336    "opensl",
    337 #endif
    338 #if defined(USE_OSS)
    339    "oss",
    340 #endif
    341 #if defined(USE_AAUDIO)
    342    "aaudio",
    343 #endif
    344 #if defined(USE_AUDIOTRACK)
    345    "audiotrack",
    346 #endif
    347 #if defined(USE_KAI)
    348    "kai",
    349 #endif
    350  };
    351 
    352  return (cubeb_backend_names){
    353      .names = backend_names,
    354      .count = NELEMS(backend_names),
    355  };
    356 }
    357 
    358 int
    359 cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
    360 {
    361  if (!context || !max_channels) {
    362    return CUBEB_ERROR_INVALID_PARAMETER;
    363  }
    364 
    365  if (!context->ops->get_max_channel_count) {
    366    return CUBEB_ERROR_NOT_SUPPORTED;
    367  }
    368 
    369  return context->ops->get_max_channel_count(context, max_channels);
    370 }
    371 
    372 int
    373 cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params,
    374                      uint32_t * latency_ms)
    375 {
    376  if (!context || !params || !latency_ms) {
    377    return CUBEB_ERROR_INVALID_PARAMETER;
    378  }
    379 
    380  if (!context->ops->get_min_latency) {
    381    return CUBEB_ERROR_NOT_SUPPORTED;
    382  }
    383 
    384  return context->ops->get_min_latency(context, *params, latency_ms);
    385 }
    386 
    387 int
    388 cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
    389 {
    390  if (!context || !rate) {
    391    return CUBEB_ERROR_INVALID_PARAMETER;
    392  }
    393 
    394  if (!context->ops->get_preferred_sample_rate) {
    395    return CUBEB_ERROR_NOT_SUPPORTED;
    396  }
    397 
    398  return context->ops->get_preferred_sample_rate(context, rate);
    399 }
    400 
    401 int
    402 cubeb_get_supported_input_processing_params(
    403    cubeb * context, cubeb_input_processing_params * params)
    404 {
    405  if (!context || !params) {
    406    return CUBEB_ERROR_INVALID_PARAMETER;
    407  }
    408 
    409  if (!context->ops->get_supported_input_processing_params) {
    410    return CUBEB_ERROR_NOT_SUPPORTED;
    411  }
    412 
    413  return context->ops->get_supported_input_processing_params(context, params);
    414 }
    415 
    416 void
    417 cubeb_destroy(cubeb * context)
    418 {
    419  if (!context) {
    420    return;
    421  }
    422 
    423  context->ops->destroy(context);
    424 
    425  cubeb_set_log_callback(CUBEB_LOG_DISABLED, NULL);
    426 }
    427 
    428 int
    429 cubeb_stream_init(cubeb * context, cubeb_stream ** stream,
    430                  char const * stream_name, cubeb_devid input_device,
    431                  cubeb_stream_params * input_stream_params,
    432                  cubeb_devid output_device,
    433                  cubeb_stream_params * output_stream_params,
    434                  unsigned int latency, cubeb_data_callback data_callback,
    435                  cubeb_state_callback state_callback, void * user_ptr)
    436 {
    437  int r;
    438 
    439  if (!context || !stream || !data_callback || !state_callback) {
    440    return CUBEB_ERROR_INVALID_PARAMETER;
    441  }
    442 
    443  if ((r = validate_stream_params(input_stream_params, output_stream_params)) !=
    444          CUBEB_OK ||
    445      (r = validate_latency(latency)) != CUBEB_OK) {
    446    return r;
    447  }
    448 
    449  r = context->ops->stream_init(context, stream, stream_name, input_device,
    450                                input_stream_params, output_device,
    451                                output_stream_params, latency, data_callback,
    452                                state_callback, user_ptr);
    453 
    454  if (r == CUBEB_ERROR_INVALID_FORMAT) {
    455    LOG("Invalid format, %p %p %d %d", output_stream_params,
    456        input_stream_params,
    457        output_stream_params && output_stream_params->format,
    458        input_stream_params && input_stream_params->format);
    459  }
    460 
    461  return r;
    462 }
    463 
    464 void
    465 cubeb_stream_destroy(cubeb_stream * stream)
    466 {
    467  if (!stream) {
    468    return;
    469  }
    470 
    471  stream->context->ops->stream_destroy(stream);
    472 }
    473 
    474 int
    475 cubeb_stream_start(cubeb_stream * stream)
    476 {
    477  if (!stream) {
    478    return CUBEB_ERROR_INVALID_PARAMETER;
    479  }
    480 
    481  return stream->context->ops->stream_start(stream);
    482 }
    483 
    484 int
    485 cubeb_stream_stop(cubeb_stream * stream)
    486 {
    487  if (!stream) {
    488    return CUBEB_ERROR_INVALID_PARAMETER;
    489  }
    490 
    491  return stream->context->ops->stream_stop(stream);
    492 }
    493 
    494 int
    495 cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
    496 {
    497  if (!stream || !position) {
    498    return CUBEB_ERROR_INVALID_PARAMETER;
    499  }
    500 
    501  return stream->context->ops->stream_get_position(stream, position);
    502 }
    503 
    504 int
    505 cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
    506 {
    507  if (!stream || !latency) {
    508    return CUBEB_ERROR_INVALID_PARAMETER;
    509  }
    510 
    511  if (!stream->context->ops->stream_get_latency) {
    512    return CUBEB_ERROR_NOT_SUPPORTED;
    513  }
    514 
    515  return stream->context->ops->stream_get_latency(stream, latency);
    516 }
    517 
    518 int
    519 cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency)
    520 {
    521  if (!stream || !latency) {
    522    return CUBEB_ERROR_INVALID_PARAMETER;
    523  }
    524 
    525  if (!stream->context->ops->stream_get_input_latency) {
    526    return CUBEB_ERROR_NOT_SUPPORTED;
    527  }
    528 
    529  return stream->context->ops->stream_get_input_latency(stream, latency);
    530 }
    531 
    532 int
    533 cubeb_stream_set_volume(cubeb_stream * stream, float volume)
    534 {
    535  if (!stream || volume > 1.0 || volume < 0.0) {
    536    return CUBEB_ERROR_INVALID_PARAMETER;
    537  }
    538 
    539  if (!stream->context->ops->stream_set_volume) {
    540    return CUBEB_ERROR_NOT_SUPPORTED;
    541  }
    542 
    543  return stream->context->ops->stream_set_volume(stream, volume);
    544 }
    545 
    546 int
    547 cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name)
    548 {
    549  if (!stream || !stream_name) {
    550    return CUBEB_ERROR_INVALID_PARAMETER;
    551  }
    552 
    553  if (!stream->context->ops->stream_set_name) {
    554    return CUBEB_ERROR_NOT_SUPPORTED;
    555  }
    556 
    557  return stream->context->ops->stream_set_name(stream, stream_name);
    558 }
    559 
    560 int
    561 cubeb_stream_get_current_device(cubeb_stream * stream,
    562                                cubeb_device ** const device)
    563 {
    564  if (!stream || !device) {
    565    return CUBEB_ERROR_INVALID_PARAMETER;
    566  }
    567 
    568  if (!stream->context->ops->stream_get_current_device) {
    569    return CUBEB_ERROR_NOT_SUPPORTED;
    570  }
    571 
    572  return stream->context->ops->stream_get_current_device(stream, device);
    573 }
    574 
    575 int
    576 cubeb_stream_set_input_mute(cubeb_stream * stream, int mute)
    577 {
    578  if (!stream) {
    579    return CUBEB_ERROR_INVALID_PARAMETER;
    580  }
    581 
    582  if (!stream->context->ops->stream_set_input_mute) {
    583    return CUBEB_ERROR_NOT_SUPPORTED;
    584  }
    585 
    586  return stream->context->ops->stream_set_input_mute(stream, mute);
    587 }
    588 
    589 int
    590 cubeb_stream_set_input_processing_params(cubeb_stream * stream,
    591                                         cubeb_input_processing_params params)
    592 {
    593  if (!stream) {
    594    return CUBEB_ERROR_INVALID_PARAMETER;
    595  }
    596 
    597  if (!stream->context->ops->stream_set_input_processing_params) {
    598    return CUBEB_ERROR_NOT_SUPPORTED;
    599  }
    600 
    601  return stream->context->ops->stream_set_input_processing_params(stream,
    602                                                                  params);
    603 }
    604 
    605 int
    606 cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
    607 {
    608  if (!stream || !device) {
    609    return CUBEB_ERROR_INVALID_PARAMETER;
    610  }
    611 
    612  if (!stream->context->ops->stream_device_destroy) {
    613    return CUBEB_ERROR_NOT_SUPPORTED;
    614  }
    615 
    616  return stream->context->ops->stream_device_destroy(stream, device);
    617 }
    618 
    619 int
    620 cubeb_stream_register_device_changed_callback(
    621    cubeb_stream * stream,
    622    cubeb_device_changed_callback device_changed_callback)
    623 {
    624  if (!stream) {
    625    return CUBEB_ERROR_INVALID_PARAMETER;
    626  }
    627 
    628  if (!stream->context->ops->stream_register_device_changed_callback) {
    629    return CUBEB_ERROR_NOT_SUPPORTED;
    630  }
    631 
    632  return stream->context->ops->stream_register_device_changed_callback(
    633      stream, device_changed_callback);
    634 }
    635 
    636 void *
    637 cubeb_stream_user_ptr(cubeb_stream * stream)
    638 {
    639  if (!stream) {
    640    return NULL;
    641  }
    642 
    643  return stream->user_ptr;
    644 }
    645 
    646 static void
    647 log_device(cubeb_device_info * device_info)
    648 {
    649  char devfmts[128] = "";
    650  const char *devtype, *devstate, *devdeffmt;
    651 
    652  switch (device_info->type) {
    653  case CUBEB_DEVICE_TYPE_INPUT:
    654    devtype = "input";
    655    break;
    656  case CUBEB_DEVICE_TYPE_OUTPUT:
    657    devtype = "output";
    658    break;
    659  case CUBEB_DEVICE_TYPE_UNKNOWN:
    660  default:
    661    devtype = "unknown?";
    662    break;
    663  };
    664 
    665  switch (device_info->state) {
    666  case CUBEB_DEVICE_STATE_DISABLED:
    667    devstate = "disabled";
    668    break;
    669  case CUBEB_DEVICE_STATE_UNPLUGGED:
    670    devstate = "unplugged";
    671    break;
    672  case CUBEB_DEVICE_STATE_ENABLED:
    673    devstate = "enabled";
    674    break;
    675  default:
    676    devstate = "unknown?";
    677    break;
    678  };
    679 
    680  switch (device_info->default_format) {
    681  case CUBEB_DEVICE_FMT_S16LE:
    682    devdeffmt = "S16LE";
    683    break;
    684  case CUBEB_DEVICE_FMT_S16BE:
    685    devdeffmt = "S16BE";
    686    break;
    687  case CUBEB_DEVICE_FMT_F32LE:
    688    devdeffmt = "F32LE";
    689    break;
    690  case CUBEB_DEVICE_FMT_F32BE:
    691    devdeffmt = "F32BE";
    692    break;
    693  default:
    694    devdeffmt = "unknown?";
    695    break;
    696  };
    697 
    698  if (device_info->format & CUBEB_DEVICE_FMT_S16LE) {
    699    strcat(devfmts, " S16LE");
    700  }
    701  if (device_info->format & CUBEB_DEVICE_FMT_S16BE) {
    702    strcat(devfmts, " S16BE");
    703  }
    704  if (device_info->format & CUBEB_DEVICE_FMT_F32LE) {
    705    strcat(devfmts, " F32LE");
    706  }
    707  if (device_info->format & CUBEB_DEVICE_FMT_F32BE) {
    708    strcat(devfmts, " F32BE");
    709  }
    710 
    711  LOG("DeviceID: \"%s\"%s\n"
    712      "\tName:\t\"%s\"\n"
    713      "\tGroup:\t\"%s\"\n"
    714      "\tVendor:\t\"%s\"\n"
    715      "\tType:\t%s\n"
    716      "\tState:\t%s\n"
    717      "\tMaximum channels:\t%u\n"
    718      "\tFormat:\t%s (0x%x) (default: %s)\n"
    719      "\tRate:\t[%u, %u] (default: %u)\n"
    720      "\tLatency: lo %u frames, hi %u frames",
    721      device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
    722      device_info->friendly_name, device_info->group_id,
    723      device_info->vendor_name, devtype, devstate, device_info->max_channels,
    724      (devfmts[0] == '\0') ? devfmts : devfmts + 1,
    725      (unsigned int)device_info->format, devdeffmt, device_info->min_rate,
    726      device_info->max_rate, device_info->default_rate, device_info->latency_lo,
    727      device_info->latency_hi);
    728 }
    729 
    730 int
    731 cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype,
    732                        cubeb_device_collection * collection)
    733 {
    734  int rv;
    735  if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
    736    return CUBEB_ERROR_INVALID_PARAMETER;
    737  if (context == NULL || collection == NULL)
    738    return CUBEB_ERROR_INVALID_PARAMETER;
    739  if (!context->ops->enumerate_devices)
    740    return CUBEB_ERROR_NOT_SUPPORTED;
    741 
    742  rv = context->ops->enumerate_devices(context, devtype, collection);
    743 
    744  if (cubeb_log_get_callback()) {
    745    for (size_t i = 0; i < collection->count; i++) {
    746      log_device(&collection->device[i]);
    747    }
    748  }
    749 
    750  return rv;
    751 }
    752 
    753 int
    754 cubeb_device_collection_destroy(cubeb * context,
    755                                cubeb_device_collection * collection)
    756 {
    757  int r;
    758 
    759  if (context == NULL || collection == NULL)
    760    return CUBEB_ERROR_INVALID_PARAMETER;
    761 
    762  if (!context->ops->device_collection_destroy)
    763    return CUBEB_ERROR_NOT_SUPPORTED;
    764 
    765  if (!collection->device)
    766    return CUBEB_OK;
    767 
    768  r = context->ops->device_collection_destroy(context, collection);
    769  if (r == CUBEB_OK) {
    770    collection->device = NULL;
    771    collection->count = 0;
    772  }
    773 
    774  return r;
    775 }
    776 
    777 int
    778 cubeb_register_device_collection_changed(
    779    cubeb * context, cubeb_device_type devtype,
    780    cubeb_device_collection_changed_callback callback, void * user_ptr)
    781 {
    782  if (context == NULL ||
    783      (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
    784    return CUBEB_ERROR_INVALID_PARAMETER;
    785 
    786  if (!context->ops->register_device_collection_changed) {
    787    return CUBEB_ERROR_NOT_SUPPORTED;
    788  }
    789 
    790  return context->ops->register_device_collection_changed(context, devtype,
    791                                                          callback, user_ptr);
    792 }
    793 
    794 int
    795 cubeb_set_log_callback(cubeb_log_level log_level,
    796                       cubeb_log_callback log_callback)
    797 {
    798  if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
    799    return CUBEB_ERROR_INVALID_FORMAT;
    800  }
    801 
    802  if (!log_callback && log_level != CUBEB_LOG_DISABLED) {
    803    return CUBEB_ERROR_INVALID_PARAMETER;
    804  }
    805 
    806  if (cubeb_log_get_callback() && log_callback) {
    807    return CUBEB_ERROR_NOT_SUPPORTED;
    808  }
    809 
    810  cubeb_log_set(log_level, log_callback);
    811 
    812  return CUBEB_OK;
    813 }