fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * VBox audio devices: Mac OS X CoreAudio audio driver
c7814cf6e1240a519cbec0441e033d0e2470ed00vboxsync * Copyright (C) 2010-2012 Oracle Corporation
f8e9aa4d8be9a89936a8db42d2e6b0b8283149a4vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
f8e9aa4d8be9a89936a8db42d2e6b0b8283149a4vboxsync * available from http://www.virtualbox.org. This file is free software;
f8e9aa4d8be9a89936a8db42d2e6b0b8283149a4vboxsync * you can redistribute it and/or modify it under the terms of the GNU
f8e9aa4d8be9a89936a8db42d2e6b0b8283149a4vboxsync * General Public License (GPL) as published by the Free Software
f8e9aa4d8be9a89936a8db42d2e6b0b8283149a4vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
f8e9aa4d8be9a89936a8db42d2e6b0b8283149a4vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
f8e9aa4d8be9a89936a8db42d2e6b0b8283149a4vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * - maybe make sure the threads are immediately stopped if playing/recording stops
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/* Most of this is based on:
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * http://developer.apple.com/mac/library/technotes/tn2004/tn2097.html
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * http://developer.apple.com/mac/library/technotes/tn2002/tn2091.html
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * http://developer.apple.com/mac/library/qa/qa2007/qa1533.html
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * http://developer.apple.com/mac/library/qa/qa2001/qa1317.html
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/*#define CA_EXTENSIVE_LOGGING*/
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * IO Ring Buffer section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/* Implementation of a lock free ring buffer which could be used in a multi
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * threaded environment. Note that only the acquire, release and getter
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * functions are threading aware. So don't use reset if the ring buffer is
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * still in use. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The current read position in the buffer */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The current write position in the buffer */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much space of the buffer is currently in use */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How big is the buffer */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The buffer itself */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/* Pointer to an ring buffer structure */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferCreate(PIORINGBUFFER *ppBuffer, uint32_t cSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferDestroy(PIORINGBUFFER pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(void) IORingBufferReset(PIORINGBUFFER pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(uint32_t) IORingBufferFree(PIORINGBUFFER pBuffer)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(uint32_t) IORingBufferUsed(PIORINGBUFFER pBuffer)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsyncDECL_FORCE_INLINE(uint32_t) IORingBufferSize(PIORINGBUFFER pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferAquireReadBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much is in use? */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Get the size out of the requested size, the read block till the end
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * of the buffer & the currently used size. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uSize = RT_MIN(cReqSize, RT_MIN(pBuffer->cBufSize - pBuffer->uReadPos, uUsed));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Return the pointer address which point to the current read
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * position. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(void) IORingBufferReleaseReadBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Split at the end of the buffer. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pBuffer->uReadPos = (pBuffer->uReadPos + cSize) % pBuffer->cBufSize;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferAquireWriteBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much is free? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uFree = pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Get the size out of the requested size, the write block till the end
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * of the buffer & the currently free size. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uSize = RT_MIN(cReqSize, RT_MIN(pBuffer->cBufSize - pBuffer->uWritePos, uFree));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Return the pointer address which point to the current write
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * position. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(void) IORingBufferReleaseWriteBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Split at the end of the buffer. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pBuffer->uWritePos = (pBuffer->uWritePos + cSize) % pBuffer->cBufSize;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * Helper function section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void caDebugOutputAudioStreamBasicDescription(const char *pszDesc, const AudioStreamBasicDescription *pStreamDesc)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("%s AudioStreamBasicDescription:\n", pszDesc));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: Format ID: %RU32 (%c%c%c%c)\n", pStreamDesc->mFormatID, RT_BYTE4(pStreamDesc->mFormatID), RT_BYTE3(pStreamDesc->mFormatID), RT_BYTE2(pStreamDesc->mFormatID), RT_BYTE1(pStreamDesc->mFormatID)));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: Flags: %RU32", pStreamDesc->mFormatFlags));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsFloat)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsBigEndian)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsSignedInteger)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsPacked)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonMixable)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagsAreAllClear)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync snprintf(pszSampleRate, 32, "%.2f", (float)pStreamDesc->mSampleRate);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: SampleRate: %s\n", pszSampleRate));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: ChannelsPerFrame: %RU32\n", pStreamDesc->mChannelsPerFrame));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: FramesPerPacket: %RU32\n", pStreamDesc->mFramesPerPacket));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: BitsPerChannel: %RU32\n", pStreamDesc->mBitsPerChannel));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: BytesPerFrame: %RU32\n", pStreamDesc->mBytesPerFrame));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: BytesPerPacket: %RU32\n", pStreamDesc->mBytesPerPacket));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void caPCMInfoToAudioStreamBasicDescription(struct audio_pcm_info *pInfo, AudioStreamBasicDescription *pStreamDesc)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mFormatFlags = kAudioFormatFlagIsPacked;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mBytesPerFrame = pStreamDesc->mChannelsPerFrame * (pStreamDesc->mBitsPerChannel / 8);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mBytesPerPacket = pStreamDesc->mFramesPerPacket * pStreamDesc->mBytesPerFrame;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsyncstatic OSStatus caSetFrameBufferSize(AudioDeviceID device, bool fInput, UInt32 cReqSize, UInt32 *pcActSize)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* First try to set the new frame buffer size. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Check if it really was set. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* If both sizes are the same, we are done. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* If not we have to check the limits of the device. First get the size of
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync the buffer size range property. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Search for the absolute minimum. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Search for the best maximum which isn't bigger than
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cReqSize. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* First try to set the new frame buffer size. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Check if it really was set. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(bool) caIsRunning(AudioDeviceID deviceID)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: Could not determine whether the device is running (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic char* caCFStringToCString(const CFStringRef pCFString)
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * CFStringGetCStringPtr doesn't reliably return requested string instead return depends on "many factors" (not clear which)
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * ( please follow the link
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * http://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFStringRef/Reference/reference.html
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * for more details). Branch below allocates memory using mechanisms which hasn't got single method for memory free:
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * RTStrDup - RTStrFree
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * RTMemAllocZTag - RTMemFree
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * which aren't compatible, opposite to CFStringGetCStringPtr CFStringGetCString has well defined
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync * behaviour and confident return value.
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* First try to get the pointer directly. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszTmp = CFStringGetCStringPtr(pCFString, kCFStringEncodingUTF8);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* On success make a copy */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* If the pointer isn't available directly, we have to make a copy. */
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync pszResult = RTMemAllocZTag(cLen * sizeof(char), RTSTR_TAG);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync /* If the pointer isn't available directly, we have to make a copy. */
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync pszResult = RTMemAllocZTag(cLen * sizeof(char), RTSTR_TAG);
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic AudioDeviceID caDeviceUIDtoID(const char* pszUID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Create a CFString out of our CString */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fill the translation structure */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync translation.mOutputDataSize = sizeof(AudioDeviceID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fetch the translation from the UID to the audio Id */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Release the temporary CFString */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Return the unknown device on error */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * Global structures section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Initialization status indicator used for the recreation of the AudioUnits. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#define CA_STATUS_UNINIT UINT32_C(0) /* The device is uninitialized */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#define CA_STATUS_IN_INIT UINT32_C(1) /* The device is currently initializing */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#define CA_STATUS_INIT UINT32_C(2) /* The device is initialized */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#define CA_STATUS_IN_UNINIT UINT32_C(3) /* The device is currently uninitializing */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#define CA_STATUS_REINIT UINT32_C(4) /* The device has to be reinitialized */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Error code which indicates "End of data" */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* HW voice output structure defined by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is default on the device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is selected for using by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The audio device ID of the currently used device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The AudioUnit used */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* A ring buffer for transferring data to the playback thread */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialization status tracker. Used when some of the device parameters
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * or the device itself is changed during the runtime. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* HW voice input structure defined by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is default on the device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is selected for using by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The audio device ID of the currently used device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The AudioUnit used */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* The audio converter if necessary */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* A temporary position value used in the caConverterCallback function */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* The ratio between the device & the stream sample rate */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* An extra buffer used for render the audio data in the recording thread */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* A ring buffer for transferring data from the recording thread */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialization status tracker. Used when some of the device parameters
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * or the device itself is changed during the runtime. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * CoreAudio output section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* We need some forward declarations */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_write(SWVoiceOut *sw, void *buf, int len);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_out(HWVoiceOut *hw, int cmd, ...);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when the default output device was changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caPlaybackDefaultDeviceChanged(AudioHardwarePropertyID inPropertyID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* This listener is called on every change of the hardware
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * device. So check if the default device has really changed. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log2(("CoreAudio: [Output] Default output device changed!\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* We move the reinitialization to the next output event.
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * This make sure this thread isn't blocked and the
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * reinitialization is done when necessary only. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_REINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when some of the properties of an audio device has changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caPlaybackAudioDevicePropertyChanged(AudioDeviceID inDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log2(("CoreAudio: [Output] Processor overload detected!\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync default: break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback to feed audio output buffer */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caPlaybackCallback(void* inRefCon,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much space is used in the ring buffer? */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync csAvail = IORingBufferUsed(caVoice->pBuf) >> caVoice->hw.info.shift; /* bytes -> samples */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much space is available in the core audio buffer. Use the smaller
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * size of the too. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync csAvail = RT_MIN(csAvail, ioData->mBuffers[0].mDataByteSize >> caVoice->hw.info.shift);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Iterate as long as data is available */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much is left? */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cbToRead = csToRead << caVoice->hw.info.shift; /* samples -> bytes */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead));
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* Try to acquire the necessary block from the ring buffer. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much to we get? */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync csToRead = cbToRead >> caVoice->hw.info.shift; /* bytes -> samples */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Break if nothing is used anymore. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Copy the data from our ring buffer to the core audio buffer. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync memcpy((char*)ioData->mBuffers[0].mData + (csReads << caVoice->hw.info.shift), pcSrc, cbToRead);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Release the read buffer, so it could be used for new data. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync IORingBufferReleaseReadBlock(caVoice->pBuf, cbToRead);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much have we reads so far. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Write the bytes to the core audio buffer which where really written. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ioData->mBuffers[0].mDataByteSize = csReads << caVoice->hw.info.shift; /* samples -> bytes */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uSize = 0; /* temporary size of properties */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync CFStringRef name; /* for the temporary device name fetching */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ComponentDescription cd; /* description for an audio component */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AURenderCallbackStruct cb; /* holds the callback structure */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_INIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fetch the default audio output device currently in use */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Unable to find default output device (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Try to get the name of the output device and log it. It's not fatal if
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * it fails. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: Using output device: %s (UID: %s)\n", pszName, pszUID));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Unable to get output device name (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Get the default frames buffer size, so that we can setup our internal
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * buffers. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to get frame buffer size of the audio device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the frame buffer size and honor any minimum/maximum restrictions on
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync the device. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to set frame buffer size on the audio device (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentManufacturer = kAudioUnitManufacturer_Apple;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Try to find the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to find HAL output component\n"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Open the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to open output component (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Switch the I/O mode for output to on. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to set output I/O mode enabled (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the default audio output device as the device for the new AudioUnit. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to set current device (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* CoreAudio will inform us on a second thread when it needs more data for
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * output. Therefor register an callback function which will provide the new
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to set callback (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the quality of the output render to the maximum. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* uFlag = kRenderQuality_High;*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* err = AudioUnitSetProperty(caVoice->audioUnit,*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* kAudioUnitProperty_RenderQuality,*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* kAudioUnitScope_Global,*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* &uFlag,*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* sizeof(uFlag));*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Not fatal */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* if (RT_UNLIKELY(err != noErr))*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* LogRel(("CoreAudio: [Output] Failed to set the render quality to the maximum (%RI32)\n", err));*/
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Fetch the current stream format of the device. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create an AudioStreamBasicDescription based on the audio settings of
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * VirtualBox. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caPCMInfoToAudioStreamBasicDescription(&caVoice->hw.info, &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] device", &caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] output", &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#endif /* DEBUG */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Set the device format description for the stream. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to set stream format (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Also set the frame buffer size off the device on our AudioUnit. This
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync should make sure that the frames count which we receive in the render
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync thread is as we like. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Finally initialize the new AudioUnit. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to initialize the AudioUnit (%RI32)\n", err));
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * the frame buffer size set in the previous calls. So finally get the
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * frame buffer size after the AudioUnit was initialized. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create the internal ring buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync cSamples = cFrames * caVoice->streamFormat.mChannelsPerFrame;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferCreate(&caVoice->pBuf, cSamples << hw->info.shift);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to create internal ring buffer\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 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));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to add the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_INIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Output] Frame count: %RU32\n", cFrames));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Check if the audio device should be reinitialized. If so do it. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) == CA_STATUS_REINIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* We return the live count in the case we are not initialized. This should
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * prevent any under runs. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Make sure the device is running */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much space is available in the ring buffer */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csAvail = IORingBufferFree(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* How much data is available. Use the smaller size of the too. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csAvail = RT_MIN(csAvail, (uint32_t)audio_pcm_hw_get_live_out(hw));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << hw->info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Iterate as long as data is available */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much is left? Split request at the end of our samples buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csToWrite = RT_MIN(csAvail - csWritten, (uint32_t)(hw->samples - hw->rpos));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync cbToWrite = csToWrite << hw->info.shift; /* samples -> bytes */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* Try to acquire the necessary space from the ring buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much to we get? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Break if nothing is free anymore. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Copy the data from our mix buffer to the ring buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Release the ring buffer, so the read thread could start reading this data. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferReleaseWriteBlock(caVoice->pBuf, cbToWrite);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much have we written so far. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << hw->info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Return the count of samples we have processed. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_write(SWVoiceOut *sw, void *buf, int len)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_out(HWVoiceOut *hw, int cmd, ...)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only start the device if it is actually stopped */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to start playback (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only stop the device if it is actually running */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to stop playback (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to reset AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to remove the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to close the AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to uninitialize the AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to stop playback (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync bool fDeviceByUser = false; /* use we a device which was set by the user? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialize the hardware info section with the audio settings */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Try to find the audio device set by the user. Use
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * to set it. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioDeviceId = caDeviceUIDtoID(conf.pszOutputDeviceUID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Unable to find output device %s. Falling back to the default audio device. \n", conf.pszOutputDeviceUID));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* The samples have to correspond to the internal ring buffer size. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->samples = (IORingBufferSize(caVoice->pBuf) >> hw->info.shift) / caVoice->streamFormat.mChannelsPerFrame;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* When the devices isn't forced by the user, we want default device change
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * notifications. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to add the default device changed listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Output] HW samples: %d\n", hw->samples));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/*******************************************************************************
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * CoreAudio input section
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ******************************************************************************/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* We need some forward declarations */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_read(SWVoiceIn *sw, void *buf, int size);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_in(HWVoiceIn *hw, int cmd, ...);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when the default input device was changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caRecordingDefaultDeviceChanged(AudioHardwarePropertyID inPropertyID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* This listener is called on every change of the hardware
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * device. So check if the default device has really changed. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log2(("CoreAudio: [Input] Default input device changed!\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* We move the reinitialization to the next input event.
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * This make sure this thread isn't blocked and the
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * reinitialization is done when necessary only. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_REINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when some of the properties of an audio device has changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caRecordingAudioDevicePropertyChanged(AudioDeviceID inDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log2(("CoreAudio: [Input] Processor overload detected!\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log2(("CoreAudio: [Input] Sample rate changed!\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* We move the reinitialization to the next input event.
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * This make sure this thread isn't blocked and the
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * reinitialization is done when necessary only. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_REINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync default: break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback to convert audio input data from one format to another */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caConverterCallback(AudioConverterRef inAudioConverter,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioStreamPacketDescription **outDataPacketDescription,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* In principle we had to check here if the source is non interleaved & if
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * so go through all buffers not only the first one like now. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync const AudioBufferList *pBufferList = &caVoice->bufferList;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Log2(("converting .... ################ %RU32 %RU32 %RU32 %RU32 %RU32\n", *ioNumberDataPackets, bufferList->mBuffers[i].mNumberChannels, bufferList->mNumberBuffers, bufferList->mBuffers[i].mDataByteSize, ioData->mNumberBuffers));*/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Use the lower one of the packets to process & the available packets in
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * the buffer */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync cSize = RT_MIN(*ioNumberDataPackets * caVoice->deviceFormat.mBytesPerPacket,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pBufferList->mBuffers[0].mDataByteSize - caVoice->rpos);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Set the new size on output, so the caller know what we have processed. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync *ioNumberDataPackets = cSize / caVoice->deviceFormat.mBytesPerPacket;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* If no data is available anymore we return with an error code. This error
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * code will be returned from AudioConverterFillComplexBuffer. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioData->mBuffers[0].mNumberChannels = pBufferList->mBuffers[0].mNumberChannels;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioData->mBuffers[0].mData = (char*)pBufferList->mBuffers[0].mData + caVoice->rpos;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Log2(("converting .... ################ %RU32 %RU32\n", size, caVoice->rpos));*/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback to feed audio input buffer */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caRecordingCallback(void* inRefCon,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* If nothing is pending return immediately. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Are we using an converter? */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Firstly render the data as usual */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mNumberChannels = caVoice->deviceFormat.mChannelsPerFrame;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mDataByteSize = caVoice->deviceFormat.mBytesPerFrame * inNumberFrames;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mData = RTMemAlloc(caVoice->bufferList.mBuffers[0].mDataByteSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Log(("CoreAudio: [Input] Failed to render audio data (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much space is free in the ring buffer? */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csAvail = IORingBufferFree(caVoice->pBuf) >> caVoice->hw.info.shift; /* bytes -> samples */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much space is used in the core audio buffer. Use the smaller size of
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * the too. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csAvail = RT_MIN(csAvail, (uint32_t)((caVoice->bufferList.mBuffers[0].mDataByteSize / caVoice->deviceFormat.mBytesPerFrame) * caVoice->sampleRatio));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Initialize the temporary output buffer */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync tmpList.mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the read position to zero. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Iterate as long as data is available */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much is left? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Try to acquire the necessary space from the ring buffer. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much to we get? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Break if nothing is free anymore. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Now set how much space is available for output */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioOutputDataPacketSize = cbToWrite / caVoice->streamFormat.mBytesPerPacket;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set our ring buffer as target. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioConverterFillComplexBuffer(caVoice->converter,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Log(("CoreAudio: [Input] Failed to convert audio data (%RI32:%c%c%c%c)\n", err, RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Check in any case what processed size is returned. It could be
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * much littler than we expected. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cbToWrite = ioOutputDataPacketSize * caVoice->streamFormat.mBytesPerPacket;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Release the ring buffer, so the main thread could start reading this data. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync IORingBufferReleaseWriteBlock(caVoice->pBuf, cbToWrite);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* If the error is "End of Data" it means there is no data anymore
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * which could be converted. So end here now. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Cleanup */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mDataByteSize = caVoice->streamFormat.mBytesPerFrame * inNumberFrames;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mData = RTMemAlloc(caVoice->bufferList.mBuffers[0].mDataByteSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Log(("CoreAudio: [Input] Failed to render audio data (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much space is free in the ring buffer? */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csAvail = IORingBufferFree(caVoice->pBuf) >> caVoice->hw.info.shift; /* bytes -> samples */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much space is used in the core audio buffer. Use the smaller size of
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * the too. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csAvail = RT_MIN(csAvail, caVoice->bufferList.mBuffers[0].mDataByteSize >> caVoice->hw.info.shift);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Iterate as long as data is available */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much is left? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* Try to acquire the necessary space from the ring buffer. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much to we get? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Break if nothing is free anymore. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Copy the data from the core audio buffer to the ring buffer. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync memcpy(pcDst, (char*)caVoice->bufferList.mBuffers[0].mData + (csWritten << caVoice->hw.info.shift), cbToWrite);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Release the ring buffer, so the main thread could start reading this data. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync IORingBufferReleaseWriteBlock(caVoice->pBuf, cbToWrite);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Cleanup */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uSize = 0; /* temporary size of properties */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync CFStringRef name; /* for the temporary device name fetching */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ComponentDescription cd; /* description for an audio component */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AURenderCallbackStruct cb; /* holds the callback structure */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_INIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fetch the default audio output device currently in use */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Unable to find default input device (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Try to get the name of the input device and log it. It's not fatal if
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * it fails. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: Using input device: %s (UID: %s)\n", pszName, pszUID));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Unable to get input device name (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Get the default frames buffer size, so that we can setup our internal
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * buffers. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to get frame buffer size of the audio device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the frame buffer size and honor any minimum/maximum restrictions on
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync the device. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set frame buffer size on the audio device (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentManufacturer = kAudioUnitManufacturer_Apple;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Try to find the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to find HAL output component\n"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Open the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to open output component (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Switch the I/O mode for input to on. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to set input I/O mode enabled (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Switch the I/O mode for output to off. This is important, as this is a
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * pure input stream. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to set output I/O mode disabled (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the default audio input device as the device for the new AudioUnit. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set current device (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* CoreAudio will inform us on a second thread for new incoming audio data.
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * Therefor register an callback function, which will process the new data.
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to set callback (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Fetch the current stream format of the device. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create an AudioStreamBasicDescription based on the audio settings of
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * VirtualBox. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caPCMInfoToAudioStreamBasicDescription(&caVoice->hw.info, &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] device", &caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] input", &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#endif /* DEBUG */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* If the frequency of the device is different from the requested one we
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * need a converter. The same count if the number of channels is different. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if ( caVoice->deviceFormat.mSampleRate != caVoice->streamFormat.mSampleRate
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync || caVoice->deviceFormat.mChannelsPerFrame != caVoice->streamFormat.mChannelsPerFrame)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to create the audio converter (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (caVoice->deviceFormat.mChannelsPerFrame == 1 &&
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* If the channel count is different we have to tell this the converter
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync and supply a channel mapping. For now we only support mapping
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync from mono to stereo. For all other cases the core audio defaults
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync are used, which means dropping additional channels in most
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioConverterSetProperty(caVoice->converter,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to add a channel mapper to the audio converter (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set sample rate converter quality to maximum */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* uFlag = kAudioConverterQuality_Max;*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* err = AudioConverterSetProperty(caVoice->converter,*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* kAudioConverterSampleRateConverterQuality,*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* sizeof(uFlag),*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* &uFlag);*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Not fatal */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* if (RT_UNLIKELY(err != noErr))*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync/* LogRel(("CoreAudio: [Input] Failed to set the audio converter quality to the maximum (%RI32)\n", err));*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the new format description for the stream. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Set the new format description for the stream. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Also set the frame buffer size off the device on our AudioUnit. This
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync should make sure that the frames count which we receive in the render
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync thread is as we like. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Finally initialize the new AudioUnit. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to initialize the AudioUnit (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err));
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * the frame buffer size set in the previous calls. So finally get the
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * frame buffer size after the AudioUnit was initialized. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Calculate the ratio between the device and the stream sample rate. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->sampleRatio = caVoice->streamFormat.mSampleRate / caVoice->deviceFormat.mSampleRate;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Set to zero first */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create the AudioBufferList structure with one buffer. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Initialize the buffer to nothing. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Make sure that the ring buffer is big enough to hold the recording
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * data. Compare the maximum frames per slice value with the frames
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * necessary when using the converter where the sample rate could differ.
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * The result is always multiplied by the channels per frame to get the
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * samples count. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync (cFrames * caVoice->deviceFormat.mBytesPerFrame * caVoice->sampleRatio) / caVoice->streamFormat.mBytesPerFrame)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 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));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Create the internal ring buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferCreate(&caVoice->pBuf, cSamples << hw->info.shift);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to create internal ring buffer\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to add the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to add the sample rate changed listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_INIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Input] Frame count: %RU32\n", cFrames));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Check if the audio device should be reinitialized. If so do it. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) == CA_STATUS_REINIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much space is used in the ring buffer? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csAvail = IORingBufferUsed(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much space is available in the mix buffer. Use the smaller size of
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * the too. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csAvail = RT_MIN(csAvail, (uint32_t)(hw->samples - audio_pcm_hw_get_live_in (hw)));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Iterate as long as data is available */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much is left? Split request at the end of our samples buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csToRead = RT_MIN(csAvail - csReads, (uint32_t)(hw->samples - hw->wpos));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead));
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* Try to acquire the necessary block from the ring buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much to we get? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Break if nothing is used anymore. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Copy the data from our ring buffer to the mix buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Release the read buffer, so it could be used for new data. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferReleaseReadBlock(caVoice->pBuf, cbToRead);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much have we reads so far. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_read(SWVoiceIn *sw, void *buf, int size)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_in(HWVoiceIn *hw, int cmd, ...)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only start the device if it is actually stopped */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to start recording (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only stop the device if it is actually running */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to stop recording (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to reset AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to remove the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to remove the sample rate changed listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to close the AudioUnit (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to uninitialize the AudioUnit (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to stop recording (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialize the hardware info section with the audio settings */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Try to find the audio device set by the user */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioDeviceId = caDeviceUIDtoID(conf.pszInputDeviceUID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Unable to find input device %s. Falling back to the default audio device. \n", conf.pszInputDeviceUID));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* The samples have to correspond to the internal ring buffer size. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->samples = (IORingBufferSize(caVoice->pBuf) >> hw->info.shift) / caVoice->streamFormat.mChannelsPerFrame;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* When the devices isn't forced by the user, we want default device change
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * notifications. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultInputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to add the default device changed listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Input] HW samples: %d\n", hw->samples));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * CoreAudio global section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
4a7922a63c8704e7d5cb5af1264745117d037d1dvboxsync {"OutputDeviceUID", AUD_OPT_STR, &conf.pszOutputDeviceUID,
4a7922a63c8704e7d5cb5af1264745117d037d1dvboxsync {"InputDeviceUID", AUD_OPT_STR, &conf.pszInputDeviceUID,