cubeb_oss.c (34400B)
1 /* 2 * Copyright © 2019-2020 Nia Alarie <nia@NetBSD.org> 3 * Copyright © 2020 Ka Ho Ng <khng300@gmail.com> 4 * Copyright © 2020 The FreeBSD Foundation 5 * 6 * Portions of this software were developed by Ka Ho Ng 7 * under sponsorship from the FreeBSD Foundation. 8 * 9 * This program is made available under an ISC-style license. See the 10 * accompanying file LICENSE for details. 11 */ 12 13 #include "cubeb-internal.h" 14 #include "cubeb/cubeb.h" 15 #include "cubeb_mixer.h" 16 #include "cubeb_strings.h" 17 #include "cubeb_tracing.h" 18 #include <assert.h> 19 #include <ctype.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <limits.h> 23 #include <poll.h> 24 #include <pthread.h> 25 #include <stdbool.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <sys/ioctl.h> 30 #include <sys/soundcard.h> 31 #include <sys/types.h> 32 #include <unistd.h> 33 34 /* Supported well by most hardware. */ 35 #ifndef OSS_PREFER_RATE 36 #define OSS_PREFER_RATE (48000) 37 #endif 38 39 /* Standard acceptable minimum. */ 40 #ifndef OSS_LATENCY_MS 41 #define OSS_LATENCY_MS (8) 42 #endif 43 44 #ifndef OSS_NFRAGS 45 #define OSS_NFRAGS (4) 46 #endif 47 48 #ifndef OSS_DEFAULT_DEVICE 49 #define OSS_DEFAULT_DEVICE "/dev/dsp" 50 #endif 51 52 #ifndef OSS_DEFAULT_MIXER 53 #define OSS_DEFAULT_MIXER "/dev/mixer" 54 #endif 55 56 #define ENV_AUDIO_DEVICE "AUDIO_DEVICE" 57 58 #ifndef OSS_MAX_CHANNELS 59 #if defined(__FreeBSD__) || defined(__DragonFly__) 60 /* 61 * The current maximum number of channels supported 62 * on FreeBSD is 8. 63 * 64 * Reference: FreeBSD 12.1-RELEASE 65 */ 66 #define OSS_MAX_CHANNELS (8) 67 #elif defined(__sun__) 68 /* 69 * The current maximum number of channels supported 70 * on Illumos is 16. 71 * 72 * Reference: PSARC 2008/318 73 */ 74 #define OSS_MAX_CHANNELS (16) 75 #else 76 #define OSS_MAX_CHANNELS (2) 77 #endif 78 #endif 79 80 #if defined(__FreeBSD__) || defined(__DragonFly__) 81 #define SNDSTAT_BEGIN_STR "Installed devices:" 82 #define SNDSTAT_USER_BEGIN_STR "Installed devices from userspace:" 83 #define SNDSTAT_FV_BEGIN_STR "File Versions:" 84 #endif 85 86 static struct cubeb_ops const oss_ops; 87 88 struct cubeb { 89 struct cubeb_ops const * ops; 90 91 /* Our intern string store */ 92 pthread_mutex_t mutex; /* protects devid_strs */ 93 cubeb_strings * devid_strs; 94 }; 95 96 struct oss_stream { 97 oss_devnode_t name; 98 int fd; 99 void * buf; 100 unsigned int bufframes; 101 unsigned int maxframes; 102 103 struct stream_info { 104 int channels; 105 int sample_rate; 106 int fmt; 107 int precision; 108 } info; 109 110 unsigned int frame_size; /* precision in bytes * channels */ 111 bool floating; 112 }; 113 114 struct cubeb_stream { 115 struct cubeb * context; 116 void * user_ptr; 117 pthread_t thread; 118 bool doorbell; /* (m) */ 119 pthread_cond_t doorbell_cv; /* (m) */ 120 pthread_cond_t stopped_cv; /* (m) */ 121 pthread_mutex_t mtx; /* Members protected by this should be marked (m) */ 122 bool thread_created; /* (m) */ 123 bool running; /* (m) */ 124 bool destroying; /* (m) */ 125 cubeb_state state; /* (m) */ 126 float volume /* (m) */; 127 struct oss_stream play; 128 struct oss_stream record; 129 cubeb_data_callback data_cb; 130 cubeb_state_callback state_cb; 131 uint64_t frames_written /* (m) */; 132 }; 133 134 static char const * 135 oss_cubeb_devid_intern(cubeb * context, char const * devid) 136 { 137 char const * is; 138 pthread_mutex_lock(&context->mutex); 139 is = cubeb_strings_intern(context->devid_strs, devid); 140 pthread_mutex_unlock(&context->mutex); 141 return is; 142 } 143 144 int 145 oss_init(cubeb ** context, char const * context_name) 146 { 147 cubeb * c; 148 149 (void)context_name; 150 if ((c = calloc(1, sizeof(cubeb))) == NULL) { 151 return CUBEB_ERROR; 152 } 153 154 if (cubeb_strings_init(&c->devid_strs) == CUBEB_ERROR) { 155 goto fail; 156 } 157 158 if (pthread_mutex_init(&c->mutex, NULL) != 0) { 159 goto fail; 160 } 161 162 c->ops = &oss_ops; 163 *context = c; 164 return CUBEB_OK; 165 166 fail: 167 cubeb_strings_destroy(c->devid_strs); 168 free(c); 169 return CUBEB_ERROR; 170 } 171 172 static void 173 oss_destroy(cubeb * context) 174 { 175 pthread_mutex_destroy(&context->mutex); 176 cubeb_strings_destroy(context->devid_strs); 177 free(context); 178 } 179 180 static char const * 181 oss_get_backend_id(cubeb * context) 182 { 183 return "oss"; 184 } 185 186 static int 187 oss_get_preferred_sample_rate(cubeb * context, uint32_t * rate) 188 { 189 (void)context; 190 191 *rate = OSS_PREFER_RATE; 192 return CUBEB_OK; 193 } 194 195 static int 196 oss_get_max_channel_count(cubeb * context, uint32_t * max_channels) 197 { 198 (void)context; 199 200 *max_channels = OSS_MAX_CHANNELS; 201 return CUBEB_OK; 202 } 203 204 static int 205 oss_get_min_latency(cubeb * context, cubeb_stream_params params, 206 uint32_t * latency_frames) 207 { 208 (void)context; 209 210 *latency_frames = (OSS_LATENCY_MS * params.rate) / 1000; 211 return CUBEB_OK; 212 } 213 214 static void 215 oss_free_cubeb_device_info_strings(cubeb_device_info * cdi) 216 { 217 free((char *)cdi->device_id); 218 free((char *)cdi->friendly_name); 219 free((char *)cdi->group_id); 220 cdi->device_id = NULL; 221 cdi->friendly_name = NULL; 222 cdi->group_id = NULL; 223 } 224 225 #if defined(__FreeBSD__) || defined(__DragonFly__) 226 /* 227 * Check if the specified DSP is okay for the purpose specified 228 * in type. Here type can only specify one operation each time 229 * this helper is called. 230 * 231 * Return 0 if OK, otherwise 1. 232 */ 233 static int 234 oss_probe_open(const char * dsppath, cubeb_device_type type, int * fdp, 235 oss_audioinfo * resai) 236 { 237 oss_audioinfo ai; 238 int error; 239 int oflags = (type == CUBEB_DEVICE_TYPE_INPUT) ? O_RDONLY : O_WRONLY; 240 int dspfd = open(dsppath, oflags); 241 if (dspfd == -1) 242 return 1; 243 244 ai.dev = -1; 245 error = ioctl(dspfd, SNDCTL_AUDIOINFO, &ai); 246 if (error < 0) { 247 close(dspfd); 248 return 1; 249 } 250 251 if (resai) 252 *resai = ai; 253 if (fdp) 254 *fdp = dspfd; 255 else 256 close(dspfd); 257 return 0; 258 } 259 260 struct sndstat_info { 261 oss_devnode_t devname; 262 const char * desc; 263 cubeb_device_type type; 264 int preferred; 265 }; 266 267 static int 268 oss_sndstat_line_parse(char * line, int is_ud, struct sndstat_info * sinfo) 269 { 270 char *matchptr = line, *n = NULL; 271 struct sndstat_info res; 272 273 memset(&res, 0, sizeof(res)); 274 275 n = strchr(matchptr, ':'); 276 if (n == NULL) 277 goto fail; 278 if (is_ud == 0) { 279 unsigned int devunit; 280 281 if (sscanf(matchptr, "pcm%u: ", &devunit) < 1) 282 goto fail; 283 284 if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1) 285 goto fail; 286 } else { 287 if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/"))) 288 goto fail; 289 290 strlcpy(res.devname, "/dev/", sizeof(res.devname)); 291 strncat(res.devname, matchptr, n - matchptr); 292 } 293 matchptr = n + 1; 294 295 n = strchr(matchptr, '<'); 296 if (n == NULL) 297 goto fail; 298 matchptr = n + 1; 299 n = strrchr(matchptr, '>'); 300 if (n == NULL) 301 goto fail; 302 *n = 0; 303 res.desc = matchptr; 304 matchptr = n + 1; 305 306 n = strchr(matchptr, '('); 307 if (n == NULL) 308 goto fail; 309 matchptr = n + 1; 310 n = strrchr(matchptr, ')'); 311 if (n == NULL) 312 goto fail; 313 *n = 0; 314 if (!isdigit(matchptr[0])) { 315 if (strstr(matchptr, "play") != NULL) 316 res.type |= CUBEB_DEVICE_TYPE_OUTPUT; 317 if (strstr(matchptr, "rec") != NULL) 318 res.type |= CUBEB_DEVICE_TYPE_INPUT; 319 } else { 320 int p, r; 321 if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2) 322 goto fail; 323 if (p > 0) 324 res.type |= CUBEB_DEVICE_TYPE_OUTPUT; 325 if (r > 0) 326 res.type |= CUBEB_DEVICE_TYPE_INPUT; 327 } 328 matchptr = n + 1; 329 if (strstr(matchptr, "default") != NULL) 330 res.preferred = 1; 331 332 *sinfo = res; 333 return 0; 334 335 fail: 336 return 1; 337 } 338 339 /* 340 * XXX: On FreeBSD we have to rely on SNDCTL_CARDINFO to get all 341 * the usable audio devices currently, as SNDCTL_AUDIOINFO will 342 * never return directly usable audio device nodes. 343 */ 344 static int 345 oss_enumerate_devices(cubeb * context, cubeb_device_type type, 346 cubeb_device_collection * collection) 347 { 348 cubeb_device_info * devinfop = NULL; 349 char * line = NULL; 350 size_t linecap = 0; 351 FILE * sndstatfp = NULL; 352 int collection_cnt = 0; 353 int is_ud = 0; 354 int skipall = 0; 355 356 devinfop = calloc(1, sizeof(cubeb_device_info)); 357 if (devinfop == NULL) 358 goto fail; 359 360 sndstatfp = fopen("/dev/sndstat", "r"); 361 if (sndstatfp == NULL) 362 goto fail; 363 while (getline(&line, &linecap, sndstatfp) > 0) { 364 const char * devid = NULL; 365 struct sndstat_info sinfo; 366 oss_audioinfo ai; 367 368 if (!strncmp(line, SNDSTAT_FV_BEGIN_STR, strlen(SNDSTAT_FV_BEGIN_STR))) { 369 skipall = 1; 370 continue; 371 } 372 if (!strncmp(line, SNDSTAT_BEGIN_STR, strlen(SNDSTAT_BEGIN_STR))) { 373 is_ud = 0; 374 skipall = 0; 375 continue; 376 } 377 if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, 378 strlen(SNDSTAT_USER_BEGIN_STR))) { 379 is_ud = 1; 380 skipall = 0; 381 continue; 382 } 383 if (skipall || isblank(line[0])) 384 continue; 385 386 if (oss_sndstat_line_parse(line, is_ud, &sinfo)) 387 continue; 388 389 devinfop[collection_cnt].type = 0; 390 switch (sinfo.type) { 391 case CUBEB_DEVICE_TYPE_INPUT: 392 if (type & CUBEB_DEVICE_TYPE_OUTPUT) 393 continue; 394 break; 395 case CUBEB_DEVICE_TYPE_OUTPUT: 396 if (type & CUBEB_DEVICE_TYPE_INPUT) 397 continue; 398 break; 399 case 0: 400 continue; 401 } 402 403 if (oss_probe_open(sinfo.devname, type, NULL, &ai)) 404 continue; 405 406 devid = oss_cubeb_devid_intern(context, sinfo.devname); 407 if (devid == NULL) 408 continue; 409 410 devinfop[collection_cnt].device_id = strdup(sinfo.devname); 411 asprintf((char **)&devinfop[collection_cnt].friendly_name, "%s: %s", 412 sinfo.devname, sinfo.desc); 413 devinfop[collection_cnt].group_id = strdup(sinfo.devname); 414 devinfop[collection_cnt].vendor_name = NULL; 415 if (devinfop[collection_cnt].device_id == NULL || 416 devinfop[collection_cnt].friendly_name == NULL || 417 devinfop[collection_cnt].group_id == NULL) { 418 oss_free_cubeb_device_info_strings(&devinfop[collection_cnt]); 419 continue; 420 } 421 422 devinfop[collection_cnt].type = type; 423 devinfop[collection_cnt].devid = devid; 424 devinfop[collection_cnt].state = CUBEB_DEVICE_STATE_ENABLED; 425 devinfop[collection_cnt].preferred = 426 (sinfo.preferred) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; 427 devinfop[collection_cnt].format = CUBEB_DEVICE_FMT_S16NE; 428 devinfop[collection_cnt].default_format = CUBEB_DEVICE_FMT_S16NE; 429 devinfop[collection_cnt].max_channels = ai.max_channels; 430 devinfop[collection_cnt].default_rate = OSS_PREFER_RATE; 431 devinfop[collection_cnt].max_rate = ai.max_rate; 432 devinfop[collection_cnt].min_rate = ai.min_rate; 433 devinfop[collection_cnt].latency_lo = 0; 434 devinfop[collection_cnt].latency_hi = 0; 435 436 collection_cnt++; 437 438 void * newp = 439 reallocarray(devinfop, collection_cnt + 1, sizeof(cubeb_device_info)); 440 if (newp == NULL) 441 goto fail; 442 devinfop = newp; 443 } 444 445 free(line); 446 fclose(sndstatfp); 447 448 collection->count = collection_cnt; 449 collection->device = devinfop; 450 451 return CUBEB_OK; 452 453 fail: 454 free(line); 455 if (sndstatfp) 456 fclose(sndstatfp); 457 free(devinfop); 458 return CUBEB_ERROR; 459 } 460 461 #else 462 463 static int 464 oss_enumerate_devices(cubeb * context, cubeb_device_type type, 465 cubeb_device_collection * collection) 466 { 467 oss_sysinfo si; 468 int error, i; 469 cubeb_device_info * devinfop = NULL; 470 int collection_cnt = 0; 471 int mixer_fd = -1; 472 473 mixer_fd = open(OSS_DEFAULT_MIXER, O_RDWR); 474 if (mixer_fd == -1) { 475 LOG("Failed to open mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); 476 return CUBEB_ERROR; 477 } 478 479 error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si); 480 if (error) { 481 LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", 482 OSS_DEFAULT_MIXER, errno); 483 goto fail; 484 } 485 486 devinfop = calloc(si.numaudios, sizeof(cubeb_device_info)); 487 if (devinfop == NULL) 488 goto fail; 489 490 collection->count = 0; 491 for (i = 0; i < si.numaudios; i++) { 492 oss_audioinfo ai; 493 cubeb_device_info cdi = {0}; 494 const char * devid = NULL; 495 496 ai.dev = i; 497 error = ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai); 498 if (error) 499 goto fail; 500 501 assert(ai.dev < si.numaudios); 502 if (!ai.enabled) 503 continue; 504 505 cdi.type = 0; 506 switch (ai.caps & DSP_CAP_DUPLEX) { 507 case DSP_CAP_INPUT: 508 if (type & CUBEB_DEVICE_TYPE_OUTPUT) 509 continue; 510 break; 511 case DSP_CAP_OUTPUT: 512 if (type & CUBEB_DEVICE_TYPE_INPUT) 513 continue; 514 break; 515 case 0: 516 continue; 517 } 518 cdi.type = type; 519 520 devid = oss_cubeb_devid_intern(context, ai.devnode); 521 cdi.device_id = strdup(ai.name); 522 cdi.friendly_name = strdup(ai.name); 523 cdi.group_id = strdup(ai.name); 524 if (devid == NULL || cdi.device_id == NULL || cdi.friendly_name == NULL || 525 cdi.group_id == NULL) { 526 oss_free_cubeb_device_info_strings(&cdi); 527 continue; 528 } 529 530 cdi.devid = devid; 531 cdi.vendor_name = NULL; 532 cdi.state = CUBEB_DEVICE_STATE_ENABLED; 533 cdi.preferred = CUBEB_DEVICE_PREF_NONE; 534 cdi.format = CUBEB_DEVICE_FMT_S16NE; 535 cdi.default_format = CUBEB_DEVICE_FMT_S16NE; 536 cdi.max_channels = ai.max_channels; 537 cdi.default_rate = OSS_PREFER_RATE; 538 cdi.max_rate = ai.max_rate; 539 cdi.min_rate = ai.min_rate; 540 cdi.latency_lo = 0; 541 cdi.latency_hi = 0; 542 543 devinfop[collection_cnt++] = cdi; 544 } 545 546 collection->count = collection_cnt; 547 collection->device = devinfop; 548 549 if (mixer_fd != -1) 550 close(mixer_fd); 551 return CUBEB_OK; 552 553 fail: 554 if (mixer_fd != -1) 555 close(mixer_fd); 556 free(devinfop); 557 return CUBEB_ERROR; 558 } 559 560 #endif 561 562 static int 563 oss_device_collection_destroy(cubeb * context, 564 cubeb_device_collection * collection) 565 { 566 size_t i; 567 for (i = 0; i < collection->count; i++) { 568 oss_free_cubeb_device_info_strings(&collection->device[i]); 569 } 570 free(collection->device); 571 collection->device = NULL; 572 collection->count = 0; 573 return 0; 574 } 575 576 static unsigned int 577 oss_chn_from_cubeb(cubeb_channel chn) 578 { 579 switch (chn) { 580 case CHANNEL_FRONT_LEFT: 581 return CHID_L; 582 case CHANNEL_FRONT_RIGHT: 583 return CHID_R; 584 case CHANNEL_FRONT_CENTER: 585 return CHID_C; 586 case CHANNEL_LOW_FREQUENCY: 587 return CHID_LFE; 588 case CHANNEL_BACK_LEFT: 589 return CHID_LR; 590 case CHANNEL_BACK_RIGHT: 591 return CHID_RR; 592 case CHANNEL_SIDE_LEFT: 593 return CHID_LS; 594 case CHANNEL_SIDE_RIGHT: 595 return CHID_RS; 596 default: 597 return CHID_UNDEF; 598 } 599 } 600 601 static unsigned long long 602 oss_cubeb_layout_to_chnorder(cubeb_channel_layout layout) 603 { 604 unsigned int i, nchns = 0; 605 unsigned long long chnorder = 0; 606 607 for (i = 0; layout; i++, layout >>= 1) { 608 unsigned long long chid = oss_chn_from_cubeb((layout & 1) << i); 609 if (chid == CHID_UNDEF) 610 continue; 611 612 chnorder |= (chid & 0xf) << nchns * 4; 613 nchns++; 614 } 615 616 return chnorder; 617 } 618 619 static int 620 oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params, 621 struct stream_info * sinfo) 622 { 623 unsigned long long chnorder; 624 625 sinfo->channels = params->channels; 626 sinfo->sample_rate = params->rate; 627 switch (params->format) { 628 case CUBEB_SAMPLE_S16LE: 629 sinfo->fmt = AFMT_S16_LE; 630 sinfo->precision = 16; 631 break; 632 case CUBEB_SAMPLE_S16BE: 633 sinfo->fmt = AFMT_S16_BE; 634 sinfo->precision = 16; 635 break; 636 case CUBEB_SAMPLE_FLOAT32NE: 637 sinfo->fmt = AFMT_S32_NE; 638 sinfo->precision = 32; 639 break; 640 default: 641 LOG("Unsupported format"); 642 return CUBEB_ERROR_INVALID_FORMAT; 643 } 644 if (ioctl(fd, SNDCTL_DSP_CHANNELS, &sinfo->channels) == -1) { 645 return CUBEB_ERROR; 646 } 647 if (ioctl(fd, SNDCTL_DSP_SETFMT, &sinfo->fmt) == -1) { 648 return CUBEB_ERROR; 649 } 650 if (ioctl(fd, SNDCTL_DSP_SPEED, &sinfo->sample_rate) == -1) { 651 return CUBEB_ERROR; 652 } 653 /* Mono layout is an exception */ 654 if (params->layout != CUBEB_LAYOUT_UNDEFINED && 655 params->layout != CUBEB_LAYOUT_MONO) { 656 chnorder = oss_cubeb_layout_to_chnorder(params->layout); 657 if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1) 658 LOG("Non-fatal error %d occured when setting channel order.", errno); 659 } 660 return CUBEB_OK; 661 } 662 663 static int 664 oss_stream_stop(cubeb_stream * s) 665 { 666 pthread_mutex_lock(&s->mtx); 667 if (s->thread_created && s->running) { 668 s->running = false; 669 s->doorbell = false; 670 pthread_cond_wait(&s->stopped_cv, &s->mtx); 671 } 672 if (s->state != CUBEB_STATE_STOPPED) { 673 s->state = CUBEB_STATE_STOPPED; 674 pthread_mutex_unlock(&s->mtx); 675 s->state_cb(s, s->user_ptr, CUBEB_STATE_STOPPED); 676 } else { 677 pthread_mutex_unlock(&s->mtx); 678 } 679 return CUBEB_OK; 680 } 681 682 static void 683 oss_stream_destroy(cubeb_stream * s) 684 { 685 pthread_mutex_lock(&s->mtx); 686 if (s->thread_created) { 687 s->destroying = true; 688 s->doorbell = true; 689 pthread_cond_signal(&s->doorbell_cv); 690 } 691 pthread_mutex_unlock(&s->mtx); 692 pthread_join(s->thread, NULL); 693 694 pthread_cond_destroy(&s->doorbell_cv); 695 pthread_cond_destroy(&s->stopped_cv); 696 pthread_mutex_destroy(&s->mtx); 697 if (s->play.fd != -1) { 698 close(s->play.fd); 699 } 700 if (s->record.fd != -1) { 701 close(s->record.fd); 702 } 703 free(s->play.buf); 704 free(s->record.buf); 705 free(s); 706 } 707 708 static void 709 oss_float_to_linear32(void * buf, unsigned sample_count, float vol) 710 { 711 float * in = buf; 712 int32_t * out = buf; 713 int32_t * tail = out + sample_count; 714 715 while (out < tail) { 716 int64_t f = *(in++) * vol * 0x80000000LL; 717 if (f < -INT32_MAX) 718 f = -INT32_MAX; 719 else if (f > INT32_MAX) 720 f = INT32_MAX; 721 *(out++) = f; 722 } 723 } 724 725 static void 726 oss_linear32_to_float(void * buf, unsigned sample_count) 727 { 728 int32_t * in = buf; 729 float * out = buf; 730 float * tail = out + sample_count; 731 732 while (out < tail) { 733 *(out++) = (1.0 / 0x80000000LL) * *(in++); 734 } 735 } 736 737 static void 738 oss_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol) 739 { 740 unsigned i; 741 int32_t multiplier = vol * 0x8000; 742 743 for (i = 0; i < sample_count; ++i) { 744 buf[i] = (buf[i] * multiplier) >> 15; 745 } 746 } 747 748 static int 749 oss_get_rec_frames(cubeb_stream * s, unsigned int nframes) 750 { 751 size_t rem = nframes * s->record.frame_size; 752 size_t read_ofs = 0; 753 while (rem > 0) { 754 ssize_t n; 755 if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, rem)) < 756 0) { 757 if (errno == EINTR) 758 continue; 759 return CUBEB_ERROR; 760 } 761 read_ofs += n; 762 rem -= n; 763 } 764 return 0; 765 } 766 767 static int 768 oss_put_play_frames(cubeb_stream * s, unsigned int nframes) 769 { 770 size_t rem = nframes * s->play.frame_size; 771 size_t write_ofs = 0; 772 while (rem > 0) { 773 ssize_t n; 774 if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, rem)) < 0) { 775 if (errno == EINTR) 776 continue; 777 return CUBEB_ERROR; 778 } 779 pthread_mutex_lock(&s->mtx); 780 s->frames_written += n / s->play.frame_size; 781 pthread_mutex_unlock(&s->mtx); 782 write_ofs += n; 783 rem -= n; 784 } 785 return 0; 786 } 787 788 static int 789 oss_wait_fds_for_space(cubeb_stream * s, long * nfrp) 790 { 791 audio_buf_info bi; 792 struct pollfd pfds[2]; 793 long nfr, tnfr; 794 int i; 795 796 assert(s->play.fd != -1 || s->record.fd != -1); 797 pfds[0].events = POLLOUT | POLLHUP; 798 pfds[0].revents = 0; 799 pfds[0].fd = s->play.fd; 800 pfds[1].events = POLLIN | POLLHUP; 801 pfds[1].revents = 0; 802 pfds[1].fd = s->record.fd; 803 804 retry: 805 nfr = LONG_MAX; 806 807 if (poll(pfds, 2, 1000) == -1) { 808 return CUBEB_ERROR; 809 } 810 811 for (i = 0; i < 2; i++) { 812 if (pfds[i].revents & POLLHUP) { 813 return CUBEB_ERROR; 814 } 815 } 816 817 if (s->play.fd != -1) { 818 if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) { 819 return CUBEB_STATE_ERROR; 820 } 821 tnfr = bi.bytes / s->play.frame_size; 822 if (tnfr <= 0) { 823 /* too little space - stop polling record, if any */ 824 pfds[0].fd = s->play.fd; 825 pfds[1].fd = -1; 826 goto retry; 827 } else if (tnfr > (long)s->play.maxframes) { 828 /* too many frames available - limit */ 829 tnfr = (long)s->play.maxframes; 830 } 831 if (nfr > tnfr) { 832 nfr = tnfr; 833 } 834 } 835 if (s->record.fd != -1) { 836 if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi) == -1) { 837 return CUBEB_STATE_ERROR; 838 } 839 tnfr = bi.bytes / s->record.frame_size; 840 if (tnfr <= 0) { 841 /* too little space - stop polling playback, if any */ 842 pfds[0].fd = -1; 843 pfds[1].fd = s->record.fd; 844 goto retry; 845 } else if (tnfr > (long)s->record.maxframes) { 846 /* too many frames available - limit */ 847 tnfr = (long)s->record.maxframes; 848 } 849 if (nfr > tnfr) { 850 nfr = tnfr; 851 } 852 } 853 854 *nfrp = nfr; 855 return 0; 856 } 857 858 /* 1 - Stopped by cubeb_stream_stop, otherwise 0 */ 859 static int 860 oss_audio_loop(cubeb_stream * s, cubeb_state * new_state) 861 { 862 cubeb_state state = CUBEB_STATE_STOPPED; 863 int trig = 0, drain = 0; 864 const bool play_on = s->play.fd != -1, record_on = s->record.fd != -1; 865 long nfr = 0; 866 867 if (record_on) { 868 if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { 869 LOG("Error %d occured when setting trigger on record fd", errno); 870 state = CUBEB_STATE_ERROR; 871 goto breakdown; 872 } 873 874 trig |= PCM_ENABLE_INPUT; 875 memset(s->record.buf, 0, s->record.bufframes * s->record.frame_size); 876 877 if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) { 878 LOG("Error %d occured when setting trigger on record fd", errno); 879 state = CUBEB_STATE_ERROR; 880 goto breakdown; 881 } 882 } 883 884 if (!play_on && !record_on) { 885 /* 886 * Stop here if the stream is not play & record stream, 887 * play-only stream or record-only stream 888 */ 889 890 goto breakdown; 891 } 892 893 while (1) { 894 pthread_mutex_lock(&s->mtx); 895 if (!s->running || s->destroying) { 896 pthread_mutex_unlock(&s->mtx); 897 break; 898 } 899 pthread_mutex_unlock(&s->mtx); 900 901 long got = 0; 902 if (nfr > 0) { 903 if (record_on) { 904 if (oss_get_rec_frames(s, nfr) == CUBEB_ERROR) { 905 state = CUBEB_STATE_ERROR; 906 goto breakdown; 907 } 908 if (s->record.floating) { 909 oss_linear32_to_float(s->record.buf, s->record.info.channels * nfr); 910 } 911 } 912 913 got = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, nfr); 914 if (got == CUBEB_ERROR) { 915 state = CUBEB_STATE_ERROR; 916 goto breakdown; 917 } 918 if (got < nfr) { 919 if (s->play.fd != -1) { 920 drain = 1; 921 } else { 922 /* 923 * This is a record-only stream and number of frames 924 * returned from data_cb() is smaller than number 925 * of frames required to read. Stop here. 926 */ 927 state = CUBEB_STATE_STOPPED; 928 goto breakdown; 929 } 930 } 931 932 if (got > 0 && play_on) { 933 float vol; 934 935 pthread_mutex_lock(&s->mtx); 936 vol = s->volume; 937 pthread_mutex_unlock(&s->mtx); 938 939 if (s->play.floating) { 940 oss_float_to_linear32(s->play.buf, s->play.info.channels * got, vol); 941 } else { 942 oss_linear16_set_vol((int16_t *)s->play.buf, 943 s->play.info.channels * got, vol); 944 } 945 if (oss_put_play_frames(s, got) == CUBEB_ERROR) { 946 state = CUBEB_STATE_ERROR; 947 goto breakdown; 948 } 949 } 950 if (drain) { 951 state = CUBEB_STATE_DRAINED; 952 goto breakdown; 953 } 954 } 955 956 if (oss_wait_fds_for_space(s, &nfr) != 0) { 957 state = CUBEB_STATE_ERROR; 958 goto breakdown; 959 } 960 } 961 962 return 1; 963 964 breakdown: 965 pthread_mutex_lock(&s->mtx); 966 *new_state = s->state = state; 967 s->running = false; 968 pthread_mutex_unlock(&s->mtx); 969 return 0; 970 } 971 972 static void * 973 oss_io_routine(void * arg) 974 { 975 cubeb_stream * s = arg; 976 cubeb_state new_state; 977 int stopped; 978 979 CUBEB_REGISTER_THREAD("cubeb rendering thread"); 980 981 do { 982 pthread_mutex_lock(&s->mtx); 983 if (s->destroying) { 984 pthread_mutex_unlock(&s->mtx); 985 break; 986 } 987 pthread_mutex_unlock(&s->mtx); 988 989 stopped = oss_audio_loop(s, &new_state); 990 if (s->record.fd != -1) 991 ioctl(s->record.fd, SNDCTL_DSP_HALT_INPUT, NULL); 992 if (!stopped) 993 s->state_cb(s, s->user_ptr, new_state); 994 995 pthread_mutex_lock(&s->mtx); 996 pthread_cond_signal(&s->stopped_cv); 997 if (s->destroying) { 998 pthread_mutex_unlock(&s->mtx); 999 break; 1000 } 1001 while (!s->doorbell) { 1002 pthread_cond_wait(&s->doorbell_cv, &s->mtx); 1003 } 1004 s->doorbell = false; 1005 pthread_mutex_unlock(&s->mtx); 1006 } while (1); 1007 1008 pthread_mutex_lock(&s->mtx); 1009 s->thread_created = false; 1010 pthread_mutex_unlock(&s->mtx); 1011 1012 CUBEB_UNREGISTER_THREAD(); 1013 1014 return NULL; 1015 } 1016 1017 static inline int 1018 oss_calc_frag_shift(unsigned int frames, unsigned int frame_size) 1019 { 1020 int n = 4; 1021 int blksize = frames * frame_size; 1022 while ((1 << n) < blksize) { 1023 n++; 1024 } 1025 return n; 1026 } 1027 1028 static inline int 1029 oss_get_frag_params(unsigned int shift) 1030 { 1031 return (OSS_NFRAGS << 16) | shift; 1032 } 1033 1034 static int 1035 oss_stream_init(cubeb * context, cubeb_stream ** stream, 1036 char const * stream_name, cubeb_devid input_device, 1037 cubeb_stream_params * input_stream_params, 1038 cubeb_devid output_device, 1039 cubeb_stream_params * output_stream_params, 1040 unsigned int latency_frames, cubeb_data_callback data_callback, 1041 cubeb_state_callback state_callback, void * user_ptr) 1042 { 1043 int ret = CUBEB_OK; 1044 cubeb_stream * s = NULL; 1045 const char * defdsp; 1046 1047 if (!(defdsp = getenv(ENV_AUDIO_DEVICE)) || *defdsp == '\0') 1048 defdsp = OSS_DEFAULT_DEVICE; 1049 1050 (void)stream_name; 1051 if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) { 1052 ret = CUBEB_ERROR; 1053 goto error; 1054 } 1055 s->state = CUBEB_STATE_STOPPED; 1056 s->record.fd = s->play.fd = -1; 1057 if (input_device != NULL) { 1058 strlcpy(s->record.name, input_device, sizeof(s->record.name)); 1059 } else { 1060 strlcpy(s->record.name, defdsp, sizeof(s->record.name)); 1061 } 1062 if (output_device != NULL) { 1063 strlcpy(s->play.name, output_device, sizeof(s->play.name)); 1064 } else { 1065 strlcpy(s->play.name, defdsp, sizeof(s->play.name)); 1066 } 1067 if (input_stream_params != NULL) { 1068 unsigned int nb_channels; 1069 uint32_t minframes; 1070 1071 if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { 1072 LOG("Loopback not supported"); 1073 ret = CUBEB_ERROR_NOT_SUPPORTED; 1074 goto error; 1075 } 1076 nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout); 1077 if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && 1078 nb_channels != input_stream_params->channels) { 1079 LOG("input_stream_params->layout does not match " 1080 "input_stream_params->channels"); 1081 ret = CUBEB_ERROR_INVALID_PARAMETER; 1082 goto error; 1083 } 1084 if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) { 1085 LOG("Audio device \"%s\" could not be opened as read-only", 1086 s->record.name); 1087 ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; 1088 goto error; 1089 } 1090 if ((ret = oss_copy_params(s->record.fd, s, input_stream_params, 1091 &s->record.info)) != CUBEB_OK) { 1092 LOG("Setting record params failed"); 1093 goto error; 1094 } 1095 s->record.floating = 1096 (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); 1097 s->record.frame_size = 1098 s->record.info.channels * (s->record.info.precision / 8); 1099 s->record.bufframes = latency_frames; 1100 1101 oss_get_min_latency(context, *input_stream_params, &minframes); 1102 if (s->record.bufframes < minframes) { 1103 s->record.bufframes = minframes; 1104 } 1105 } 1106 if (output_stream_params != NULL) { 1107 unsigned int nb_channels; 1108 uint32_t minframes; 1109 1110 if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { 1111 LOG("Loopback not supported"); 1112 ret = CUBEB_ERROR_NOT_SUPPORTED; 1113 goto error; 1114 } 1115 nb_channels = 1116 cubeb_channel_layout_nb_channels(output_stream_params->layout); 1117 if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && 1118 nb_channels != output_stream_params->channels) { 1119 LOG("output_stream_params->layout does not match " 1120 "output_stream_params->channels"); 1121 ret = CUBEB_ERROR_INVALID_PARAMETER; 1122 goto error; 1123 } 1124 if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) { 1125 LOG("Audio device \"%s\" could not be opened as write-only", 1126 s->play.name); 1127 ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; 1128 goto error; 1129 } 1130 if ((ret = oss_copy_params(s->play.fd, s, output_stream_params, 1131 &s->play.info)) != CUBEB_OK) { 1132 LOG("Setting play params failed"); 1133 goto error; 1134 } 1135 s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); 1136 s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8); 1137 s->play.bufframes = latency_frames; 1138 1139 oss_get_min_latency(context, *output_stream_params, &minframes); 1140 if (s->play.bufframes < minframes) { 1141 s->play.bufframes = minframes; 1142 } 1143 } 1144 if (s->play.fd != -1) { 1145 int frag = oss_get_frag_params( 1146 oss_calc_frag_shift(s->play.bufframes, s->play.frame_size)); 1147 if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) 1148 LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", 1149 frag); 1150 audio_buf_info bi; 1151 if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi)) 1152 LOG("Failed to get play fd's buffer info."); 1153 else { 1154 s->play.bufframes = (bi.fragsize * bi.fragstotal) / s->play.frame_size; 1155 } 1156 int lw; 1157 1158 /* 1159 * Force 32 ms service intervals at most, or when recording is 1160 * active, use the recording service intervals as a reference. 1161 */ 1162 s->play.maxframes = (32 * output_stream_params->rate) / 1000; 1163 if (s->record.fd != -1 || s->play.maxframes >= s->play.bufframes) { 1164 lw = s->play.frame_size; /* Feed data when possible. */ 1165 s->play.maxframes = s->play.bufframes; 1166 } else { 1167 lw = (s->play.bufframes - s->play.maxframes) * s->play.frame_size; 1168 } 1169 if (ioctl(s->play.fd, SNDCTL_DSP_LOW_WATER, &lw)) 1170 LOG("Audio device \"%s\" (play) could not set trigger threshold", 1171 s->play.name); 1172 } 1173 if (s->record.fd != -1) { 1174 int frag = oss_get_frag_params( 1175 oss_calc_frag_shift(s->record.bufframes, s->record.frame_size)); 1176 if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) 1177 LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", 1178 frag); 1179 audio_buf_info bi; 1180 if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi)) 1181 LOG("Failed to get record fd's buffer info."); 1182 else { 1183 s->record.bufframes = 1184 (bi.fragsize * bi.fragstotal) / s->record.frame_size; 1185 } 1186 1187 s->record.maxframes = s->record.bufframes; 1188 int lw = s->record.frame_size; 1189 if (ioctl(s->record.fd, SNDCTL_DSP_LOW_WATER, &lw)) 1190 LOG("Audio device \"%s\" (record) could not set trigger threshold", 1191 s->record.name); 1192 } 1193 s->context = context; 1194 s->volume = 1.0; 1195 s->state_cb = state_callback; 1196 s->data_cb = data_callback; 1197 s->user_ptr = user_ptr; 1198 1199 if (pthread_mutex_init(&s->mtx, NULL) != 0) { 1200 LOG("Failed to create mutex"); 1201 goto error; 1202 } 1203 if (pthread_cond_init(&s->doorbell_cv, NULL) != 0) { 1204 LOG("Failed to create cv"); 1205 goto error; 1206 } 1207 if (pthread_cond_init(&s->stopped_cv, NULL) != 0) { 1208 LOG("Failed to create cv"); 1209 goto error; 1210 } 1211 s->doorbell = false; 1212 1213 if (s->play.fd != -1) { 1214 if ((s->play.buf = calloc(s->play.bufframes, s->play.frame_size)) == NULL) { 1215 ret = CUBEB_ERROR; 1216 goto error; 1217 } 1218 } 1219 if (s->record.fd != -1) { 1220 if ((s->record.buf = calloc(s->record.bufframes, s->record.frame_size)) == 1221 NULL) { 1222 ret = CUBEB_ERROR; 1223 goto error; 1224 } 1225 } 1226 1227 *stream = s; 1228 return CUBEB_OK; 1229 error: 1230 if (s != NULL) { 1231 oss_stream_destroy(s); 1232 } 1233 return ret; 1234 } 1235 1236 static int 1237 oss_stream_thr_create(cubeb_stream * s) 1238 { 1239 if (s->thread_created) { 1240 s->doorbell = true; 1241 pthread_cond_signal(&s->doorbell_cv); 1242 return CUBEB_OK; 1243 } 1244 1245 if (pthread_create(&s->thread, NULL, oss_io_routine, s) != 0) { 1246 LOG("Couldn't create thread"); 1247 return CUBEB_ERROR; 1248 } 1249 1250 return CUBEB_OK; 1251 } 1252 1253 static int 1254 oss_stream_start(cubeb_stream * s) 1255 { 1256 s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED); 1257 pthread_mutex_lock(&s->mtx); 1258 /* Disallow starting an already started stream */ 1259 assert(!s->running && s->state != CUBEB_STATE_STARTED); 1260 if (oss_stream_thr_create(s) != CUBEB_OK) { 1261 pthread_mutex_unlock(&s->mtx); 1262 s->state_cb(s, s->user_ptr, CUBEB_STATE_ERROR); 1263 return CUBEB_ERROR; 1264 } 1265 s->state = CUBEB_STATE_STARTED; 1266 s->thread_created = true; 1267 s->running = true; 1268 pthread_mutex_unlock(&s->mtx); 1269 return CUBEB_OK; 1270 } 1271 1272 static int 1273 oss_stream_get_position(cubeb_stream * s, uint64_t * position) 1274 { 1275 pthread_mutex_lock(&s->mtx); 1276 *position = s->frames_written; 1277 pthread_mutex_unlock(&s->mtx); 1278 return CUBEB_OK; 1279 } 1280 1281 static int 1282 oss_stream_get_latency(cubeb_stream * s, uint32_t * latency) 1283 { 1284 int delay; 1285 1286 if (ioctl(s->play.fd, SNDCTL_DSP_GETODELAY, &delay) == -1) { 1287 return CUBEB_ERROR; 1288 } 1289 1290 /* Return number of frames there */ 1291 *latency = delay / s->play.frame_size; 1292 return CUBEB_OK; 1293 } 1294 1295 static int 1296 oss_stream_set_volume(cubeb_stream * stream, float volume) 1297 { 1298 if (volume < 0.0) 1299 volume = 0.0; 1300 else if (volume > 1.0) 1301 volume = 1.0; 1302 pthread_mutex_lock(&stream->mtx); 1303 stream->volume = volume; 1304 pthread_mutex_unlock(&stream->mtx); 1305 return CUBEB_OK; 1306 } 1307 1308 static int 1309 oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device) 1310 { 1311 *device = calloc(1, sizeof(cubeb_device)); 1312 if (*device == NULL) { 1313 return CUBEB_ERROR; 1314 } 1315 (*device)->input_name = 1316 stream->record.fd != -1 ? strdup(stream->record.name) : NULL; 1317 (*device)->output_name = 1318 stream->play.fd != -1 ? strdup(stream->play.name) : NULL; 1319 return CUBEB_OK; 1320 } 1321 1322 static int 1323 oss_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) 1324 { 1325 (void)stream; 1326 free(device->input_name); 1327 free(device->output_name); 1328 free(device); 1329 return CUBEB_OK; 1330 } 1331 1332 static struct cubeb_ops const oss_ops = { 1333 .init = oss_init, 1334 .get_backend_id = oss_get_backend_id, 1335 .get_max_channel_count = oss_get_max_channel_count, 1336 .get_min_latency = oss_get_min_latency, 1337 .get_preferred_sample_rate = oss_get_preferred_sample_rate, 1338 .get_supported_input_processing_params = NULL, 1339 .enumerate_devices = oss_enumerate_devices, 1340 .device_collection_destroy = oss_device_collection_destroy, 1341 .destroy = oss_destroy, 1342 .stream_init = oss_stream_init, 1343 .stream_destroy = oss_stream_destroy, 1344 .stream_start = oss_stream_start, 1345 .stream_stop = oss_stream_stop, 1346 .stream_get_position = oss_stream_get_position, 1347 .stream_get_latency = oss_stream_get_latency, 1348 .stream_get_input_latency = NULL, 1349 .stream_set_volume = oss_stream_set_volume, 1350 .stream_set_name = NULL, 1351 .stream_get_current_device = oss_get_current_device, 1352 .stream_set_input_mute = NULL, 1353 .stream_set_input_processing_params = NULL, 1354 .stream_device_destroy = oss_stream_device_destroy, 1355 .stream_register_device_changed_callback = NULL, 1356 .register_device_collection_changed = NULL};