1 /* 2 * QEMU OS X CoreAudio audio driver 3 * 4 * Copyright (c) 2008 The Android Open Source Project 5 * Copyright (c) 2005 Mike Kronenberg 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 * THE SOFTWARE. 24 */ 25 26 #include <CoreAudio/CoreAudio.h> 27 #include <string.h> /* strerror */ 28 #include <pthread.h> /* pthread_X */ 29 30 #include "qemu-common.h" 31 #include "audio.h" 32 33 #define AUDIO_CAP "coreaudio" 34 #include "audio_int.h" 35 36 #if 0 37 # define D(...) fprintf(stderr, __VA_ARGS__) 38 #else 39 # define D(...) ((void)0) 40 #endif 41 42 struct { 43 int out_buffer_frames; 44 int out_nbuffers; 45 int in_buffer_frames; 46 int in_nbuffers; 47 int isAtexit; 48 } conf = { 49 .out_buffer_frames = 512, 50 .out_nbuffers = 4, 51 .in_buffer_frames = 512, 52 .in_nbuffers = 4, 53 .isAtexit = 0 54 }; 55 56 /***************************************************************************************/ 57 /***************************************************************************************/ 58 /*** ***/ 59 /*** U T I L I T Y R O U T I N E S ***/ 60 /*** ***/ 61 /***************************************************************************************/ 62 /***************************************************************************************/ 63 64 static void coreaudio_logstatus (OSStatus status) 65 { 66 char *str = "BUG"; 67 68 switch(status) { 69 case kAudioHardwareNoError: 70 str = "kAudioHardwareNoError"; 71 break; 72 73 case kAudioHardwareNotRunningError: 74 str = "kAudioHardwareNotRunningError"; 75 break; 76 77 case kAudioHardwareUnspecifiedError: 78 str = "kAudioHardwareUnspecifiedError"; 79 break; 80 81 case kAudioHardwareUnknownPropertyError: 82 str = "kAudioHardwareUnknownPropertyError"; 83 break; 84 85 case kAudioHardwareBadPropertySizeError: 86 str = "kAudioHardwareBadPropertySizeError"; 87 break; 88 89 case kAudioHardwareIllegalOperationError: 90 str = "kAudioHardwareIllegalOperationError"; 91 break; 92 93 case kAudioHardwareBadDeviceError: 94 str = "kAudioHardwareBadDeviceError"; 95 break; 96 97 case kAudioHardwareBadStreamError: 98 str = "kAudioHardwareBadStreamError"; 99 break; 100 101 case kAudioHardwareUnsupportedOperationError: 102 str = "kAudioHardwareUnsupportedOperationError"; 103 break; 104 105 case kAudioDeviceUnsupportedFormatError: 106 str = "kAudioDeviceUnsupportedFormatError"; 107 break; 108 109 case kAudioDevicePermissionsError: 110 str = "kAudioDevicePermissionsError"; 111 break; 112 113 default: 114 AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); 115 return; 116 } 117 118 AUD_log (AUDIO_CAP, "Reason: %s\n", str); 119 } 120 121 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( 122 OSStatus status, 123 const char *fmt, 124 ... 125 ) 126 { 127 va_list ap; 128 129 va_start (ap, fmt); 130 AUD_log (AUDIO_CAP, fmt, ap); 131 va_end (ap); 132 133 coreaudio_logstatus (status); 134 } 135 136 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( 137 OSStatus status, 138 const char *typ, 139 const char *fmt, 140 ... 141 ) 142 { 143 va_list ap; 144 145 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); 146 147 va_start (ap, fmt); 148 AUD_vlog (AUDIO_CAP, fmt, ap); 149 va_end (ap); 150 151 coreaudio_logstatus (status); 152 } 153 154 /***************************************************************************************/ 155 /***************************************************************************************/ 156 /*** ***/ 157 /*** S H A R E D I N / O U T V O I C E ***/ 158 /*** ***/ 159 /***************************************************************************************/ 160 /***************************************************************************************/ 161 162 typedef struct coreAudioVoice { 163 pthread_mutex_t mutex; 164 AudioDeviceID deviceID; 165 Boolean isInput; 166 UInt32 bufferFrameSize; 167 AudioStreamBasicDescription streamBasicDescription; 168 AudioDeviceIOProc ioproc; 169 int live; 170 int decr; 171 int pos; 172 } coreaudioVoice; 173 174 static inline UInt32 175 coreaudio_voice_isPlaying (coreaudioVoice *core) 176 { 177 OSStatus status; 178 UInt32 result = 0; 179 UInt32 propertySize = sizeof(core->deviceID); 180 status = AudioDeviceGetProperty( 181 core->deviceID, 0, core->isInput, 182 kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); 183 if (status != kAudioHardwareNoError) { 184 coreaudio_logerr(status, 185 "Could not determine whether Device is playing\n"); 186 } 187 return result; 188 } 189 190 static void coreaudio_atexit (void) 191 { 192 conf.isAtexit = 1; 193 } 194 195 static int coreaudio_voice_lock (coreaudioVoice *core, const char *fn_name) 196 { 197 int err; 198 199 err = pthread_mutex_lock (&core->mutex); 200 if (err) { 201 dolog ("Could not lock voice for %s\nReason: %s\n", 202 fn_name, strerror (err)); 203 return -1; 204 } 205 return 0; 206 } 207 208 static int 209 coreaudio_voice_unlock (coreaudioVoice *core, const char *fn_name) 210 { 211 int err; 212 213 err = pthread_mutex_unlock (&core->mutex); 214 if (err) { 215 dolog ("Could not unlock voice for %s\nReason: %s\n", 216 fn_name, strerror (err)); 217 return -1; 218 } 219 return 0; 220 } 221 222 static int 223 coreaudio_voice_ctl (coreaudioVoice* core, int cmd) 224 { 225 OSStatus status; 226 227 switch (cmd) { 228 case VOICE_ENABLE: 229 /* start playback */ 230 D("%s: %s started\n", __FUNCTION__, core->isInput ? "input" : "output"); 231 if (!coreaudio_voice_isPlaying(core)) { 232 status = AudioDeviceStart(core->deviceID, core->ioproc); 233 if (status != kAudioHardwareNoError) { 234 coreaudio_logerr (status, "Could not resume playback\n"); 235 } 236 } 237 break; 238 239 case VOICE_DISABLE: 240 /* stop playback */ 241 D("%s: %s stopped\n", __FUNCTION__, core->isInput ? "input" : "output"); 242 if (!conf.isAtexit) { 243 if (coreaudio_voice_isPlaying(core)) { 244 status = AudioDeviceStop(core->deviceID, core->ioproc); 245 if (status != kAudioHardwareNoError) { 246 coreaudio_logerr (status, "Could not pause playback\n"); 247 } 248 } 249 } 250 break; 251 } 252 return 0; 253 } 254 255 static void 256 coreaudio_voice_fini (coreaudioVoice* core) 257 { 258 OSStatus status; 259 int err; 260 261 if (!conf.isAtexit) { 262 /* stop playback */ 263 coreaudio_voice_ctl(core, VOICE_DISABLE); 264 265 /* remove callback */ 266 status = AudioDeviceRemoveIOProc(core->deviceID, core->ioproc); 267 if (status != kAudioHardwareNoError) { 268 coreaudio_logerr (status, "Could not remove IOProc\n"); 269 } 270 } 271 core->deviceID = kAudioDeviceUnknown; 272 273 /* destroy mutex */ 274 err = pthread_mutex_destroy(&core->mutex); 275 if (err) { 276 dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); 277 } 278 } 279 280 281 static int 282 coreaudio_voice_init (coreaudioVoice* core, 283 struct audsettings* as, 284 int frameSize, 285 AudioDeviceIOProc ioproc, 286 void* hw, 287 int input) 288 { 289 OSStatus status; 290 UInt32 propertySize; 291 int err; 292 int bits = 8; 293 AudioValueRange frameRange; 294 const char* typ = input ? "input" : "playback"; 295 296 core->isInput = input ? true : false; 297 298 /* create mutex */ 299 err = pthread_mutex_init(&core->mutex, NULL); 300 if (err) { 301 dolog("Could not create mutex\nReason: %s\n", strerror (err)); 302 return -1; 303 } 304 305 if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) { 306 bits = 16; 307 } 308 309 // TODO: audio_pcm_init_info (&hw->info, as); 310 /* open default output device */ 311 /* note: we use DefaultSystemOutputDevice because DefaultOutputDevice seems to 312 * always link to the internal speakers, and not the ones selected through system properties 313 * go figure... 314 */ 315 propertySize = sizeof(core->deviceID); 316 status = AudioHardwareGetProperty( 317 input ? kAudioHardwarePropertyDefaultInputDevice : 318 kAudioHardwarePropertyDefaultSystemOutputDevice, 319 &propertySize, 320 &core->deviceID); 321 if (status != kAudioHardwareNoError) { 322 coreaudio_logerr2 (status, typ, 323 "Could not get default %s device\n", typ); 324 return -1; 325 } 326 if (core->deviceID == kAudioDeviceUnknown) { 327 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); 328 return -1; 329 } 330 331 /* get minimum and maximum buffer frame sizes */ 332 propertySize = sizeof(frameRange); 333 status = AudioDeviceGetProperty( 334 core->deviceID, 335 0, 336 core->isInput, 337 kAudioDevicePropertyBufferFrameSizeRange, 338 &propertySize, 339 &frameRange); 340 if (status != kAudioHardwareNoError) { 341 coreaudio_logerr2 (status, typ, 342 "Could not get device buffer frame range\n"); 343 return -1; 344 } 345 346 if (frameRange.mMinimum > frameSize) { 347 core->bufferFrameSize = (UInt32) frameRange.mMinimum; 348 dolog ("warning: Upsizing Output Buffer Frames to %f\n", frameRange.mMinimum); 349 } 350 else if (frameRange.mMaximum < frameSize) { 351 core->bufferFrameSize = (UInt32) frameRange.mMaximum; 352 dolog ("warning: Downsizing Output Buffer Frames to %f\n", frameRange.mMaximum); 353 } 354 else { 355 core->bufferFrameSize = frameSize; 356 } 357 358 /* set Buffer Frame Size */ 359 propertySize = sizeof(core->bufferFrameSize); 360 status = AudioDeviceSetProperty( 361 core->deviceID, 362 NULL, 363 0, 364 core->isInput, 365 kAudioDevicePropertyBufferFrameSize, 366 propertySize, 367 &core->bufferFrameSize); 368 if (status != kAudioHardwareNoError) { 369 coreaudio_logerr2 (status, typ, 370 "Could not set device buffer frame size %ld\n", 371 core->bufferFrameSize); 372 return -1; 373 } 374 375 /* get Buffer Frame Size */ 376 propertySize = sizeof(core->bufferFrameSize); 377 status = AudioDeviceGetProperty( 378 core->deviceID, 379 0, 380 core->isInput, 381 kAudioDevicePropertyBufferFrameSize, 382 &propertySize, 383 &core->bufferFrameSize); 384 if (status != kAudioHardwareNoError) { 385 coreaudio_logerr2 (status, typ, 386 "Could not get device buffer frame size\n"); 387 return -1; 388 } 389 // TODO: hw->samples = *pNBuffers * core->bufferFrameSize; 390 391 /* get StreamFormat */ 392 propertySize = sizeof(core->streamBasicDescription); 393 status = AudioDeviceGetProperty( 394 core->deviceID, 395 0, 396 core->isInput, 397 kAudioDevicePropertyStreamFormat, 398 &propertySize, 399 &core->streamBasicDescription); 400 if (status != kAudioHardwareNoError) { 401 coreaudio_logerr2 (status, typ, 402 "Could not get Device Stream properties\n"); 403 core->deviceID = kAudioDeviceUnknown; 404 return -1; 405 } 406 407 /* set Samplerate */ 408 core->streamBasicDescription.mSampleRate = (Float64) as->freq; 409 propertySize = sizeof(core->streamBasicDescription); 410 status = AudioDeviceSetProperty( 411 core->deviceID, 412 0, 413 0, 414 core->isInput, 415 kAudioDevicePropertyStreamFormat, 416 propertySize, 417 &core->streamBasicDescription); 418 if (status != kAudioHardwareNoError) { 419 coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", 420 as->freq); 421 core->deviceID = kAudioDeviceUnknown; 422 return -1; 423 } 424 425 /* set Callback */ 426 core->ioproc = ioproc; 427 status = AudioDeviceAddIOProc(core->deviceID, ioproc, hw); 428 if (status != kAudioHardwareNoError) { 429 coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); 430 core->deviceID = kAudioDeviceUnknown; 431 return -1; 432 } 433 434 /* start Playback */ 435 if (!input && !coreaudio_voice_isPlaying(core)) { 436 status = AudioDeviceStart(core->deviceID, core->ioproc); 437 if (status != kAudioHardwareNoError) { 438 coreaudio_logerr2 (status, typ, "Could not start playback\n"); 439 AudioDeviceRemoveIOProc(core->deviceID, core->ioproc); 440 core->deviceID = kAudioDeviceUnknown; 441 return -1; 442 } 443 } 444 445 return 0; 446 } 447 448 449 /***************************************************************************************/ 450 /***************************************************************************************/ 451 /*** ***/ 452 /*** O U T P U T V O I C E ***/ 453 /*** ***/ 454 /***************************************************************************************/ 455 /***************************************************************************************/ 456 457 typedef struct coreaudioVoiceOut { 458 HWVoiceOut hw; 459 coreaudioVoice core[1]; 460 } coreaudioVoiceOut; 461 462 #define CORE_OUT(hw) ((coreaudioVoiceOut*)(hw))->core 463 464 465 static int coreaudio_run_out (HWVoiceOut *hw, int live) 466 { 467 int decr; 468 coreaudioVoice *core = CORE_OUT(hw); 469 470 if (coreaudio_voice_lock (core, "coreaudio_run_out")) { 471 return 0; 472 } 473 474 if (core->decr > live) { 475 ldebug ("core->decr %d live %d core->live %d\n", 476 core->decr, 477 live, 478 core->live); 479 } 480 481 decr = audio_MIN (core->decr, live); 482 core->decr -= decr; 483 484 core->live = live - decr; 485 hw->rpos = core->pos; 486 487 coreaudio_voice_unlock (core, "coreaudio_run_out"); 488 return decr; 489 } 490 491 /* callback to feed audiooutput buffer */ 492 static OSStatus audioOutDeviceIOProc( 493 AudioDeviceID inDevice, 494 const AudioTimeStamp* inNow, 495 const AudioBufferList* inInputData, 496 const AudioTimeStamp* inInputTime, 497 AudioBufferList* outOutputData, 498 const AudioTimeStamp* inOutputTime, 499 void* hwptr) 500 { 501 UInt32 frame, frameCount; 502 float *out = outOutputData->mBuffers[0].mData; 503 HWVoiceOut *hw = hwptr; 504 coreaudioVoice *core = CORE_OUT(hw); 505 int rpos, live; 506 struct st_sample *src; 507 #ifndef FLOAT_MIXENG 508 #ifdef RECIPROCAL 509 const float scale = 1.f / UINT_MAX; 510 #else 511 const float scale = UINT_MAX; 512 #endif 513 #endif 514 515 if (coreaudio_voice_lock (core, "audioDeviceIOProc")) { 516 inInputTime = 0; 517 return 0; 518 } 519 520 frameCount = core->bufferFrameSize; 521 live = core->live; 522 523 /* if there are not enough samples, set signal and return */ 524 if (live < frameCount) { 525 inInputTime = 0; 526 coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)"); 527 return 0; 528 } 529 530 rpos = core->pos; 531 src = hw->mix_buf + rpos; 532 533 /* fill buffer */ 534 for (frame = 0; frame < frameCount; frame++) { 535 #ifdef FLOAT_MIXENG 536 *out++ = src[frame].l; /* left channel */ 537 *out++ = src[frame].r; /* right channel */ 538 #else 539 #ifdef RECIPROCAL 540 *out++ = src[frame].l * scale; /* left channel */ 541 *out++ = src[frame].r * scale; /* right channel */ 542 #else 543 *out++ = src[frame].l / scale; /* left channel */ 544 *out++ = src[frame].r / scale; /* right channel */ 545 #endif 546 #endif 547 } 548 549 rpos = (rpos + frameCount) % hw->samples; 550 core->decr += frameCount; 551 core->pos = rpos; 552 553 coreaudio_voice_unlock (core, "audioDeviceIOProc"); 554 return 0; 555 } 556 557 static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) 558 { 559 return audio_pcm_sw_write (sw, buf, len); 560 } 561 562 static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) 563 { 564 coreaudioVoice* core = CORE_OUT(hw); 565 int err; 566 567 audio_pcm_init_info (&hw->info, as); 568 569 err = coreaudio_voice_init (core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0); 570 if (err < 0) 571 return err; 572 573 hw->samples = core->bufferFrameSize * conf.out_nbuffers; 574 return 0; 575 } 576 577 static void coreaudio_fini_out (HWVoiceOut *hw) 578 { 579 coreaudioVoice *core = CORE_OUT(hw); 580 581 coreaudio_voice_fini (core); 582 } 583 584 static int 585 coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) 586 { 587 coreaudioVoice *core = CORE_OUT(hw); 588 589 return coreaudio_voice_ctl (core, cmd); 590 } 591 592 /***************************************************************************************/ 593 /***************************************************************************************/ 594 /*** ***/ 595 /*** I N P U T V O I C E ***/ 596 /*** ***/ 597 /***************************************************************************************/ 598 /***************************************************************************************/ 599 600 601 602 typedef struct coreaudioVoiceIn { 603 HWVoiceIn hw; 604 coreaudioVoice core[1]; 605 } coreaudioVoiceIn; 606 607 #define CORE_IN(hw) ((coreaudioVoiceIn *) (hw))->core 608 609 610 static int coreaudio_run_in (HWVoiceIn *hw, int live) 611 { 612 int decr; 613 614 coreaudioVoice *core = CORE_IN(hw); 615 616 if (coreaudio_voice_lock (core, "coreaudio_run_in")) { 617 return 0; 618 } 619 D("%s: core.decr=%d core.pos=%d\n", __FUNCTION__, core->decr, core->pos); 620 decr = core->decr; 621 core->decr -= decr; 622 hw->wpos = core->pos; 623 624 coreaudio_voice_unlock (core, "coreaudio_run_in"); 625 return decr; 626 } 627 628 629 /* callback to feed audiooutput buffer */ 630 static OSStatus audioInDeviceIOProc( 631 AudioDeviceID inDevice, 632 const AudioTimeStamp* inNow, 633 const AudioBufferList* inInputData, 634 const AudioTimeStamp* inInputTime, 635 AudioBufferList* outOutputData, 636 const AudioTimeStamp* inOutputTime, 637 void* hwptr) 638 { 639 UInt32 frame, frameCount; 640 float *in = inInputData->mBuffers[0].mData; 641 HWVoiceIn *hw = hwptr; 642 coreaudioVoice *core = CORE_IN(hw); 643 int wpos, avail; 644 struct st_sample *dst; 645 #ifndef FLOAT_MIXENG 646 #ifdef RECIPROCAL 647 const float scale = 1.f / UINT_MAX; 648 #else 649 const float scale = UINT_MAX; 650 #endif 651 #endif 652 653 if (coreaudio_voice_lock (core, "audioDeviceIOProc")) { 654 inInputTime = 0; 655 return 0; 656 } 657 658 frameCount = core->bufferFrameSize; 659 avail = hw->samples - hw->total_samples_captured - core->decr; 660 661 D("%s: enter avail=%d core.decr=%d core.pos=%d hw.samples=%d hw.total_samples_captured=%d frameCount=%d\n", 662 __FUNCTION__, avail, core->decr, core->pos, hw->samples, hw->total_samples_captured, (int)frameCount); 663 664 /* if there are not enough samples, set signal and return */ 665 if (avail < frameCount) { 666 inInputTime = 0; 667 coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)"); 668 return 0; 669 } 670 671 wpos = core->pos; 672 dst = hw->conv_buf + wpos; 673 674 /* fill buffer */ 675 for (frame = 0; frame < frameCount; frame++) { 676 #ifdef FLOAT_MIXENG 677 dst[frame].l = *in++; /* left channel */ 678 dst[frame].r = *in++; /* right channel */ 679 #else 680 #ifdef RECIPROCAL 681 dst[frame].l = *in++ * scale; /* left channel */ 682 dst[frame].r = *in++ * scale; /* right channel */ 683 #else 684 dst[frame].l = *in++ / scale; /* left channel */ 685 dst[frame].r = *in++ / scale; /* right channel */ 686 #endif 687 #endif 688 } 689 690 wpos = (wpos + frameCount) % hw->samples; 691 core->decr += frameCount; 692 core->pos = wpos; 693 694 D("exit: core.decr=%d core.pos=%d\n", core->decr, core->pos); 695 coreaudio_voice_unlock (core, "audioDeviceIOProc"); 696 return 0; 697 } 698 699 static int 700 coreaudio_read (SWVoiceIn *sw, void *buf, int len) 701 { 702 int result = audio_pcm_sw_read (sw, buf, len); 703 D("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result); 704 return result; 705 } 706 707 static int 708 coreaudio_init_in (HWVoiceIn *hw, struct audsettings *as) 709 { 710 coreaudioVoice* core = CORE_IN(hw); 711 int err; 712 713 audio_pcm_init_info (&hw->info, as); 714 715 err = coreaudio_voice_init (core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1); 716 if (err < 0) { 717 return err; 718 } 719 720 hw->samples = core->bufferFrameSize * conf.in_nbuffers; 721 return 0; 722 } 723 724 static void 725 coreaudio_fini_in (HWVoiceIn *hw) 726 { 727 728 coreaudioVoice* core = CORE_IN(hw); 729 730 coreaudio_voice_fini(core); 731 } 732 733 static int 734 coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...) 735 { 736 coreaudioVoice* core = CORE_IN(hw); 737 738 return coreaudio_voice_ctl(core, cmd); 739 } 740 741 static void *coreaudio_audio_init (void) 742 { 743 atexit(coreaudio_atexit); 744 return &coreaudio_audio_init; 745 } 746 747 static void coreaudio_audio_fini (void *opaque) 748 { 749 (void) opaque; 750 } 751 752 static struct audio_option coreaudio_options[] = { 753 { 754 .name = "OUT_BUFFER_SIZE", 755 .tag = AUD_OPT_INT, 756 .valp = &conf.out_buffer_frames, 757 .descr = "Size of the output buffer in frames" 758 }, 759 { 760 .name = "OUT_BUFFER_COUNT", 761 .tag = AUD_OPT_INT, 762 .valp = &conf.out_nbuffers, 763 .descr = "Number of output buffers" 764 }, 765 { 766 .name = "IN_BUFFER_SIZE", 767 .tag = AUD_OPT_INT, 768 .valp = &conf.in_buffer_frames, 769 .descr = "Size of the input buffer in frames" 770 }, 771 { 772 .name = "IN_BUFFER_COUNT", 773 .tag = AUD_OPT_INT, 774 .valp = &conf.in_nbuffers, 775 .descr = "Number of input buffers" 776 }, 777 { /* End of list */ } 778 }; 779 780 static struct audio_pcm_ops coreaudio_pcm_ops = { 781 .init_out = coreaudio_init_out, 782 .fini_out = coreaudio_fini_out, 783 .run_out = coreaudio_run_out, 784 .write = coreaudio_write, 785 .ctl_out = coreaudio_ctl_out, 786 787 .init_in = coreaudio_init_in, 788 .fini_in = coreaudio_fini_in, 789 .run_in = coreaudio_run_in, 790 .read = coreaudio_read, 791 .ctl_in = coreaudio_ctl_in 792 }; 793 794 struct audio_driver coreaudio_audio_driver = { 795 .name = "coreaudio", 796 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", 797 .options = coreaudio_options, 798 .init = coreaudio_audio_init, 799 .fini = coreaudio_audio_fini, 800 .pcm_ops = &coreaudio_pcm_ops, 801 .can_be_default = 1, 802 .max_voices_out = 1, 803 .max_voices_in = 1, 804 .voice_size_out = sizeof (coreaudioVoiceOut), 805 .voice_size_in = sizeof (coreaudioVoiceIn), 806 }; 807