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 }