Changeset 26100 in vbox
- Timestamp:
- Jan 29, 2010 1:43:11 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/coreaudio.c
r25917 r26100 36 36 37 37 /* todo: 38 * - checking for properties changes of the devices39 * - checking for changing of the default device40 * - let the user set the device used (use config)41 * - try to set frame size (use config)42 38 * - maybe make sure the threads are immediately stopped if playing/recording stops 43 39 */ … … 50 46 * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html 51 47 */ 48 49 /*#define CA_EXTENSIVE_LOGGING*/ 52 50 53 51 /******************************************************************************* … … 121 119 { 122 120 AssertPtr(pBuffer); 123 return pBuffer->cBufSize - pBuffer->cBufferUsed;121 return pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed); 124 122 } 125 123 … … 127 125 { 128 126 AssertPtr(pBuffer); 129 return pBuffer->cBufferUsed;127 return ASMAtomicReadU32(&pBuffer->cBufferUsed); 130 128 } 131 129 … … 147 145 148 146 /* How much is in use? */ 149 uUsed = ASMAtomic AddU32(&pBuffer->cBufferUsed, 0);147 uUsed = ASMAtomicReadU32(&pBuffer->cBufferUsed); 150 148 if (uUsed > 0) 151 149 { … … 184 182 185 183 /* How much is free? */ 186 uFree = pBuffer->cBufSize - ASMAtomic AddU32(&pBuffer->cBufferUsed, 0);184 uFree = pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed); 187 185 if (uFree > 0) 188 186 { … … 250 248 #endif /* DEBUG */ 251 249 252 static void ca AudioSettingsToAudioStreamBasicDescription(const audsettings_t *pAS, AudioStreamBasicDescription *pStreamDesc)250 static void caPCMInfoToAudioStreamBasicDescription(struct audio_pcm_info *pInfo, AudioStreamBasicDescription *pStreamDesc) 253 251 { 254 252 pStreamDesc->mFormatID = kAudioFormatLinearPCM; 255 253 pStreamDesc->mFormatFlags = kAudioFormatFlagIsPacked; 256 254 pStreamDesc->mFramesPerPacket = 1; 257 pStreamDesc->mSampleRate = (Float64)pAS->freq; 258 pStreamDesc->mChannelsPerFrame = pAS->nchannels; 259 switch (pAS->fmt) 260 { 261 case AUD_FMT_U8: 262 { 263 pStreamDesc->mBitsPerChannel = 8; 264 break; 265 } 266 case AUD_FMT_S8: 267 { 268 pStreamDesc->mBitsPerChannel = 8; 269 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger; 270 break; 271 } 272 case AUD_FMT_U16: 273 { 274 pStreamDesc->mBitsPerChannel = 16; 275 break; 276 } 277 case AUD_FMT_S16: 278 { 279 pStreamDesc->mBitsPerChannel = 16; 280 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger; 281 break; 282 } 283 #ifdef PA_SAMPLE_S32LE 284 case AUD_FMT_U32: 285 { 286 pStreamDesc->mBitsPerChannel = 32; 287 break; 288 } 289 case AUD_FMT_S32: 290 { 291 pStreamDesc->mBitsPerChannel = 32; 292 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger; 293 break; 294 } 295 #endif 296 default: 297 break; 298 } 255 pStreamDesc->mSampleRate = (Float64)pInfo->freq; 256 pStreamDesc->mChannelsPerFrame = pInfo->nchannels; 257 pStreamDesc->mBitsPerChannel = pInfo->bits; 258 if (pInfo->sign == 1) 259 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger; 299 260 pStreamDesc->mBytesPerFrame = pStreamDesc->mChannelsPerFrame * (pStreamDesc->mBitsPerChannel / 8); 300 261 pStreamDesc->mBytesPerPacket = pStreamDesc->mFramesPerPacket * pStreamDesc->mBytesPerFrame; … … 342 303 return err; 343 304 pRange = RTMemAllocZ(cSize); 344 if ( VALID_PTR(pRange))305 if (RT_VALID_PTR(pRange)) 345 306 { 346 307 err = AudioDeviceGetProperty(device, … … 399 360 UInt32 uFlag = 0; 400 361 UInt32 uSize = sizeof(uFlag); 362 401 363 err = AudioDeviceGetProperty(deviceID, 402 364 0, … … 410 372 } 411 373 374 static char* caCFStringToCString(const CFStringRef pCFString) 375 { 376 const char *pszTmp = NULL; 377 char *pszResult = NULL; 378 CFIndex cLen; 379 380 /* First try to get the pointer directly. */ 381 pszTmp = CFStringGetCStringPtr(pCFString, kCFStringEncodingUTF8); 382 if (pszTmp) 383 /* On success make a copy */ 384 pszResult = RTStrDup(pszTmp); 385 else 386 { 387 /* If the pointer isn't available directly, we have to make a copy. */ 388 cLen = CFStringGetLength(pCFString) + 1; 389 pszResult = RTMemAlloc(cLen * sizeof(char)); 390 if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8)) 391 { 392 RTMemFree(pszResult); 393 pszResult = NULL; 394 } 395 } 396 397 return pszResult; 398 } 399 400 static AudioDeviceID caDeviceUIDtoID(const char* pszUID) 401 { 402 OSStatus err = noErr; 403 UInt32 uSize; 404 AudioValueTranslation translation; 405 CFStringRef strUID; 406 AudioDeviceID audioId; 407 408 /* Create a CFString out of our CString */ 409 strUID = CFStringCreateWithCString(NULL, 410 pszUID, 411 kCFStringEncodingMacRoman); 412 413 /* Fill the translation structure */ 414 translation.mInputData = &strUID; 415 translation.mInputDataSize = sizeof(CFStringRef); 416 translation.mOutputData = &audioId; 417 translation.mOutputDataSize = sizeof(AudioDeviceID); 418 uSize = sizeof(AudioValueTranslation); 419 /* Fetch the translation from the UID to the audio Id */ 420 err = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, 421 &uSize, 422 &translation); 423 /* Release the temporary CFString */ 424 CFRelease(strUID); 425 426 if (RT_LIKELY(err == noErr)) 427 return audioId; 428 /* Return the unknown device on error */ 429 return kAudioDeviceUnknown; 430 } 431 412 432 /******************************************************************************* 413 433 * … … 416 436 ******************************************************************************/ 417 437 438 /* Initialization status indicator used for the recreation of the AudioUnits. */ 439 #define CA_STATUS_UNINIT UINT32_C(0) /* The device is uninitialized */ 440 #define CA_STATUS_IN_INIT UINT32_C(1) /* The device is currently initializing */ 441 #define CA_STATUS_INIT UINT32_C(2) /* The device is initialized */ 442 #define CA_STATUS_IN_UNINIT UINT32_C(3) /* The device is currently uninitializing */ 443 #define CA_STATUS_REINIT UINT32_C(4) /* The device has to be reinitialized */ 444 445 /* Error code which indicates "End of data" */ 446 static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */ 447 418 448 struct 419 449 { 420 int cBufferFrames; 450 const char *pszOutputDeviceUID; 451 const char *pszInputDeviceUID; 421 452 } conf = 422 453 { 423 INIT_FIELD(.cBufferFrames =) 512 454 INIT_FIELD(.pszOutputDeviceUID =) NULL, 455 INIT_FIELD(.pszInputDeviceUID =) NULL 424 456 }; 425 457 … … 438 470 /* A ring buffer for transferring data to the playback thread */ 439 471 PIORINGBUFFER pBuf; 472 /* Initialization status tracker. Used when some of the device parameters 473 * or the device itself is changed during the runtime. */ 474 volatile uint32_t status; 440 475 } caVoiceOut; 441 476 … … 462 497 /* A ring buffer for transferring data from the recording thread */ 463 498 PIORINGBUFFER pBuf; 499 /* Initialization status tracker. Used when some of the device parameters 500 * or the device itself is changed during the runtime. */ 501 volatile uint32_t status; 464 502 } caVoiceIn; 465 503 466 /* Error code which indicates "End of data" */ 467 static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */ 504 #ifdef CA_EXTENSIVE_LOGGING 505 # define CA_EXT_DEBUG_LOG(a) Log2(a) 506 #else 507 # define CA_EXT_DEBUG_LOG(a) do {} while(0) 508 #endif 468 509 469 510 /******************************************************************************* … … 473 514 ******************************************************************************/ 474 515 475 /* callback to feed audio output buffer */ 516 /* We need some forward declarations */ 517 static int coreaudio_run_out(HWVoiceOut *hw); 518 static int coreaudio_write(SWVoiceOut *sw, void *buf, int len); 519 static int coreaudio_ctl_out(HWVoiceOut *hw, int cmd, ...); 520 static void coreaudio_fini_out(HWVoiceOut *hw); 521 static int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as); 522 static int caInitOutput(HWVoiceOut *hw); 523 static void caReinitOutput(HWVoiceOut *hw); 524 525 /* Callback for getting notified when the default output device was changed */ 526 static OSStatus caPlaybackDefaultDeviceChanged(AudioHardwarePropertyID inPropertyID, 527 void *inClientData) 528 { 529 OSStatus err = noErr; 530 UInt32 uSize = 0; 531 UInt32 ad = 0; 532 bool fRun = false; 533 534 caVoiceOut *caVoice = (caVoiceOut *) inClientData; 535 536 switch (inPropertyID) 537 { 538 case kAudioHardwarePropertyDefaultOutputDevice: 539 { 540 /* This listener is called on every change of the hardware 541 * device. So check if the default device has really changed. */ 542 uSize = sizeof(caVoice->audioDeviceId); 543 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, 544 &uSize, 545 &ad); 546 if (caVoice->audioDeviceId != ad) 547 { 548 Log2(("CoreAudio: [Output] Default output device changed!\n")); 549 /* We move the reinitialization to the next output event. 550 * This make sure this thread isn't blocked and the 551 * reinitialization is done when necessary only. */ 552 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_REINIT); 553 } 554 break; 555 } 556 } 557 558 return noErr; 559 } 560 561 /* Callback for getting notified when some of the properties of an audio device has changed */ 562 static OSStatus caPlaybackAudioDevicePropertyChanged(AudioDeviceID inDevice, 563 UInt32 inChannel, 564 Boolean isInput, 565 AudioDevicePropertyID inPropertyID, 566 void *inClientData) 567 { 568 switch (inPropertyID) 569 { 570 #ifdef DEBUG 571 case kAudioDeviceProcessorOverload: 572 { 573 Log2(("CoreAudio: [Output] Processor overload detected!\n")); 574 break; 575 } 576 #endif /* DEBUG */ 577 default: break; 578 } 579 580 return noErr; 581 } 582 583 /* Callback to feed audio output buffer */ 476 584 static OSStatus caPlaybackCallback(void* inRefCon, 477 585 AudioUnitRenderActionFlags* ioActionFlags, … … 489 597 caVoiceOut *caVoice = (caVoiceOut *) inRefCon; 490 598 599 if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT) 600 return noErr; 601 491 602 /* How much space is used in the ring buffer? */ 492 603 csAvail = IORingBufferUsed(caVoice->pBuf) >> caVoice->hw.info.shift; /* bytes -> samples */ … … 495 606 csAvail = RT_MIN(csAvail, ioData->mBuffers[0].mDataByteSize >> caVoice->hw.info.shift); 496 607 497 Log2(("CoreAudio: [Output] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));608 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift)); 498 609 499 610 /* Iterate as long as data is available */ … … 503 614 csToRead = csAvail - csReads; 504 615 cbToRead = csToRead << caVoice->hw.info.shift; /* samples -> bytes */ 505 Log2(("CoreAudio: [Output] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead));616 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead)); 506 617 /* Try to aquire the necessary block from the ring buffer. */ 507 618 IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead); 508 619 /* How much to we get? */ 509 620 csToRead = cbToRead >> caVoice->hw.info.shift; /* bytes -> samples */ 510 Log2(("CoreAudio: [Output] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead));621 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead)); 511 622 /* Break if nothing is used anymore. */ 512 623 if (RT_UNLIKELY(cbToRead == 0)) … … 522 633 ioData->mBuffers[0].mDataByteSize = csReads << caVoice->hw.info.shift; /* samples -> bytes */ 523 634 524 Log2(("CoreAudio: [Output] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));635 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift)); 525 636 526 637 return noErr; 638 } 639 640 static int caInitOutput(HWVoiceOut *hw) 641 { 642 OSStatus err = noErr; 643 UInt32 uSize = 0; /* temporary size of properties */ 644 UInt32 uFlag = 0; /* for setting flags */ 645 CFStringRef name; /* for the temporary device name fetching */ 646 char *pszName; 647 char *pszUID; 648 ComponentDescription cd; /* description for an audio component */ 649 Component cp; /* an audio component */ 650 AURenderCallbackStruct cb; /* holds the callback structure */ 651 UInt32 cFrames; /* default frame count */ 652 UInt32 cSamples; /* samples count */ 653 654 caVoiceOut *caVoice = (caVoiceOut *) hw; 655 656 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_INIT); 657 658 if (caVoice->audioDeviceId == kAudioDeviceUnknown) 659 { 660 /* Fetch the default audio output device currently in use */ 661 uSize = sizeof(caVoice->audioDeviceId); 662 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, 663 &uSize, 664 &caVoice->audioDeviceId); 665 if (RT_UNLIKELY(err != noErr)) 666 { 667 LogRel(("CoreAudio: [Output] Unable to find default output device (%RI32)\n", err)); 668 return -1; 669 } 670 } 671 672 /* Try to get the name of the output device and log it. It's not fatal if 673 * it fails. */ 674 uSize = sizeof(CFStringRef); 675 err = AudioDeviceGetProperty(caVoice->audioDeviceId, 676 0, 677 0, 678 kAudioObjectPropertyName, 679 &uSize, 680 &name); 681 if (RT_LIKELY(err == noErr)) 682 { 683 pszName = caCFStringToCString(name); 684 CFRelease(name); 685 err = AudioDeviceGetProperty(caVoice->audioDeviceId, 686 0, 687 0, 688 kAudioDevicePropertyDeviceUID, 689 &uSize, 690 &name); 691 if (RT_LIKELY(err == noErr)) 692 { 693 pszUID = caCFStringToCString(name); 694 CFRelease(name); 695 if (pszName && pszUID) 696 LogRel(("CoreAudio: Using output device: %s (UID: %s)\n", pszName, pszUID)); 697 RTStrFree(pszName); 698 } 699 RTStrFree(pszUID); 700 } 701 else 702 LogRel(("CoreAudio: [Output] Unable to get output device name (%RI32)\n", err)); 703 704 /* Get the default frames buffer size, so that we can setup our internal 705 * buffers. */ 706 uSize = sizeof(cFrames); 707 err = AudioDeviceGetProperty(caVoice->audioDeviceId, 708 0, 709 false, 710 kAudioDevicePropertyBufferFrameSize, 711 &uSize, 712 &cFrames); 713 if (RT_UNLIKELY(err != noErr)) 714 { 715 LogRel(("CoreAudio: [Output] Failed to get frame buffer size of the audio device (%RI32)\n", err)); 716 return -1; 717 } 718 /* Set the frame buffer size and honor any minimum/maximum restrictions on 719 the device. */ 720 err = caSetFrameBufferSize(caVoice->audioDeviceId, 721 false, 722 cFrames, 723 &cFrames); 724 if (RT_UNLIKELY(err != noErr)) 725 { 726 LogRel(("CoreAudio: [Output] Failed to set frame buffer size on the audio device (%RI32)\n", err)); 727 return -1; 728 } 729 730 cd.componentType = kAudioUnitType_Output; 731 cd.componentSubType = kAudioUnitSubType_HALOutput; 732 cd.componentManufacturer = kAudioUnitManufacturer_Apple; 733 cd.componentFlags = 0; 734 cd.componentFlagsMask = 0; 735 736 /* Try to find the default HAL output component. */ 737 cp = FindNextComponent(NULL, &cd); 738 if (RT_UNLIKELY(cp == 0)) 739 { 740 LogRel(("CoreAudio: [Output] Failed to find HAL output component\n")); 741 return -1; 742 } 743 744 /* Open the default HAL output component. */ 745 err = OpenAComponent(cp, &caVoice->audioUnit); 746 if (RT_UNLIKELY(err != noErr)) 747 { 748 LogRel(("CoreAudio: [Output] Failed to open output component (%RI32)\n", err)); 749 return -1; 750 } 751 752 /* Switch the I/O mode for output to on. */ 753 uFlag = 1; 754 err = AudioUnitSetProperty(caVoice->audioUnit, 755 kAudioOutputUnitProperty_EnableIO, 756 kAudioUnitScope_Output, 757 0, 758 &uFlag, 759 sizeof(uFlag)); 760 if (RT_UNLIKELY(err != noErr)) 761 { 762 LogRel(("CoreAudio: [Output] Failed to set output I/O mode enabled (%RI32)\n", err)); 763 return -1; 764 } 765 766 /* Set the default audio output device as the device for the new AudioUnit. */ 767 err = AudioUnitSetProperty(caVoice->audioUnit, 768 kAudioOutputUnitProperty_CurrentDevice, 769 kAudioUnitScope_Output, 770 0, 771 &caVoice->audioDeviceId, 772 sizeof(caVoice->audioDeviceId)); 773 if (RT_UNLIKELY(err != noErr)) 774 { 775 LogRel(("CoreAudio: [Output] Failed to set current device (%RI32)\n", err)); 776 return -1; 777 } 778 779 /* CoreAudio will inform us on a second thread when it needs more data for 780 * output. Therefor register an callback function which will provide the new 781 * data. */ 782 cb.inputProc = caPlaybackCallback; 783 cb.inputProcRefCon = caVoice; 784 785 err = AudioUnitSetProperty(caVoice->audioUnit, 786 kAudioUnitProperty_SetRenderCallback, 787 kAudioUnitScope_Input, 788 0, 789 &cb, 790 sizeof(cb)); 791 if (RT_UNLIKELY(err != noErr)) 792 { 793 LogRel(("CoreAudio: [Output] Failed to set callback (%RI32)\n", err)); 794 return -1; 795 } 796 797 /* Set the quality of the output render to the maximum. */ 798 /* uFlag = kRenderQuality_High;*/ 799 /* err = AudioUnitSetProperty(caVoice->audioUnit,*/ 800 /* kAudioUnitProperty_RenderQuality,*/ 801 /* kAudioUnitScope_Global,*/ 802 /* 0,*/ 803 /* &uFlag,*/ 804 /* sizeof(uFlag));*/ 805 /* Not fatal */ 806 /* if (RT_UNLIKELY(err != noErr))*/ 807 /* LogRel(("CoreAudio: [Output] Failed to set the render quality to the maximum (%RI32)\n", err));*/ 808 809 /* Fetch the current stream format of the device. */ 810 uSize = sizeof(caVoice->deviceFormat); 811 err = AudioUnitGetProperty(caVoice->audioUnit, 812 kAudioUnitProperty_StreamFormat, 813 kAudioUnitScope_Input, 814 0, 815 &caVoice->deviceFormat, 816 &uSize); 817 if (RT_UNLIKELY(err != noErr)) 818 { 819 LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err)); 820 return -1; 821 } 822 823 /* Create an AudioStreamBasicDescription based on the audio settings of 824 * VirtualBox. */ 825 caPCMInfoToAudioStreamBasicDescription(&caVoice->hw.info, &caVoice->streamFormat); 826 827 #if DEBUG 828 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] device", &caVoice->deviceFormat); 829 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] output", &caVoice->streamFormat); 830 #endif /* DEBUG */ 831 832 /* Set the device format description for the stream. */ 833 err = AudioUnitSetProperty(caVoice->audioUnit, 834 kAudioUnitProperty_StreamFormat, 835 kAudioUnitScope_Input, 836 0, 837 &caVoice->streamFormat, 838 sizeof(caVoice->streamFormat)); 839 if (RT_UNLIKELY(err != noErr)) 840 { 841 LogRel(("CoreAudio: [Output] Failed to set stream format (%RI32)\n", err)); 842 return -1; 843 } 844 845 uSize = sizeof(caVoice->deviceFormat); 846 err = AudioUnitGetProperty(caVoice->audioUnit, 847 kAudioUnitProperty_StreamFormat, 848 kAudioUnitScope_Input, 849 0, 850 &caVoice->deviceFormat, 851 &uSize); 852 if (RT_UNLIKELY(err != noErr)) 853 { 854 LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err)); 855 return -1; 856 } 857 858 /* Also set the frame buffer size off the device on our AudioUnit. This 859 should make sure that the frames count which we receive in the render 860 thread is as we like. */ 861 err = AudioUnitSetProperty(caVoice->audioUnit, 862 kAudioUnitProperty_MaximumFramesPerSlice, 863 kAudioUnitScope_Global, 864 0, 865 &cFrames, 866 sizeof(cFrames)); 867 if (RT_UNLIKELY(err != noErr)) 868 { 869 LogRel(("CoreAudio: [Output] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err)); 870 return -1; 871 } 872 873 /* Finally initialize the new AudioUnit. */ 874 err = AudioUnitInitialize(caVoice->audioUnit); 875 if (RT_UNLIKELY(err != noErr)) 876 { 877 LogRel(("CoreAudio: [Output] Failed to initialize the AudioUnit (%RI32)\n", err)); 878 return -1; 879 } 880 881 /* There are buggy devices (e.g. my bluetooth headset) which doesn't honor 882 * the frame buffer size set in the previous calls. So finally get the 883 * frame buffer size after the AudioUnit was initialized. */ 884 uSize = sizeof(cFrames); 885 err = AudioUnitGetProperty(caVoice->audioUnit, 886 kAudioUnitProperty_MaximumFramesPerSlice, 887 kAudioUnitScope_Global, 888 0, 889 &cFrames, 890 &uSize); 891 if (RT_UNLIKELY(err != noErr)) 892 { 893 LogRel(("CoreAudio: [Output] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err)); 894 return -1; 895 } 896 897 /* Create the internal ring buffer. */ 898 cSamples = cFrames * caVoice->streamFormat.mChannelsPerFrame; 899 IORingBufferCreate(&caVoice->pBuf, cSamples << hw->info.shift); 900 if (!RT_VALID_PTR(caVoice->pBuf)) 901 { 902 LogRel(("CoreAudio: [Output] Failed to create internal ring buffer\n")); 903 AudioUnitUninitialize(caVoice->audioUnit); 904 return -1; 905 } 906 907 if ( hw->samples != 0 908 && hw->samples != (int32_t)cSamples) 909 LogRel(("CoreAudio: [Output] Warning! After recreation, the CoreAudio ring buffer doesn't has the same size as the device buffer (%RU32 vs. %RU32).\n", cSamples, (uint32_t)hw->samples)); 910 911 #ifdef DEBUG 912 err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId, 913 0, 914 false, 915 kAudioDeviceProcessorOverload, 916 caPlaybackAudioDevicePropertyChanged, 917 caVoice); 918 /* Not Fatal */ 919 if (RT_UNLIKELY(err != noErr)) 920 LogRel(("CoreAudio: [Output] Failed to add the processor overload listener (%RI32)\n", err)); 921 #endif /* DEBUG */ 922 923 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_INIT); 924 925 Log(("CoreAudio: [Output] Frame count: %RU32\n", cFrames)); 926 927 return 0; 928 } 929 930 static void caReinitOutput(HWVoiceOut *hw) 931 { 932 caVoiceOut *caVoice = (caVoiceOut *) hw; 933 934 coreaudio_fini_out(&caVoice->hw); 935 caInitOutput(&caVoice->hw); 936 937 coreaudio_ctl_out(&caVoice->hw, VOICE_ENABLE); 527 938 } 528 939 … … 538 949 caVoiceOut *caVoice = (caVoiceOut *) hw; 539 950 951 /* Check if the audio device should be reinitialized. If so do it. */ 952 if (ASMAtomicReadU32(&caVoice->status) == CA_STATUS_REINIT) 953 caReinitOutput(&caVoice->hw); 954 955 /* We return the live count in the case we are not initialized. This should 956 * prevent any under runs. */ 957 if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT) 958 return audio_pcm_hw_get_live_out(hw); 959 960 /* Make sure the device is running */ 961 coreaudio_ctl_out(&caVoice->hw, VOICE_ENABLE); 962 540 963 /* How much space is available in the ring buffer */ 541 964 csAvail = IORingBufferFree(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */ 965 542 966 /* How much data is availabe. Use the smaller size of the too. */ 543 967 csAvail = RT_MIN(csAvail, (uint32_t)audio_pcm_hw_get_live_out(hw)); 544 968 545 Log2(("CoreAudio: [Output] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << hw->info.shift));969 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << hw->info.shift)); 546 970 547 971 /* Iterate as long as data is available */ … … 551 975 csToWrite = RT_MIN(csAvail - csWritten, (uint32_t)(hw->samples - hw->rpos)); 552 976 cbToWrite = csToWrite << hw->info.shift; /* samples -> bytes */ 553 Log2(("CoreAudio: [Output] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));977 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite)); 554 978 /* Try to aquire the necessary space from the ring buffer. */ 555 979 IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite); 556 980 /* How much to we get? */ 557 981 csToWrite = cbToWrite >> hw->info.shift; 558 Log2(("CoreAudio: [Output] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));982 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite)); 559 983 /* Break if nothing is free anymore. */ 560 984 if (RT_UNLIKELY(cbToWrite == 0)) … … 570 994 } 571 995 572 Log2(("CoreAudio: [Output] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << hw->info.shift));996 CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << hw->info.shift)); 573 997 574 998 /* Return the count of samples we have processed. */ … … 584 1008 { 585 1009 OSStatus err = noErr; 1010 uint32_t status; 586 1011 caVoiceOut *caVoice = (caVoiceOut *) hw; 1012 1013 status = ASMAtomicReadU32(&caVoice->status); 1014 if (!( status == CA_STATUS_INIT 1015 || status == CA_STATUS_REINIT)) 1016 return 0; 587 1017 588 1018 switch (cmd) … … 593 1023 if (!caIsRunning(caVoice->audioDeviceId)) 594 1024 { 1025 err = AudioUnitReset(caVoice->audioUnit, 1026 kAudioUnitScope_Input, 1027 0); 595 1028 IORingBufferReset(caVoice->pBuf); 596 1029 err = AudioOutputUnitStart(caVoice->audioUnit); … … 629 1062 } 630 1063 631 static int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as)632 {633 OSStatus err = noErr;634 UInt32 uSize = 0; /* temporary size of properties */635 UInt32 uFlag = 0; /* for setting flags */636 CFStringRef name; /* for the temporary device name fetching */637 const char *pszName;638 ComponentDescription cd; /* description for an audio component */639 Component cp; /* an audio component */640 AURenderCallbackStruct cb; /* holds the callback structure */641 UInt32 cFrames; /* default frame count */642 643 caVoiceOut *caVoice = (caVoiceOut *) hw;644 645 caVoice->audioUnit = NULL;646 caVoice->audioDeviceId = kAudioDeviceUnknown;647 648 /* Initialize the hardware info section with the audio settings */649 audio_pcm_init_info(&hw->info, as);650 651 /* Fetch the default audio output device currently in use */652 uSize = sizeof(caVoice->audioDeviceId);653 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,654 &uSize,655 &caVoice->audioDeviceId);656 if (RT_UNLIKELY(err != noErr))657 {658 LogRel(("CoreAudio: [Output] Unable to find default output device (%RI32)\n", err));659 return -1;660 }661 662 /* Try to get the name of the default output device and log it. It's not663 * fatal if it fails. */664 uSize = sizeof(CFStringRef);665 err = AudioDeviceGetProperty(caVoice->audioDeviceId,666 0,667 0,668 kAudioObjectPropertyName,669 &uSize,670 &name);671 if (RT_LIKELY(err == noErr))672 {673 pszName = CFStringGetCStringPtr(name, kCFStringEncodingMacRoman);674 if (pszName)675 LogRel(("CoreAudio: Using default output device: %s\n", pszName));676 CFRelease(name);677 }678 else679 LogRel(("CoreAudio: [Output] Unable to get output device name (%RI32)\n", err));680 681 /* Get the default frames buffer size, so that we can setup our internal682 * buffers. */683 uSize = sizeof(cFrames);684 err = AudioDeviceGetProperty(caVoice->audioDeviceId,685 0,686 false,687 kAudioDevicePropertyBufferFrameSize,688 &uSize,689 &cFrames);690 if (RT_UNLIKELY(err != noErr))691 {692 LogRel(("CoreAudio: [Output] Failed to get frame buffer size of the audio device (%RI32)\n", err));693 return -1;694 }695 /* Set the frame buffer size and honor any minimum/maximum restrictions on696 the device. */697 err = caSetFrameBufferSize(caVoice->audioDeviceId,698 false,699 cFrames,700 &cFrames);701 if (RT_UNLIKELY(err != noErr))702 {703 LogRel(("CoreAudio: [Output] Failed to set frame buffer size on the audio device (%RI32)\n", err));704 return -1;705 }706 707 cd.componentType = kAudioUnitType_Output;708 cd.componentSubType = kAudioUnitSubType_HALOutput;709 cd.componentManufacturer = kAudioUnitManufacturer_Apple;710 cd.componentFlags = 0;711 cd.componentFlagsMask = 0;712 713 /* Try to find the default HAL output component. */714 cp = FindNextComponent(NULL, &cd);715 if (RT_UNLIKELY(cp == 0))716 {717 LogRel(("CoreAudio: [Output] Failed to find HAL output component\n"));718 return -1;719 }720 721 /* Open the default HAL output component. */722 err = OpenAComponent(cp, &caVoice->audioUnit);723 if (RT_UNLIKELY(err != noErr))724 {725 LogRel(("CoreAudio: [Output] Failed to open output component (%RI32)\n", err));726 return -1;727 }728 729 /* Switch the I/O mode for output to on. */730 uFlag = 1;731 err = AudioUnitSetProperty(caVoice->audioUnit,732 kAudioOutputUnitProperty_EnableIO,733 kAudioUnitScope_Output,734 0,735 &uFlag,736 sizeof(uFlag));737 if (RT_UNLIKELY(err != noErr))738 {739 LogRel(("CoreAudio: [Output] Failed to set output I/O mode enabled (%RI32)\n", err));740 return -1;741 }742 743 /* Set the default audio output device as the device for the new AudioUnit. */744 err = AudioUnitSetProperty(caVoice->audioUnit,745 kAudioOutputUnitProperty_CurrentDevice,746 kAudioUnitScope_Output,747 0,748 &caVoice->audioDeviceId,749 sizeof(caVoice->audioDeviceId));750 if (RT_UNLIKELY(err != noErr))751 {752 LogRel(("CoreAudio: [Output] Failed to set current device (%RI32)\n", err));753 return -1;754 }755 756 /* CoreAudio will inform us on a second thread when it needs more data for757 * output. Therefor register an callback function which will provide the new758 * data. */759 cb.inputProc = caPlaybackCallback;760 cb.inputProcRefCon = caVoice;761 762 err = AudioUnitSetProperty(caVoice->audioUnit,763 kAudioUnitProperty_SetRenderCallback,764 kAudioUnitScope_Input,765 0,766 &cb,767 sizeof(cb));768 if (RT_UNLIKELY(err != noErr))769 {770 LogRel(("CoreAudio: [Output] Failed to set callback (%RI32)\n", err));771 return -1;772 }773 774 /* Set the quality of the output render to the maximum. */775 /* uFlag = kRenderQuality_High;*/776 /* err = AudioUnitSetProperty(caVoice->audioUnit,*/777 /* kAudioUnitProperty_RenderQuality,*/778 /* kAudioUnitScope_Global,*/779 /* 0,*/780 /* &uFlag,*/781 /* sizeof(uFlag));*/782 /* Not fatal */783 /* if (RT_UNLIKELY(err != noErr))*/784 /* LogRel(("CoreAudio: [Output] Failed to set the render quality to the maximum (%RI32)\n", err));*/785 786 /* Fetch the current stream format of the device. */787 uSize = sizeof(caVoice->deviceFormat);788 err = AudioUnitGetProperty(caVoice->audioUnit,789 kAudioUnitProperty_StreamFormat,790 kAudioUnitScope_Input,791 0,792 &caVoice->deviceFormat,793 &uSize);794 if (RT_UNLIKELY(err != noErr))795 {796 LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err));797 return -1;798 }799 800 /* Create an AudioStreamBasicDescription based on the audio settings of801 * VirtualBox. */802 caAudioSettingsToAudioStreamBasicDescription(as, &caVoice->streamFormat);803 804 #if DEBUG805 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] device", &caVoice->deviceFormat);806 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] output", &caVoice->streamFormat);807 #endif /* DEBUG */808 809 /* Set the device format description for the stream. */810 err = AudioUnitSetProperty(caVoice->audioUnit,811 kAudioUnitProperty_StreamFormat,812 kAudioUnitScope_Input,813 0,814 &caVoice->streamFormat,815 sizeof(caVoice->streamFormat));816 if (RT_UNLIKELY(err != noErr))817 {818 LogRel(("CoreAudio: [Output] Failed to set stream format (%RI32)\n", err));819 return -1;820 }821 822 uSize = sizeof(caVoice->deviceFormat);823 err = AudioUnitGetProperty(caVoice->audioUnit,824 kAudioUnitProperty_StreamFormat,825 kAudioUnitScope_Input,826 0,827 &caVoice->deviceFormat,828 &uSize);829 if (RT_UNLIKELY(err != noErr))830 {831 LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err));832 return -1;833 }834 835 /* Also set the frame buffer size off the device on our AudioUnit. This836 should make sure that the frames count which we receive in the render837 thread is as we like. */838 err = AudioUnitSetProperty(caVoice->audioUnit,839 kAudioUnitProperty_MaximumFramesPerSlice,840 kAudioUnitScope_Global,841 0,842 &cFrames,843 sizeof(cFrames));844 if (RT_UNLIKELY(err != noErr))845 {846 LogRel(("CoreAudio: [Output] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err));847 return -1;848 }849 850 /* Finally initialize the new AudioUnit. */851 err = AudioUnitInitialize(caVoice->audioUnit);852 if (RT_UNLIKELY(err != noErr))853 {854 LogRel(("CoreAudio: [Output] Failed to initialize the AudioUnit (%RI32)\n", err));855 return -1;856 }857 858 /* There are buggy devices (e.g. my bluetooth headset) which doesn't honor859 * the frame buffer size set in the previous calls. So finally get the860 * frame buffer size after the AudioUnit was initialized. */861 uSize = sizeof(cFrames);862 err = AudioUnitGetProperty(caVoice->audioUnit,863 kAudioUnitProperty_MaximumFramesPerSlice,864 kAudioUnitScope_Global,865 0,866 &cFrames,867 &uSize);868 if (RT_UNLIKELY(err != noErr))869 {870 LogRel(("CoreAudio: [Output] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err));871 return -1;872 }873 874 /* Create the internal ring buffer. */875 hw->samples = cFrames * caVoice->streamFormat.mChannelsPerFrame;876 IORingBufferCreate(&caVoice->pBuf, hw->samples << hw->info.shift);877 if (!VALID_PTR(caVoice->pBuf))878 {879 LogRel(("CoreAudio: [Output] Failed to create internal ring buffer\n"));880 AudioUnitUninitialize(caVoice->audioUnit);881 return -1;882 }883 884 Log(("CoreAudio: [Output] HW samples: %d; Frame count: %RU32\n", hw->samples, cFrames));885 886 return 0;887 }888 889 1064 static void coreaudio_fini_out(HWVoiceOut *hw) 890 1065 { 891 1066 int rc = 0; 1067 uint32_t status; 892 1068 OSStatus err = noErr; 893 1069 caVoiceOut *caVoice = (caVoiceOut *) hw; 894 1070 1071 status = ASMAtomicReadU32(&caVoice->status); 1072 if (!( status == CA_STATUS_INIT 1073 || status == CA_STATUS_REINIT)) 1074 return; 1075 895 1076 rc = coreaudio_ctl_out(hw, VOICE_DISABLE); 896 1077 if (RT_LIKELY(rc == 0)) 897 1078 { 1079 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_UNINIT); 1080 #ifdef DEBUG 1081 err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId, 1082 0, 1083 false, 1084 kAudioDeviceProcessorOverload, 1085 caPlaybackAudioDevicePropertyChanged); 1086 /* Not Fatal */ 1087 if (RT_UNLIKELY(err != noErr)) 1088 LogRel(("CoreAudio: [Output] Failed to remove the processor overload listener (%RI32)\n", err)); 1089 #endif /* DEBUG */ 898 1090 err = AudioUnitUninitialize(caVoice->audioUnit); 899 1091 if (RT_LIKELY(err == noErr)) … … 902 1094 if (RT_LIKELY(err == noErr)) 903 1095 { 1096 IORingBufferDestroy(caVoice->pBuf); 904 1097 caVoice->audioUnit = NULL; 905 1098 caVoice->audioDeviceId = kAudioDeviceUnknown; 906 IORingBufferDestroy(caVoice->pBuf); 1099 caVoice->pBuf = NULL; 1100 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT); 907 1101 } 908 1102 else … … 916 1110 } 917 1111 1112 static int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as) 1113 { 1114 OSStatus err = noErr; 1115 int rc = 0; 1116 bool fDeviceByUser = false; /* use we a device which was set by the user? */ 1117 1118 caVoiceOut *caVoice = (caVoiceOut *) hw; 1119 1120 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT); 1121 caVoice->audioUnit = NULL; 1122 caVoice->audioDeviceId = kAudioDeviceUnknown; 1123 hw->samples = 0; 1124 1125 /* Initialize the hardware info section with the audio settings */ 1126 audio_pcm_init_info(&hw->info, as); 1127 1128 /* Try to find the audio device set by the user. Use 1129 * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0 1130 * to set it. */ 1131 if (conf.pszOutputDeviceUID) 1132 { 1133 caVoice->audioDeviceId = caDeviceUIDtoID(conf.pszOutputDeviceUID); 1134 /* Not fatal */ 1135 if (caVoice->audioDeviceId == kAudioDeviceUnknown) 1136 LogRel(("CoreAudio: [Output] Unable to find output device %s. Falling back to the default audio device. \n", conf.pszOutputDeviceUID)); 1137 else 1138 fDeviceByUser = true; 1139 } 1140 1141 rc = caInitOutput(hw); 1142 if (RT_UNLIKELY(rc != 0)) 1143 return rc; 1144 1145 /* The samples have to correspond to the internal ring buffer size. */ 1146 hw->samples = (IORingBufferSize(caVoice->pBuf) >> hw->info.shift) / caVoice->streamFormat.mChannelsPerFrame; 1147 1148 /* When the devices isn't forced by the user, we want default device change 1149 * notifications. */ 1150 if (!fDeviceByUser) 1151 { 1152 err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice, 1153 caPlaybackDefaultDeviceChanged, 1154 caVoice); 1155 /* Not Fatal */ 1156 if (RT_UNLIKELY(err != noErr)) 1157 LogRel(("CoreAudio: [Output] Failed to add the default device changed listener (%RI32)\n", err)); 1158 } 1159 1160 Log(("CoreAudio: [Output] HW samples: %d\n", hw->samples)); 1161 1162 return 0; 1163 } 1164 918 1165 /******************************************************************************* 919 1166 * … … 922 1169 ******************************************************************************/ 923 1170 924 /* callback to convert audio input data from one format to another */ 1171 /* We need some forward declarations */ 1172 static int coreaudio_run_in(HWVoiceIn *hw); 1173 static int coreaudio_read(SWVoiceIn *sw, void *buf, int size); 1174 static int coreaudio_ctl_in(HWVoiceIn *hw, int cmd, ...); 1175 static void coreaudio_fini_in(HWVoiceIn *hw); 1176 static int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as); 1177 static int caInitInput(HWVoiceIn *hw); 1178 static void caReinitInput(HWVoiceIn *hw); 1179 1180 /* Callback for getting notified when the default input device was changed */ 1181 static OSStatus caRecordingDefaultDeviceChanged(AudioHardwarePropertyID inPropertyID, 1182 void *inClientData) 1183 { 1184 OSStatus err = noErr; 1185 UInt32 uSize = 0; 1186 UInt32 ad = 0; 1187 bool fRun = false; 1188 1189 caVoiceIn *caVoice = (caVoiceIn *) inClientData; 1190 1191 switch (inPropertyID) 1192 { 1193 case kAudioHardwarePropertyDefaultInputDevice: 1194 { 1195 /* This listener is called on every change of the hardware 1196 * device. So check if the default device has really changed. */ 1197 uSize = sizeof(caVoice->audioDeviceId); 1198 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, 1199 &uSize, 1200 &ad); 1201 if (caVoice->audioDeviceId != ad) 1202 { 1203 Log2(("CoreAudio: [Input] Default input device changed!\n")); 1204 /* We move the reinitialization to the next input event. 1205 * This make sure this thread isn't blocked and the 1206 * reinitialization is done when necessary only. */ 1207 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_REINIT); 1208 } 1209 break; 1210 } 1211 } 1212 1213 return noErr; 1214 } 1215 1216 /* Callback for getting notified when some of the properties of an audio device has changed */ 1217 static OSStatus caRecordingAudioDevicePropertyChanged(AudioDeviceID inDevice, 1218 UInt32 inChannel, 1219 Boolean isInput, 1220 AudioDevicePropertyID inPropertyID, 1221 void *inClientData) 1222 { 1223 caVoiceIn *caVoice = (caVoiceIn *) inClientData; 1224 1225 switch (inPropertyID) 1226 { 1227 #ifdef DEBUG 1228 case kAudioDeviceProcessorOverload: 1229 { 1230 Log2(("CoreAudio: [Input] Processor overload detected!\n")); 1231 break; 1232 } 1233 #endif /* DEBUG */ 1234 case kAudioDevicePropertyNominalSampleRate: 1235 { 1236 Log2(("CoreAudio: [Input] Sample rate changed!\n")); 1237 /* We move the reinitialization to the next input event. 1238 * This make sure this thread isn't blocked and the 1239 * reinitialization is done when necessary only. */ 1240 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_REINIT); 1241 break; 1242 } 1243 default: break; 1244 } 1245 1246 return noErr; 1247 } 1248 1249 /* Callback to convert audio input data from one format to another */ 925 1250 static OSStatus caConverterCallback(AudioConverterRef inAudioConverter, 926 1251 UInt32 *ioNumberDataPackets, … … 936 1261 937 1262 const AudioBufferList *pBufferList = &caVoice->bufferList; 1263 1264 if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT) 1265 return noErr; 1266 938 1267 /* Log2(("converting .... ################ %RU32 %RU32 %RU32 %RU32 %RU32\n", *ioNumberDataPackets, bufferList->mBuffers[i].mNumberChannels, bufferList->mNumberBuffers, bufferList->mBuffers[i].mDataByteSize, ioData->mNumberBuffers));*/ 939 1268 … … 965 1294 } 966 1295 967 /* callback to feed audio input buffer */1296 /* Callback to feed audio input buffer */ 968 1297 static OSStatus caRecordingCallback(void* inRefCon, 969 1298 AudioUnitRenderActionFlags* ioActionFlags, … … 984 1313 caVoiceIn *caVoice = (caVoiceIn *) inRefCon; 985 1314 1315 if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT) 1316 return noErr; 1317 986 1318 /* If nothing is pending return immediately. */ 987 1319 if (inNumberFrames == 0) … … 989 1321 990 1322 /* Are we using an converter? */ 991 if ( VALID_PTR(caVoice->converter))1323 if (RT_VALID_PTR(caVoice->converter)) 992 1324 { 993 1325 /* Firstly render the data as usual */ … … 1015 1347 csAvail = RT_MIN(csAvail, (uint32_t)((caVoice->bufferList.mBuffers[0].mDataByteSize / caVoice->deviceFormat.mBytesPerFrame) * caVoice->sampleRatio)); 1016 1348 1017 Log2(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));1349 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift)); 1018 1350 /* Initialize the temporary output buffer */ 1019 1351 tmpList.mNumberBuffers = 1; … … 1027 1359 csToWrite = csAvail - csWritten; 1028 1360 cbToWrite = csToWrite << caVoice->hw.info.shift; 1029 Log2(("CoreAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));1361 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite)); 1030 1362 /* Try to acquire the necessary space from the ring buffer. */ 1031 1363 IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite); 1032 1364 /* How much to we get? */ 1033 1365 csToWrite = cbToWrite >> caVoice->hw.info.shift; 1034 Log2(("CoreAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));1366 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite)); 1035 1367 /* Break if nothing is free anymore. */ 1036 1368 if (RT_UNLIKELY(cbToWrite == 0)) … … 1069 1401 /* Cleanup */ 1070 1402 RTMemFree(caVoice->bufferList.mBuffers[0].mData); 1071 Log2(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift));1403 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift)); 1072 1404 } 1073 1405 else … … 1096 1428 csAvail = RT_MIN(csAvail, caVoice->bufferList.mBuffers[0].mDataByteSize >> caVoice->hw.info.shift); 1097 1429 1098 Log2(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));1430 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift)); 1099 1431 1100 1432 /* Iterate as long as data is available */ … … 1104 1436 csToWrite = csAvail - csWritten; 1105 1437 cbToWrite = csToWrite << caVoice->hw.info.shift; 1106 Log2(("CoreAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));1438 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite)); 1107 1439 /* Try to aquire the necessary space from the ring buffer. */ 1108 1440 IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite); 1109 1441 /* How much to we get? */ 1110 1442 csToWrite = cbToWrite >> caVoice->hw.info.shift; 1111 Log2(("CoreAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));1443 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite)); 1112 1444 /* Break if nothing is free anymore. */ 1113 1445 if (RT_UNLIKELY(cbToWrite == 0)) … … 1122 1454 RTMemFree(caVoice->bufferList.mBuffers[0].mData); 1123 1455 1124 Log2(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift));1456 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift)); 1125 1457 } 1126 1458 1127 1459 return err; 1460 } 1461 1462 static int caInitInput(HWVoiceIn *hw) 1463 { 1464 OSStatus err = noErr; 1465 int rc = -1; 1466 UInt32 uSize = 0; /* temporary size of properties */ 1467 UInt32 uFlag = 0; /* for setting flags */ 1468 CFStringRef name; /* for the temporary device name fetching */ 1469 char *pszName; 1470 char *pszUID; 1471 ComponentDescription cd; /* description for an audio component */ 1472 Component cp; /* an audio component */ 1473 AURenderCallbackStruct cb; /* holds the callback structure */ 1474 UInt32 cFrames; /* default frame count */ 1475 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo */ 1476 UInt32 cSamples; /* samples count */ 1477 1478 caVoiceIn *caVoice = (caVoiceIn *) hw; 1479 1480 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_INIT); 1481 1482 if (caVoice->audioDeviceId == kAudioDeviceUnknown) 1483 { 1484 /* Fetch the default audio output device currently in use */ 1485 uSize = sizeof(caVoice->audioDeviceId); 1486 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, 1487 &uSize, 1488 &caVoice->audioDeviceId); 1489 if (RT_UNLIKELY(err != noErr)) 1490 { 1491 LogRel(("CoreAudio: [Input] Unable to find default input device (%RI32)\n", err)); 1492 return -1; 1493 } 1494 } 1495 1496 /* Try to get the name of the input device and log it. It's not fatal if 1497 * it fails. */ 1498 uSize = sizeof(CFStringRef); 1499 err = AudioDeviceGetProperty(caVoice->audioDeviceId, 1500 0, 1501 0, 1502 kAudioObjectPropertyName, 1503 &uSize, 1504 &name); 1505 if (RT_LIKELY(err == noErr)) 1506 { 1507 pszName = caCFStringToCString(name); 1508 CFRelease(name); 1509 err = AudioDeviceGetProperty(caVoice->audioDeviceId, 1510 0, 1511 0, 1512 kAudioDevicePropertyDeviceUID, 1513 &uSize, 1514 &name); 1515 if (RT_LIKELY(err == noErr)) 1516 { 1517 pszUID = caCFStringToCString(name); 1518 CFRelease(name); 1519 if (pszName && pszUID) 1520 LogRel(("CoreAudio: Using input device: %s (UID: %s)\n", pszName, pszUID)); 1521 RTStrFree(pszName); 1522 } 1523 RTStrFree(pszUID); 1524 } 1525 else 1526 LogRel(("CoreAudio: [Input] Unable to get input device name (%RI32)\n", err)); 1527 1528 /* Get the default frames buffer size, so that we can setup our internal 1529 * buffers. */ 1530 uSize = sizeof(cFrames); 1531 err = AudioDeviceGetProperty(caVoice->audioDeviceId, 1532 0, 1533 true, 1534 kAudioDevicePropertyBufferFrameSize, 1535 &uSize, 1536 &cFrames); 1537 if (RT_UNLIKELY(err != noErr)) 1538 { 1539 LogRel(("CoreAudio: [Input] Failed to get frame buffer size of the audio device (%RI32)\n", err)); 1540 return -1; 1541 } 1542 /* Set the frame buffer size and honor any minimum/maximum restrictions on 1543 the device. */ 1544 err = caSetFrameBufferSize(caVoice->audioDeviceId, 1545 true, 1546 cFrames, 1547 &cFrames); 1548 if (RT_UNLIKELY(err != noErr)) 1549 { 1550 LogRel(("CoreAudio: [Input] Failed to set frame buffer size on the audio device (%RI32)\n", err)); 1551 return -1; 1552 } 1553 1554 cd.componentType = kAudioUnitType_Output; 1555 cd.componentSubType = kAudioUnitSubType_HALOutput; 1556 cd.componentManufacturer = kAudioUnitManufacturer_Apple; 1557 cd.componentFlags = 0; 1558 cd.componentFlagsMask = 0; 1559 1560 /* Try to find the default HAL output component. */ 1561 cp = FindNextComponent(NULL, &cd); 1562 if (RT_UNLIKELY(cp == 0)) 1563 { 1564 LogRel(("CoreAudio: [Input] Failed to find HAL output component\n")); 1565 return -1; 1566 } 1567 1568 /* Open the default HAL output component. */ 1569 err = OpenAComponent(cp, &caVoice->audioUnit); 1570 if (RT_UNLIKELY(err != noErr)) 1571 { 1572 LogRel(("CoreAudio: [Input] Failed to open output component (%RI32)\n", err)); 1573 return -1; 1574 } 1575 1576 /* Switch the I/O mode for input to on. */ 1577 uFlag = 1; 1578 err = AudioUnitSetProperty(caVoice->audioUnit, 1579 kAudioOutputUnitProperty_EnableIO, 1580 kAudioUnitScope_Input, 1581 1, 1582 &uFlag, 1583 sizeof(uFlag)); 1584 if (RT_UNLIKELY(err != noErr)) 1585 { 1586 LogRel(("CoreAudio: [Input] Failed to set input I/O mode enabled (%RI32)\n", err)); 1587 return -1; 1588 } 1589 1590 /* Switch the I/O mode for output to off. This is important, as this is a 1591 * pure input stream. */ 1592 uFlag = 0; 1593 err = AudioUnitSetProperty(caVoice->audioUnit, 1594 kAudioOutputUnitProperty_EnableIO, 1595 kAudioUnitScope_Output, 1596 0, 1597 &uFlag, 1598 sizeof(uFlag)); 1599 if (RT_UNLIKELY(err != noErr)) 1600 { 1601 LogRel(("CoreAudio: [Input] Failed to set output I/O mode disabled (%RI32)\n", err)); 1602 return -1; 1603 } 1604 1605 /* Set the default audio input device as the device for the new AudioUnit. */ 1606 err = AudioUnitSetProperty(caVoice->audioUnit, 1607 kAudioOutputUnitProperty_CurrentDevice, 1608 kAudioUnitScope_Global, 1609 0, 1610 &caVoice->audioDeviceId, 1611 sizeof(caVoice->audioDeviceId)); 1612 if (RT_UNLIKELY(err != noErr)) 1613 { 1614 LogRel(("CoreAudio: [Input] Failed to set current device (%RI32)\n", err)); 1615 return -1; 1616 } 1617 1618 /* CoreAudio will inform us on a second thread for new incoming audio data. 1619 * Therefor register an callback function, which will process the new data. 1620 * */ 1621 cb.inputProc = caRecordingCallback; 1622 cb.inputProcRefCon = caVoice; 1623 1624 err = AudioUnitSetProperty(caVoice->audioUnit, 1625 kAudioOutputUnitProperty_SetInputCallback, 1626 kAudioUnitScope_Global, 1627 0, 1628 &cb, 1629 sizeof(cb)); 1630 if (RT_UNLIKELY(err != noErr)) 1631 { 1632 LogRel(("CoreAudio: [Input] Failed to set callback (%RI32)\n", err)); 1633 return -1; 1634 } 1635 1636 /* Fetch the current stream format of the device. */ 1637 uSize = sizeof(caVoice->deviceFormat); 1638 err = AudioUnitGetProperty(caVoice->audioUnit, 1639 kAudioUnitProperty_StreamFormat, 1640 kAudioUnitScope_Input, 1641 1, 1642 &caVoice->deviceFormat, 1643 &uSize); 1644 if (RT_UNLIKELY(err != noErr)) 1645 { 1646 LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err)); 1647 return -1; 1648 } 1649 1650 /* Create an AudioStreamBasicDescription based on the audio settings of 1651 * VirtualBox. */ 1652 caPCMInfoToAudioStreamBasicDescription(&caVoice->hw.info, &caVoice->streamFormat); 1653 1654 #if DEBUG 1655 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] device", &caVoice->deviceFormat); 1656 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] input", &caVoice->streamFormat); 1657 #endif /* DEBUG */ 1658 1659 /* If the frequency of the device is different from the requested one we 1660 * need a converter. The same count if the number of channels is different. */ 1661 if ( caVoice->deviceFormat.mSampleRate != caVoice->streamFormat.mSampleRate 1662 || caVoice->deviceFormat.mChannelsPerFrame != caVoice->streamFormat.mChannelsPerFrame) 1663 { 1664 err = AudioConverterNew(&caVoice->deviceFormat, 1665 &caVoice->streamFormat, 1666 &caVoice->converter); 1667 if (RT_UNLIKELY(err != noErr)) 1668 { 1669 LogRel(("CoreAudio: [Input] Failed to create the audio converter (%RI32)\n", err)); 1670 return -1; 1671 } 1672 1673 if (caVoice->deviceFormat.mChannelsPerFrame == 1 && 1674 caVoice->streamFormat.mChannelsPerFrame == 2) 1675 { 1676 /* If the channel count is different we have to tell this the converter 1677 and supply a channel mapping. For now we only support mapping 1678 from mono to stereo. For all other cases the core audio defaults 1679 are used, which means dropping additional channels in most 1680 cases. */ 1681 err = AudioConverterSetProperty(caVoice->converter, 1682 kAudioConverterChannelMap, 1683 sizeof(channelMap), 1684 channelMap); 1685 if (RT_UNLIKELY(err != noErr)) 1686 { 1687 LogRel(("CoreAudio: [Input] Failed to add a channel mapper to the audio converter (%RI32)\n", err)); 1688 return -1; 1689 } 1690 } 1691 /* Set sample rate converter quality to maximum */ 1692 /* uFlag = kAudioConverterQuality_Max;*/ 1693 /* err = AudioConverterSetProperty(caVoice->converter,*/ 1694 /* kAudioConverterSampleRateConverterQuality,*/ 1695 /* sizeof(uFlag),*/ 1696 /* &uFlag);*/ 1697 /* Not fatal */ 1698 /* if (RT_UNLIKELY(err != noErr))*/ 1699 /* LogRel(("CoreAudio: [Input] Failed to set the audio converter quality to the maximum (%RI32)\n", err));*/ 1700 1701 Log(("CoreAudio: [Input] Converter in use\n")); 1702 /* Set the new format description for the stream. */ 1703 err = AudioUnitSetProperty(caVoice->audioUnit, 1704 kAudioUnitProperty_StreamFormat, 1705 kAudioUnitScope_Output, 1706 1, 1707 &caVoice->deviceFormat, 1708 sizeof(caVoice->deviceFormat)); 1709 if (RT_UNLIKELY(err != noErr)) 1710 { 1711 LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err)); 1712 return -1; 1713 } 1714 err = AudioUnitSetProperty(caVoice->audioUnit, 1715 kAudioUnitProperty_StreamFormat, 1716 kAudioUnitScope_Input, 1717 1, 1718 &caVoice->deviceFormat, 1719 sizeof(caVoice->deviceFormat)); 1720 if (RT_UNLIKELY(err != noErr)) 1721 { 1722 LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err)); 1723 return -1; 1724 } 1725 } 1726 else 1727 { 1728 /* Set the new format description for the stream. */ 1729 err = AudioUnitSetProperty(caVoice->audioUnit, 1730 kAudioUnitProperty_StreamFormat, 1731 kAudioUnitScope_Output, 1732 1, 1733 &caVoice->streamFormat, 1734 sizeof(caVoice->streamFormat)); 1735 if (RT_UNLIKELY(err != noErr)) 1736 { 1737 LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err)); 1738 return -1; 1739 } 1740 } 1741 1742 /* Also set the frame buffer size off the device on our AudioUnit. This 1743 should make sure that the frames count which we receive in the render 1744 thread is as we like. */ 1745 err = AudioUnitSetProperty(caVoice->audioUnit, 1746 kAudioUnitProperty_MaximumFramesPerSlice, 1747 kAudioUnitScope_Global, 1748 1, 1749 &cFrames, 1750 sizeof(cFrames)); 1751 if (RT_UNLIKELY(err != noErr)) 1752 { 1753 LogRel(("CoreAudio: [Input] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err)); 1754 return -1; 1755 } 1756 1757 /* Finally initialize the new AudioUnit. */ 1758 err = AudioUnitInitialize(caVoice->audioUnit); 1759 if (RT_UNLIKELY(err != noErr)) 1760 { 1761 LogRel(("CoreAudio: [Input] Failed to initialize the AudioUnit (%RI32)\n", err)); 1762 return -1; 1763 } 1764 1765 uSize = sizeof(caVoice->deviceFormat); 1766 err = AudioUnitGetProperty(caVoice->audioUnit, 1767 kAudioUnitProperty_StreamFormat, 1768 kAudioUnitScope_Output, 1769 1, 1770 &caVoice->deviceFormat, 1771 &uSize); 1772 if (RT_UNLIKELY(err != noErr)) 1773 { 1774 LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err)); 1775 return -1; 1776 } 1777 1778 /* There are buggy devices (e.g. my bluetooth headset) which doesn't honor 1779 * the frame buffer size set in the previous calls. So finally get the 1780 * frame buffer size after the AudioUnit was initialized. */ 1781 uSize = sizeof(cFrames); 1782 err = AudioUnitGetProperty(caVoice->audioUnit, 1783 kAudioUnitProperty_MaximumFramesPerSlice, 1784 kAudioUnitScope_Global, 1785 0, 1786 &cFrames, 1787 &uSize); 1788 if (RT_UNLIKELY(err != noErr)) 1789 { 1790 LogRel(("CoreAudio: [Input] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err)); 1791 return -1; 1792 } 1793 1794 /* Calculate the ratio between the device and the stream sample rate. */ 1795 caVoice->sampleRatio = caVoice->streamFormat.mSampleRate / caVoice->deviceFormat.mSampleRate; 1796 1797 /* Set to zero first */ 1798 caVoice->pBuf = NULL; 1799 /* Create the AudioBufferList structure with one buffer. */ 1800 caVoice->bufferList.mNumberBuffers = 1; 1801 /* Initialize the buffer to nothing. */ 1802 caVoice->bufferList.mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame; 1803 caVoice->bufferList.mBuffers[0].mDataByteSize = 0; 1804 caVoice->bufferList.mBuffers[0].mData = NULL; 1805 1806 /* Make sure that the ring buffer is big enough to hold the recording 1807 * data. Compare the maximum frames per slice value with the frames 1808 * necessary when using the converter where the sample rate could differ. 1809 * The result is always multiplied by the channels per frame to get the 1810 * samples count. */ 1811 cSamples = RT_MAX(cFrames, 1812 (cFrames * caVoice->deviceFormat.mBytesPerFrame * caVoice->sampleRatio) / caVoice->streamFormat.mBytesPerFrame) 1813 * caVoice->streamFormat.mChannelsPerFrame; 1814 if ( hw->samples != 0 1815 && hw->samples != (int32_t)cSamples) 1816 LogRel(("CoreAudio: [Input] Warning! After recreation, the CoreAudio ring buffer doesn't has the same size as the device buffer (%RU32 vs. %RU32).\n", cSamples, (uint32_t)hw->samples)); 1817 /* Create the internal ring buffer. */ 1818 IORingBufferCreate(&caVoice->pBuf, cSamples << hw->info.shift); 1819 if (RT_VALID_PTR(caVoice->pBuf)) 1820 rc = 0; 1821 else 1822 LogRel(("CoreAudio: [Input] Failed to create internal ring buffer\n")); 1823 1824 if (rc != 0) 1825 { 1826 if (caVoice->pBuf) 1827 IORingBufferDestroy(caVoice->pBuf); 1828 AudioUnitUninitialize(caVoice->audioUnit); 1829 return -1; 1830 } 1831 1832 #ifdef DEBUG 1833 err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId, 1834 0, 1835 true, 1836 kAudioDeviceProcessorOverload, 1837 caRecordingAudioDevicePropertyChanged, 1838 caVoice); 1839 /* Not Fatal */ 1840 if (RT_UNLIKELY(err != noErr)) 1841 LogRel(("CoreAudio: [Input] Failed to add the processor overload listener (%RI32)\n", err)); 1842 #endif /* DEBUG */ 1843 err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId, 1844 0, 1845 true, 1846 kAudioDevicePropertyNominalSampleRate, 1847 caRecordingAudioDevicePropertyChanged, 1848 caVoice); 1849 /* Not Fatal */ 1850 if (RT_UNLIKELY(err != noErr)) 1851 LogRel(("CoreAudio: [Input] Failed to add the sample rate changed listener (%RI32)\n", err)); 1852 1853 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_INIT); 1854 1855 Log(("CoreAudio: [Input] Frame count: %RU32\n", cFrames)); 1856 1857 return 0; 1858 } 1859 1860 static void caReinitInput(HWVoiceIn *hw) 1861 { 1862 caVoiceIn *caVoice = (caVoiceIn *) hw; 1863 1864 coreaudio_fini_in(&caVoice->hw); 1865 caInitInput(&caVoice->hw); 1866 1867 coreaudio_ctl_in(&caVoice->hw, VOICE_ENABLE); 1128 1868 } 1129 1869 … … 1139 1879 caVoiceIn *caVoice = (caVoiceIn *) hw; 1140 1880 1881 /* Check if the audio device should be reinitialized. If so do it. */ 1882 if (ASMAtomicReadU32(&caVoice->status) == CA_STATUS_REINIT) 1883 caReinitInput(&caVoice->hw); 1884 1885 if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT) 1886 return 0; 1887 1141 1888 /* How much space is used in the ring buffer? */ 1142 1889 csAvail = IORingBufferUsed(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */ … … 1145 1892 csAvail = RT_MIN(csAvail, (uint32_t)(hw->samples - audio_pcm_hw_get_live_in (hw))); 1146 1893 1147 Log2(("CoreAudio: [Input] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));1894 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift)); 1148 1895 1149 1896 /* Iterate as long as data is available */ … … 1153 1900 csToRead = RT_MIN(csAvail - csReads, (uint32_t)(hw->samples - hw->wpos)); 1154 1901 cbToRead = csToRead << hw->info.shift; 1155 Log2(("CoreAudio: [Input] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead));1902 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead)); 1156 1903 /* Try to aquire the necessary block from the ring buffer. */ 1157 1904 IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead); 1158 1905 /* How much to we get? */ 1159 1906 csToRead = cbToRead >> hw->info.shift; 1160 Log2(("CoreAudio: [Input] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead));1907 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead)); 1161 1908 /* Break if nothing is used anymore. */ 1162 1909 if (cbToRead == 0) … … 1172 1919 } 1173 1920 1174 Log2(("CoreAudio: [Input] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));1921 CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift)); 1175 1922 1176 1923 return csReads; … … 1185 1932 { 1186 1933 OSStatus err = noErr; 1934 uint32_t status; 1187 1935 caVoiceIn *caVoice = (caVoiceIn *) hw; 1936 1937 status = ASMAtomicReadU32(&caVoice->status); 1938 if (!( status == CA_STATUS_INIT 1939 || status == CA_STATUS_REINIT)) 1940 return 0; 1188 1941 1189 1942 switch (cmd) … … 1230 1983 } 1231 1984 1232 static int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as)1233 {1234 OSStatus err = noErr;1235 int rc = -1;1236 UInt32 uSize = 0; /* temporary size of properties */1237 UInt32 uFlag = 0; /* for setting flags */1238 CFStringRef name; /* for the temporary device name fetching */1239 const char *pszName;1240 ComponentDescription cd; /* description for an audio component */1241 Component cp; /* an audio component */1242 AURenderCallbackStruct cb; /* holds the callback structure */1243 UInt32 cFrames; /* default frame count */1244 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo */1245 1246 caVoiceIn *caVoice = (caVoiceIn *) hw;1247 1248 caVoice->audioUnit = NULL;1249 caVoice->audioDeviceId = kAudioDeviceUnknown;1250 caVoice->converter = NULL;1251 caVoice->sampleRatio = 1;1252 1253 /* Initialize the hardware info section with the audio settings */1254 audio_pcm_init_info(&hw->info, as);1255 1256 /* Fetch the default audio input device currently in use */1257 uSize = sizeof(caVoice->audioDeviceId);1258 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,1259 &uSize,1260 &caVoice->audioDeviceId);1261 if (RT_UNLIKELY(err != noErr))1262 {1263 LogRel(("CoreAudio: [Input] Unable to find default input device (%RI32)\n", err));1264 return -1;1265 }1266 1267 /* Try to get the name of the default input device and log it. It's not1268 * fatal if it fails. */1269 uSize = sizeof(CFStringRef);1270 err = AudioDeviceGetProperty(caVoice->audioDeviceId,1271 0,1272 1,1273 kAudioObjectPropertyName,1274 &uSize,1275 &name);1276 if (RT_LIKELY(err == noErr))1277 {1278 pszName = CFStringGetCStringPtr(name, kCFStringEncodingMacRoman);1279 if (pszName)1280 LogRel(("CoreAudio: Using default input device: %s\n", pszName));1281 CFRelease(name);1282 }1283 else1284 LogRel(("CoreAudio: [Input] Unable to get input device name (%RI32)\n", err));1285 1286 /* Get the default frames buffer size, so that we can setup our internal1287 * buffers. */1288 uSize = sizeof(cFrames);1289 err = AudioDeviceGetProperty(caVoice->audioDeviceId,1290 0,1291 true,1292 kAudioDevicePropertyBufferFrameSize,1293 &uSize,1294 &cFrames);1295 if (RT_UNLIKELY(err != noErr))1296 {1297 LogRel(("CoreAudio: [Input] Failed to get frame buffer size of the audio device (%RI32)\n", err));1298 return -1;1299 }1300 /* Set the frame buffer size and honor any minimum/maximum restrictions on1301 the device. */1302 err = caSetFrameBufferSize(caVoice->audioDeviceId,1303 true,1304 cFrames,1305 &cFrames);1306 if (RT_UNLIKELY(err != noErr))1307 {1308 LogRel(("CoreAudio: [Input] Failed to set frame buffer size on the audio device (%RI32)\n", err));1309 return -1;1310 }1311 1312 cd.componentType = kAudioUnitType_Output;1313 cd.componentSubType = kAudioUnitSubType_HALOutput;1314 cd.componentManufacturer = kAudioUnitManufacturer_Apple;1315 cd.componentFlags = 0;1316 cd.componentFlagsMask = 0;1317 1318 /* Try to find the default HAL output component. */1319 cp = FindNextComponent(NULL, &cd);1320 if (RT_UNLIKELY(cp == 0))1321 {1322 LogRel(("CoreAudio: [Input] Failed to find HAL output component\n"));1323 return -1;1324 }1325 1326 /* Open the default HAL output component. */1327 err = OpenAComponent(cp, &caVoice->audioUnit);1328 if (RT_UNLIKELY(err != noErr))1329 {1330 LogRel(("CoreAudio: [Input] Failed to open output component (%RI32)\n", err));1331 return -1;1332 }1333 1334 /* Switch the I/O mode for input to on. */1335 uFlag = 1;1336 err = AudioUnitSetProperty(caVoice->audioUnit,1337 kAudioOutputUnitProperty_EnableIO,1338 kAudioUnitScope_Input,1339 1,1340 &uFlag,1341 sizeof(uFlag));1342 if (RT_UNLIKELY(err != noErr))1343 {1344 LogRel(("CoreAudio: [Input] Failed to set input I/O mode enabled (%RI32)\n", err));1345 return -1;1346 }1347 1348 /* Switch the I/O mode for output to off. This is important, as this is a1349 * pure input stream. */1350 uFlag = 0;1351 err = AudioUnitSetProperty(caVoice->audioUnit,1352 kAudioOutputUnitProperty_EnableIO,1353 kAudioUnitScope_Output,1354 0,1355 &uFlag,1356 sizeof(uFlag));1357 if (RT_UNLIKELY(err != noErr))1358 {1359 LogRel(("CoreAudio: [Input] Failed to set output I/O mode disabled (%RI32)\n", err));1360 return -1;1361 }1362 1363 /* Set the default audio input device as the device for the new AudioUnit. */1364 err = AudioUnitSetProperty(caVoice->audioUnit,1365 kAudioOutputUnitProperty_CurrentDevice,1366 kAudioUnitScope_Global,1367 0,1368 &caVoice->audioDeviceId,1369 sizeof(caVoice->audioDeviceId));1370 if (RT_UNLIKELY(err != noErr))1371 {1372 LogRel(("CoreAudio: [Input] Failed to set current device (%RI32)\n", err));1373 return -1;1374 }1375 1376 /* CoreAudio will inform us on a second thread for new incoming audio data.1377 * Therefor register an callback function, which will process the new data.1378 * */1379 cb.inputProc = caRecordingCallback;1380 cb.inputProcRefCon = caVoice;1381 1382 err = AudioUnitSetProperty(caVoice->audioUnit,1383 kAudioOutputUnitProperty_SetInputCallback,1384 kAudioUnitScope_Global,1385 0,1386 &cb,1387 sizeof(cb));1388 if (RT_UNLIKELY(err != noErr))1389 {1390 LogRel(("CoreAudio: [Input] Failed to set callback (%RI32)\n", err));1391 return -1;1392 }1393 1394 /* Fetch the current stream format of the device. */1395 uSize = sizeof(caVoice->deviceFormat);1396 err = AudioUnitGetProperty(caVoice->audioUnit,1397 kAudioUnitProperty_StreamFormat,1398 kAudioUnitScope_Input,1399 1,1400 &caVoice->deviceFormat,1401 &uSize);1402 if (RT_UNLIKELY(err != noErr))1403 {1404 LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err));1405 return -1;1406 }1407 1408 /* Create an AudioStreamBasicDescription based on the audio settings of1409 * VirtualBox. */1410 caAudioSettingsToAudioStreamBasicDescription(as, &caVoice->streamFormat);1411 1412 #if DEBUG1413 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] device", &caVoice->deviceFormat);1414 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] input", &caVoice->streamFormat);1415 #endif /* DEBUG */1416 1417 /* If the frequency of the device is different from the requested one we1418 * need a converter. The same count if the number of channels is different. */1419 if ( caVoice->deviceFormat.mSampleRate != caVoice->streamFormat.mSampleRate1420 || caVoice->deviceFormat.mChannelsPerFrame != caVoice->streamFormat.mChannelsPerFrame)1421 {1422 err = AudioConverterNew(&caVoice->deviceFormat,1423 &caVoice->streamFormat,1424 &caVoice->converter);1425 if (RT_UNLIKELY(err != noErr))1426 {1427 LogRel(("CoreAudio: [Input] Failed to create the audio converter (%RI32)\n", err));1428 return -1;1429 }1430 1431 if (caVoice->deviceFormat.mChannelsPerFrame == 1 &&1432 caVoice->streamFormat.mChannelsPerFrame == 2)1433 {1434 /* If the channel count is different we have to tell this the converter1435 and supply a channel mapping. For now we only support mapping1436 from mono to stereo. For all other cases the core audio defaults1437 are used, which means dropping additional channels in most1438 cases. */1439 err = AudioConverterSetProperty(caVoice->converter,1440 kAudioConverterChannelMap,1441 sizeof(channelMap),1442 channelMap);1443 if (RT_UNLIKELY(err != noErr))1444 {1445 LogRel(("CoreAudio: [Input] Failed to add a channel mapper to the audio converter (%RI32)\n", err));1446 return -1;1447 }1448 }1449 /* Set sample rate converter quality to maximum */1450 /* uFlag = kAudioConverterQuality_Max;*/1451 /* err = AudioConverterSetProperty(caVoice->converter,*/1452 /* kAudioConverterSampleRateConverterQuality,*/1453 /* sizeof(uFlag),*/1454 /* &uFlag);*/1455 /* Not fatal */1456 /* if (RT_UNLIKELY(err != noErr))*/1457 /* LogRel(("CoreAudio: [Input] Failed to set the audio converter quality to the maximum (%RI32)\n", err));*/1458 1459 Log(("CoreAudio: [Input] Converter in use\n"));1460 /* Set the new format description for the stream. */1461 err = AudioUnitSetProperty(caVoice->audioUnit,1462 kAudioUnitProperty_StreamFormat,1463 kAudioUnitScope_Output,1464 1,1465 &caVoice->deviceFormat,1466 sizeof(caVoice->deviceFormat));1467 if (RT_UNLIKELY(err != noErr))1468 {1469 LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));1470 return -1;1471 }1472 err = AudioUnitSetProperty(caVoice->audioUnit,1473 kAudioUnitProperty_StreamFormat,1474 kAudioUnitScope_Input,1475 1,1476 &caVoice->deviceFormat,1477 sizeof(caVoice->deviceFormat));1478 if (RT_UNLIKELY(err != noErr))1479 {1480 LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));1481 return -1;1482 }1483 }1484 else1485 {1486 /* Set the new format description for the stream. */1487 err = AudioUnitSetProperty(caVoice->audioUnit,1488 kAudioUnitProperty_StreamFormat,1489 kAudioUnitScope_Output,1490 1,1491 &caVoice->streamFormat,1492 sizeof(caVoice->streamFormat));1493 if (RT_UNLIKELY(err != noErr))1494 {1495 LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));1496 return -1;1497 }1498 }1499 1500 /* Also set the frame buffer size off the device on our AudioUnit. This1501 should make sure that the frames count which we receive in the render1502 thread is as we like. */1503 err = AudioUnitSetProperty(caVoice->audioUnit,1504 kAudioUnitProperty_MaximumFramesPerSlice,1505 kAudioUnitScope_Global,1506 1,1507 &cFrames,1508 sizeof(cFrames));1509 if (RT_UNLIKELY(err != noErr))1510 {1511 LogRel(("CoreAudio: [Input] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err));1512 return -1;1513 }1514 1515 /* Finally initialize the new AudioUnit. */1516 err = AudioUnitInitialize(caVoice->audioUnit);1517 if (RT_UNLIKELY(err != noErr))1518 {1519 LogRel(("CoreAudio: [Input] Failed to initialize the AudioUnit (%RI32)\n", err));1520 return -1;1521 }1522 1523 uSize = sizeof(caVoice->deviceFormat);1524 err = AudioUnitGetProperty(caVoice->audioUnit,1525 kAudioUnitProperty_StreamFormat,1526 kAudioUnitScope_Output,1527 1,1528 &caVoice->deviceFormat,1529 &uSize);1530 if (RT_UNLIKELY(err != noErr))1531 {1532 LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err));1533 return -1;1534 }1535 1536 /* There are buggy devices (e.g. my bluetooth headset) which doesn't honor1537 * the frame buffer size set in the previous calls. So finally get the1538 * frame buffer size after the AudioUnit was initialized. */1539 uSize = sizeof(cFrames);1540 err = AudioUnitGetProperty(caVoice->audioUnit,1541 kAudioUnitProperty_MaximumFramesPerSlice,1542 kAudioUnitScope_Global,1543 0,1544 &cFrames,1545 &uSize);1546 if (RT_UNLIKELY(err != noErr))1547 {1548 LogRel(("CoreAudio: [Input] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err));1549 return -1;1550 }1551 1552 /* Calculate the ratio between the device and the stream sample rate. */1553 caVoice->sampleRatio = caVoice->streamFormat.mSampleRate / caVoice->deviceFormat.mSampleRate;1554 1555 /* Set to zero first */1556 caVoice->pBuf = NULL;1557 /* Create the AudioBufferList structure with one buffer. */1558 caVoice->bufferList.mNumberBuffers = 1;1559 /* Initialize the buffer to nothing. */1560 caVoice->bufferList.mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame;1561 caVoice->bufferList.mBuffers[0].mDataByteSize = 0;1562 caVoice->bufferList.mBuffers[0].mData = NULL;1563 1564 /* Make sure that the ring buffer is big enough to hold the recording1565 * data. Compare the maximum frames per slice value with the frames1566 * necessary when using the converter where the sample rate could differ.1567 * The result is always multiplied by the channels per frame to get the1568 * samples count. */1569 hw->samples = RT_MAX( cFrames,1570 (cFrames * caVoice->deviceFormat.mBytesPerFrame * caVoice->sampleRatio) / caVoice->streamFormat.mBytesPerFrame)1571 * caVoice->streamFormat.mChannelsPerFrame;1572 /* Create the internal ring buffer. */1573 IORingBufferCreate(&caVoice->pBuf, hw->samples << hw->info.shift);1574 if (VALID_PTR(caVoice->pBuf))1575 rc = 0;1576 else1577 LogRel(("CoreAudio: [Input] Failed to create internal ring buffer\n"));1578 1579 if (rc != 0)1580 {1581 if (caVoice->pBuf)1582 IORingBufferDestroy(caVoice->pBuf);1583 AudioUnitUninitialize(caVoice->audioUnit);1584 }1585 1586 Log(("CoreAudio: [Input] HW samples: %d; Frame count: %RU32\n", hw->samples, cFrames));1587 1588 return 0;1589 }1590 1591 1985 static void coreaudio_fini_in(HWVoiceIn *hw) 1592 1986 { 1593 1987 int rc = 0; 1594 1988 OSStatus err = noErr; 1989 uint32_t status; 1595 1990 caVoiceIn *caVoice = (caVoiceIn *) hw; 1991 1992 status = ASMAtomicReadU32(&caVoice->status); 1993 if (!( status == CA_STATUS_INIT 1994 || status == CA_STATUS_REINIT)) 1995 return; 1596 1996 1597 1997 rc = coreaudio_ctl_in(hw, VOICE_DISABLE); 1598 1998 if (RT_LIKELY(rc == 0)) 1599 1999 { 2000 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_UNINIT); 2001 #ifdef DEBUG 2002 err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId, 2003 0, 2004 true, 2005 kAudioDeviceProcessorOverload, 2006 caRecordingAudioDevicePropertyChanged); 2007 /* Not Fatal */ 2008 if (RT_UNLIKELY(err != noErr)) 2009 LogRel(("CoreAudio: [Input] Failed to remove the processor overload listener (%RI32)\n", err)); 2010 #endif /* DEBUG */ 2011 err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId, 2012 0, 2013 true, 2014 kAudioDevicePropertyNominalSampleRate, 2015 caRecordingAudioDevicePropertyChanged); 2016 /* Not Fatal */ 2017 if (RT_UNLIKELY(err != noErr)) 2018 LogRel(("CoreAudio: [Input] Failed to remove the sample rate changed listener (%RI32)\n", err)); 1600 2019 if (caVoice->converter) 2020 { 1601 2021 AudioConverterDispose(caVoice->converter); 2022 caVoice->converter = NULL; 2023 } 1602 2024 err = AudioUnitUninitialize(caVoice->audioUnit); 1603 2025 if (RT_LIKELY(err == noErr)) … … 1606 2028 if (RT_LIKELY(err == noErr)) 1607 2029 { 2030 IORingBufferDestroy(caVoice->pBuf); 1608 2031 caVoice->audioUnit = NULL; 1609 2032 caVoice->audioDeviceId = kAudioDeviceUnknown; 1610 IORingBufferDestroy(caVoice->pBuf); 2033 caVoice->pBuf = NULL; 2034 caVoice->sampleRatio = 1; 2035 caVoice->rpos = 0; 2036 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT); 1611 2037 } 1612 2038 else … … 1620 2046 } 1621 2047 2048 static int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as) 2049 { 2050 OSStatus err = noErr; 2051 int rc = -1; 2052 bool fDeviceByUser = false; 2053 2054 caVoiceIn *caVoice = (caVoiceIn *) hw; 2055 2056 ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT); 2057 caVoice->audioUnit = NULL; 2058 caVoice->audioDeviceId = kAudioDeviceUnknown; 2059 caVoice->converter = NULL; 2060 caVoice->sampleRatio = 1; 2061 caVoice->rpos = 0; 2062 hw->samples = 0; 2063 2064 /* Initialize the hardware info section with the audio settings */ 2065 audio_pcm_init_info(&hw->info, as); 2066 2067 /* Try to find the audio device set by the user */ 2068 if (conf.pszInputDeviceUID) 2069 { 2070 caVoice->audioDeviceId = caDeviceUIDtoID(conf.pszInputDeviceUID); 2071 /* Not fatal */ 2072 if (caVoice->audioDeviceId == kAudioDeviceUnknown) 2073 LogRel(("CoreAudio: [Input] Unable to find input device %s. Falling back to the default audio device. \n", conf.pszInputDeviceUID)); 2074 else 2075 fDeviceByUser = true; 2076 } 2077 2078 rc = caInitInput(hw); 2079 if (RT_UNLIKELY(rc != 0)) 2080 return rc; 2081 2082 /* The samples have to correspond to the internal ring buffer size. */ 2083 hw->samples = (IORingBufferSize(caVoice->pBuf) >> hw->info.shift) / caVoice->streamFormat.mChannelsPerFrame; 2084 2085 /* When the devices isn't forced by the user, we want default device change 2086 * notifications. */ 2087 if (!fDeviceByUser) 2088 { 2089 err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultInputDevice, 2090 caRecordingDefaultDeviceChanged, 2091 caVoice); 2092 /* Not Fatal */ 2093 if (RT_UNLIKELY(err != noErr)) 2094 LogRel(("CoreAudio: [Input] Failed to add the default device changed listener (%RI32)\n", err)); 2095 } 2096 2097 Log(("CoreAudio: [Input] HW samples: %d\n", hw->samples)); 2098 2099 return 0; 2100 } 2101 1622 2102 /******************************************************************************* 1623 2103 * … … 1638 2118 static struct audio_option coreaudio_options[] = 1639 2119 { 1640 {"BUFFER_SIZE", AUD_OPT_INT, &conf.cBufferFrames, 1641 "Size of the buffer in frames", NULL, 0}, 2120 {"OUTPUT_DEVICE_UID", AUD_OPT_STR, &conf.pszOutputDeviceUID, 2121 "UID of the output device to use", NULL, 0}, 2122 {"INPUT_DEVICE_UID", AUD_OPT_STR, &conf.pszInputDeviceUID, 2123 "UID of the input device to use", NULL, 0}, 1642 2124 {NULL, 0, NULL, NULL, NULL, 0} 1643 2125 };
Note:
See TracChangeset
for help on using the changeset viewer.