duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cubeb.c (17017B)


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