VirtualBox

Changeset 26100 in vbox


Ignore:
Timestamp:
Jan 29, 2010 1:43:11 PM (15 years ago)
Author:
vboxsync
Message:

Audio-OSX: Added input/output device change detection, sample rate change
detection, specific device forcing & generic improvements.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/coreaudio.c

    r25917 r26100  
    3636
    3737/* todo:
    38  * - checking for properties changes of the devices
    39  * - checking for changing of the default device
    40  * - let the user set the device used (use config)
    41  * - try to set frame size (use config)
    4238 * - maybe make sure the threads are immediately stopped if playing/recording stops
    4339 */
     
    5046 * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
    5147 */
     48
     49/*#define CA_EXTENSIVE_LOGGING*/
    5250
    5351/*******************************************************************************
     
    121119{
    122120    AssertPtr(pBuffer);
    123     return pBuffer->cBufSize - pBuffer->cBufferUsed;
     121    return pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
    124122}
    125123
     
    127125{
    128126    AssertPtr(pBuffer);
    129     return pBuffer->cBufferUsed;
     127    return ASMAtomicReadU32(&pBuffer->cBufferUsed);
    130128}
    131129
     
    147145
    148146    /* How much is in use? */
    149     uUsed = ASMAtomicAddU32(&pBuffer->cBufferUsed, 0);
     147    uUsed = ASMAtomicReadU32(&pBuffer->cBufferUsed);
    150148    if (uUsed > 0)
    151149    {
     
    184182
    185183    /* How much is free? */
    186     uFree = pBuffer->cBufSize - ASMAtomicAddU32(&pBuffer->cBufferUsed, 0);
     184    uFree = pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
    187185    if (uFree > 0)
    188186    {
     
    250248#endif /* DEBUG */
    251249
    252 static void caAudioSettingsToAudioStreamBasicDescription(const audsettings_t *pAS, AudioStreamBasicDescription *pStreamDesc)
     250static void caPCMInfoToAudioStreamBasicDescription(struct audio_pcm_info *pInfo, AudioStreamBasicDescription *pStreamDesc)
    253251{
    254252    pStreamDesc->mFormatID = kAudioFormatLinearPCM;
    255253    pStreamDesc->mFormatFlags = kAudioFormatFlagIsPacked;
    256254    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;
    299260    pStreamDesc->mBytesPerFrame = pStreamDesc->mChannelsPerFrame * (pStreamDesc->mBitsPerChannel / 8);
    300261    pStreamDesc->mBytesPerPacket = pStreamDesc->mFramesPerPacket * pStreamDesc->mBytesPerFrame;
     
    342303        return err;
    343304    pRange = RTMemAllocZ(cSize);
    344     if (VALID_PTR(pRange))
     305    if (RT_VALID_PTR(pRange))
    345306    {
    346307        err = AudioDeviceGetProperty(device,
     
    399360    UInt32 uFlag = 0;
    400361    UInt32 uSize = sizeof(uFlag);
     362
    401363    err = AudioDeviceGetProperty(deviceID,
    402364                                 0,
     
    410372}
    411373
     374static 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
     400static 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
    412432/*******************************************************************************
    413433 *
     
    416436 ******************************************************************************/
    417437
     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" */
     446static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */
     447
    418448struct
    419449{
    420     int cBufferFrames;
     450    const char *pszOutputDeviceUID;
     451    const char *pszInputDeviceUID;
    421452} conf =
    422453{
    423     INIT_FIELD(.cBufferFrames =) 512
     454    INIT_FIELD(.pszOutputDeviceUID =) NULL,
     455    INIT_FIELD(.pszInputDeviceUID =) NULL
    424456};
    425457
     
    438470    /* A ring buffer for transferring data to the playback thread */
    439471    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;
    440475} caVoiceOut;
    441476
     
    462497    /* A ring buffer for transferring data from the recording thread */
    463498    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;
    464502} caVoiceIn;
    465503
    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
    468509
    469510/*******************************************************************************
     
    473514 ******************************************************************************/
    474515
    475 /* callback to feed audio output buffer */
     516/* We need some forward declarations */
     517static int coreaudio_run_out(HWVoiceOut *hw);
     518static int coreaudio_write(SWVoiceOut *sw, void *buf, int len);
     519static int coreaudio_ctl_out(HWVoiceOut *hw, int cmd, ...);
     520static void coreaudio_fini_out(HWVoiceOut *hw);
     521static int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as);
     522static int caInitOutput(HWVoiceOut *hw);
     523static void caReinitOutput(HWVoiceOut *hw);
     524
     525/* Callback for getting notified when the default output device was changed */
     526static 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 */
     562static 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 */
    476584static OSStatus caPlaybackCallback(void* inRefCon,
    477585                                   AudioUnitRenderActionFlags* ioActionFlags,
     
    489597    caVoiceOut *caVoice = (caVoiceOut *) inRefCon;
    490598
     599    if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
     600        return noErr;
     601
    491602    /* How much space is used in the ring buffer? */
    492603    csAvail = IORingBufferUsed(caVoice->pBuf) >> caVoice->hw.info.shift; /* bytes -> samples */
     
    495606    csAvail = RT_MIN(csAvail, ioData->mBuffers[0].mDataByteSize >> caVoice->hw.info.shift);
    496607
    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));
    498609
    499610    /* Iterate as long as data is available */
     
    503614        csToRead = csAvail - csReads;
    504615        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));
    506617        /* Try to aquire the necessary block from the ring buffer. */
    507618        IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead);
    508619        /* How much to we get? */
    509620        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));
    511622        /* Break if nothing is used anymore. */
    512623        if (RT_UNLIKELY(cbToRead == 0))
     
    522633    ioData->mBuffers[0].mDataByteSize = csReads << caVoice->hw.info.shift; /* samples -> bytes */
    523634
    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));
    525636
    526637    return noErr;
     638}
     639
     640static 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
     930static 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);
    527938}
    528939
     
    538949    caVoiceOut *caVoice = (caVoiceOut *) hw;
    539950
     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
    540963    /* How much space is available in the ring buffer */
    541964    csAvail = IORingBufferFree(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */
     965
    542966    /* How much data is availabe. Use the smaller size of the too. */
    543967    csAvail = RT_MIN(csAvail, (uint32_t)audio_pcm_hw_get_live_out(hw));
    544968
    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));
    546970
    547971    /* Iterate as long as data is available */
     
    551975        csToWrite = RT_MIN(csAvail - csWritten, (uint32_t)(hw->samples - hw->rpos));
    552976        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));
    554978        /* Try to aquire the necessary space from the ring buffer. */
    555979        IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
    556980        /* How much to we get? */
    557981        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));
    559983        /* Break if nothing is free anymore. */
    560984        if (RT_UNLIKELY(cbToWrite == 0))
     
    570994    }
    571995
    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));
    573997
    574998    /* Return the count of samples we have processed. */
     
    5841008{
    5851009    OSStatus err = noErr;
     1010    uint32_t status;
    5861011    caVoiceOut *caVoice = (caVoiceOut *) hw;
     1012
     1013    status = ASMAtomicReadU32(&caVoice->status);
     1014    if (!(   status == CA_STATUS_INIT
     1015          || status == CA_STATUS_REINIT))
     1016        return 0;
    5871017
    5881018    switch (cmd)
     
    5931023                if (!caIsRunning(caVoice->audioDeviceId))
    5941024                {
     1025                    err = AudioUnitReset(caVoice->audioUnit,
     1026                                         kAudioUnitScope_Input,
     1027                                         0);
    5951028                    IORingBufferReset(caVoice->pBuf);
    5961029                    err = AudioOutputUnitStart(caVoice->audioUnit);
     
    6291062}
    6301063
    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 not
    663      * 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     else
    679         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 internal
    682      * 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 on
    696        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 for
    757      * output. Therefor register an callback function which will provide the new
    758      * 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 of
    801      * VirtualBox. */
    802     caAudioSettingsToAudioStreamBasicDescription(as, &caVoice->streamFormat);
    803 
    804 #if DEBUG
    805     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. This
    836        should make sure that the frames count which we receive in the render
    837        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 honor
    859      * the frame buffer size set in the previous calls. So finally get the
    860      * 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 
    8891064static void coreaudio_fini_out(HWVoiceOut *hw)
    8901065{
    8911066    int rc = 0;
     1067    uint32_t status;
    8921068    OSStatus err = noErr;
    8931069    caVoiceOut *caVoice = (caVoiceOut *) hw;
    8941070
     1071    status = ASMAtomicReadU32(&caVoice->status);
     1072    if (!(   status == CA_STATUS_INIT
     1073          || status == CA_STATUS_REINIT))
     1074        return;
     1075
    8951076    rc = coreaudio_ctl_out(hw, VOICE_DISABLE);
    8961077    if (RT_LIKELY(rc == 0))
    8971078    {
     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 */
    8981090        err = AudioUnitUninitialize(caVoice->audioUnit);
    8991091        if (RT_LIKELY(err == noErr))
     
    9021094            if (RT_LIKELY(err == noErr))
    9031095            {
     1096                IORingBufferDestroy(caVoice->pBuf);
    9041097                caVoice->audioUnit = NULL;
    9051098                caVoice->audioDeviceId = kAudioDeviceUnknown;
    906                 IORingBufferDestroy(caVoice->pBuf);
     1099                caVoice->pBuf = NULL;
     1100                ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
    9071101            }
    9081102            else
     
    9161110}
    9171111
     1112static 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
    9181165/*******************************************************************************
    9191166 *
     
    9221169 ******************************************************************************/
    9231170
    924 /* callback to convert audio input data from one format to another */
     1171/* We need some forward declarations */
     1172static int coreaudio_run_in(HWVoiceIn *hw);
     1173static int coreaudio_read(SWVoiceIn *sw, void *buf, int size);
     1174static int coreaudio_ctl_in(HWVoiceIn *hw, int cmd, ...);
     1175static void coreaudio_fini_in(HWVoiceIn *hw);
     1176static int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as);
     1177static int caInitInput(HWVoiceIn *hw);
     1178static void caReinitInput(HWVoiceIn *hw);
     1179
     1180/* Callback for getting notified when the default input device was changed */
     1181static 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 */
     1217static 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 */
    9251250static OSStatus caConverterCallback(AudioConverterRef inAudioConverter,
    9261251                                    UInt32 *ioNumberDataPackets,
     
    9361261
    9371262    const AudioBufferList *pBufferList = &caVoice->bufferList;
     1263
     1264    if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
     1265        return noErr;
     1266
    9381267/*    Log2(("converting .... ################ %RU32  %RU32 %RU32 %RU32 %RU32\n", *ioNumberDataPackets, bufferList->mBuffers[i].mNumberChannels, bufferList->mNumberBuffers, bufferList->mBuffers[i].mDataByteSize, ioData->mNumberBuffers));*/
    9391268
     
    9651294}
    9661295
    967 /* callback to feed audio input buffer */
     1296/* Callback to feed audio input buffer */
    9681297static OSStatus caRecordingCallback(void* inRefCon,
    9691298                                    AudioUnitRenderActionFlags* ioActionFlags,
     
    9841313    caVoiceIn *caVoice = (caVoiceIn *) inRefCon;
    9851314
     1315    if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
     1316        return noErr;
     1317
    9861318    /* If nothing is pending return immediately. */
    9871319    if (inNumberFrames == 0)
     
    9891321
    9901322    /* Are we using an converter? */
    991     if (VALID_PTR(caVoice->converter))
     1323    if (RT_VALID_PTR(caVoice->converter))
    9921324    {
    9931325        /* Firstly render the data as usual */
     
    10151347        csAvail = RT_MIN(csAvail, (uint32_t)((caVoice->bufferList.mBuffers[0].mDataByteSize / caVoice->deviceFormat.mBytesPerFrame) * caVoice->sampleRatio));
    10161348
    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));
    10181350        /* Initialize the temporary output buffer */
    10191351        tmpList.mNumberBuffers = 1;
     
    10271359            csToWrite = csAvail - csWritten;
    10281360            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));
    10301362            /* Try to acquire the necessary space from the ring buffer. */
    10311363            IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
    10321364            /* How much to we get? */
    10331365            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));
    10351367            /* Break if nothing is free anymore. */
    10361368            if (RT_UNLIKELY(cbToWrite == 0))
     
    10691401        /* Cleanup */
    10701402        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));
    10721404    }
    10731405    else
     
    10961428        csAvail = RT_MIN(csAvail, caVoice->bufferList.mBuffers[0].mDataByteSize >> caVoice->hw.info.shift);
    10971429
    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));
    10991431
    11001432        /* Iterate as long as data is available */
     
    11041436            csToWrite = csAvail - csWritten;
    11051437            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));
    11071439            /* Try to aquire the necessary space from the ring buffer. */
    11081440            IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
    11091441            /* How much to we get? */
    11101442            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));
    11121444            /* Break if nothing is free anymore. */
    11131445            if (RT_UNLIKELY(cbToWrite == 0))
     
    11221454        RTMemFree(caVoice->bufferList.mBuffers[0].mData);
    11231455
    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));
    11251457    }
    11261458
    11271459    return err;
     1460}
     1461
     1462static 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
     1860static 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);
    11281868}
    11291869
     
    11391879    caVoiceIn *caVoice = (caVoiceIn *) hw;
    11401880
     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
    11411888    /* How much space is used in the ring buffer? */
    11421889    csAvail = IORingBufferUsed(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */
     
    11451892    csAvail = RT_MIN(csAvail, (uint32_t)(hw->samples - audio_pcm_hw_get_live_in (hw)));
    11461893
    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));
    11481895
    11491896    /* Iterate as long as data is available */
     
    11531900        csToRead = RT_MIN(csAvail - csReads, (uint32_t)(hw->samples - hw->wpos));
    11541901        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));
    11561903        /* Try to aquire the necessary block from the ring buffer. */
    11571904        IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead);
    11581905        /* How much to we get? */
    11591906        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));
    11611908        /* Break if nothing is used anymore. */
    11621909        if (cbToRead == 0)
     
    11721919    }
    11731920
    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));
    11751922
    11761923    return csReads;
     
    11851932{
    11861933    OSStatus err = noErr;
     1934    uint32_t status;
    11871935    caVoiceIn *caVoice = (caVoiceIn *) hw;
     1936
     1937    status = ASMAtomicReadU32(&caVoice->status);
     1938    if (!(   status == CA_STATUS_INIT
     1939          || status == CA_STATUS_REINIT))
     1940        return 0;
    11881941
    11891942    switch (cmd)
     
    12301983}
    12311984
    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 not
    1268      * 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     else
    1284         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 internal
    1287      * 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 on
    1301        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 a
    1349      * 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 of
    1409      * VirtualBox. */
    1410     caAudioSettingsToAudioStreamBasicDescription(as, &caVoice->streamFormat);
    1411 
    1412 #if DEBUG
    1413     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 we
    1418      * need a converter. The same count if the number of channels is different. */
    1419     if (   caVoice->deviceFormat.mSampleRate != caVoice->streamFormat.mSampleRate
    1420         || 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 converter
    1435                and supply a channel mapping. For now we only support mapping
    1436                from mono to stereo. For all other cases the core audio defaults
    1437                are used, which means dropping additional channels in most
    1438                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     else
    1485     {
    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. This
    1501        should make sure that the frames count which we receive in the render
    1502        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 honor
    1537      * the frame buffer size set in the previous calls. So finally get the
    1538      * 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 recording
    1565      * data. Compare the maximum frames per slice value with the frames
    1566      * necessary when using the converter where the sample rate could differ.
    1567      * The result is always multiplied by the channels per frame to get the
    1568      * 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     else
    1577         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 
    15911985static void coreaudio_fini_in(HWVoiceIn *hw)
    15921986{
    15931987    int rc = 0;
    15941988    OSStatus err = noErr;
     1989    uint32_t status;
    15951990    caVoiceIn *caVoice = (caVoiceIn *) hw;
     1991
     1992    status = ASMAtomicReadU32(&caVoice->status);
     1993    if (!(   status == CA_STATUS_INIT
     1994          || status == CA_STATUS_REINIT))
     1995        return;
    15961996
    15971997    rc = coreaudio_ctl_in(hw, VOICE_DISABLE);
    15981998    if (RT_LIKELY(rc == 0))
    15991999    {
     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));
    16002019        if (caVoice->converter)
     2020        {
    16012021            AudioConverterDispose(caVoice->converter);
     2022            caVoice->converter = NULL;
     2023        }
    16022024        err = AudioUnitUninitialize(caVoice->audioUnit);
    16032025        if (RT_LIKELY(err == noErr))
     
    16062028            if (RT_LIKELY(err == noErr))
    16072029            {
     2030                IORingBufferDestroy(caVoice->pBuf);
    16082031                caVoice->audioUnit = NULL;
    16092032                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);
    16112037            }
    16122038            else
     
    16202046}
    16212047
     2048static 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
    16222102/*******************************************************************************
    16232103 *
     
    16382118static struct audio_option coreaudio_options[] =
    16392119{
    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},
    16422124    {NULL, 0, NULL, NULL, NULL, 0}
    16432125};
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette