fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/* $Id$ */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/** @file
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * VBox audio devices: Mac OS X CoreAudio audio driver
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync/*
c7814cf6e1240a519cbec0441e033d0e2470ed00vboxsync * Copyright (C) 2010-2012 Oracle Corporation
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync *
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.
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync */
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#define LOG_GROUP LOG_GROUP_DEV_AUDIO
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#include <VBox/log.h>
3e1c765e4b714192d779f0b4cd66d3f1d5643fd8vboxsync#include <iprt/asm.h>
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#include <iprt/mem.h>
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#include <iprt/cdefs.h>
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync#define AUDIO_CAP "coreaudio"
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#include "vl_vbox.h"
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#include "audio.h"
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync#include "audio_int.h"
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#include <CoreAudio/CoreAudio.h>
8a03453415c4398bcb4ea110c1b93e03828d498cvboxsync#include <CoreServices/CoreServices.h>
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#include <AudioUnit/AudioUnit.h>
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync#include <AudioToolbox/AudioConverter.h>
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/* todo:
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * - maybe make sure the threads are immediately stopped if playing/recording stops
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync */
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
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
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync */
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/*#define CA_EXTENSIVE_LOGGING*/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * IO Ring Buffer section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
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. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsynctypedef struct IORINGBUFFER
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The current read position in the buffer */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t uReadPos;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The current write position in the buffer */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t uWritePos;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much space of the buffer is currently in use */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync volatile uint32_t cBufferUsed;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How big is the buffer */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t cBufSize;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The buffer itself */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync char *pBuffer;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync} IORINGBUFFER;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/* Pointer to an ring buffer structure */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsynctypedef IORINGBUFFER* PIORINGBUFFER;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferCreate(PIORINGBUFFER *ppBuffer, uint32_t cSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync PIORINGBUFFER pTmpBuffer;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(ppBuffer);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *ppBuffer = NULL;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pTmpBuffer = RTMemAllocZ(sizeof(IORINGBUFFER));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pTmpBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pTmpBuffer->pBuffer = RTMemAlloc(cSize);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if(pTmpBuffer->pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pTmpBuffer->cBufSize = cSize;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *ppBuffer = pTmpBuffer;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync else
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync RTMemFree(pTmpBuffer);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferDestroy(PIORINGBUFFER pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pBuffer->pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync RTMemFree(pBuffer->pBuffer);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync RTMemFree(pBuffer);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(void) IORingBufferReset(PIORINGBUFFER pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(pBuffer);
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pBuffer->uReadPos = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pBuffer->uWritePos = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pBuffer->cBufferUsed = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(uint32_t) IORingBufferFree(PIORINGBUFFER pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(pBuffer);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(uint32_t) IORingBufferUsed(PIORINGBUFFER pBuffer)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(pBuffer);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return ASMAtomicReadU32(&pBuffer->cBufferUsed);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsyncDECL_FORCE_INLINE(uint32_t) IORingBufferSize(PIORINGBUFFER pBuffer)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync{
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AssertPtr(pBuffer);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return pBuffer->cBufSize;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync}
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferAquireReadBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t uUsed = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t uSize = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(pBuffer);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *ppStart = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *pcSize = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much is in use? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uUsed = ASMAtomicReadU32(&pBuffer->cBufferUsed);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (uUsed > 0)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
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 if (uSize > 0)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Return the pointer address which point to the current read
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * position. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *ppStart = pBuffer->pBuffer + pBuffer->uReadPos;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *pcSize = uSize;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(void) IORingBufferReleaseReadBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(pBuffer);
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Split at the end of the buffer. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pBuffer->uReadPos = (pBuffer->uReadPos + cSize) % pBuffer->cBufSize;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
2640b304447a3353a161e04ea294b8b66e18dc5dvboxsync ASMAtomicSubU32(&pBuffer->cBufferUsed, cSize);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void IORingBufferAquireWriteBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t uFree;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t uSize;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(pBuffer);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *ppStart = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *pcSize = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much is free? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uFree = pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (uFree > 0)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
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 if (uSize > 0)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Return the pointer address which point to the current write
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * position. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *ppStart = pBuffer->pBuffer + pBuffer->uWritePos;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *pcSize = uSize;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(void) IORingBufferReleaseWriteBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AssertPtr(pBuffer);
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Split at the end of the buffer. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pBuffer->uWritePos = (pBuffer->uWritePos + cSize) % pBuffer->cBufSize;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ASMAtomicAddU32(&pBuffer->cBufferUsed, cSize);
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * Helper function section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#if DEBUG
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void caDebugOutputAudioStreamBasicDescription(const char *pszDesc, const AudioStreamBasicDescription *pStreamDesc)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync char pszSampleRate[32];
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 Log((" Float"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsBigEndian)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log((" BigEndian"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsSignedInteger)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log((" SignedInteger"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsPacked)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log((" Packed"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log((" AlignedHigh"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log((" NonInterleaved"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonMixable)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log((" NonMixable"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (pStreamDesc->mFormatFlags & kAudioFormatFlagsAreAllClear)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log((" AllClear"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("\n"));
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}
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#endif /* DEBUG */
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void caPCMInfoToAudioStreamBasicDescription(struct audio_pcm_info *pInfo, AudioStreamBasicDescription *pStreamDesc)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mFormatID = kAudioFormatLinearPCM;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mFormatFlags = kAudioFormatFlagIsPacked;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mFramesPerPacket = 1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pStreamDesc->mSampleRate = (Float64)pInfo->freq;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pStreamDesc->mChannelsPerFrame = pInfo->nchannels;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pStreamDesc->mBitsPerChannel = pInfo->bits;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (pInfo->sign == 1)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mBytesPerFrame = pStreamDesc->mChannelsPerFrame * (pStreamDesc->mBitsPerChannel / 8);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync pStreamDesc->mBytesPerPacket = pStreamDesc->mFramesPerPacket * pStreamDesc->mBytesPerFrame;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsyncstatic OSStatus caSetFrameBufferSize(AudioDeviceID device, bool fInput, UInt32 cReqSize, UInt32 *pcActSize)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync{
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync OSStatus err = noErr;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync UInt32 cSize = 0;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioValueRange *pRange = NULL;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync size_t a = 0;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Float64 cMin = -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Float64 cMax = -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* First try to set the new frame buffer size. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioDeviceSetProperty(device,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync NULL,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync fInput,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferFrameSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(cReqSize),
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cReqSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Check if it really was set. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cSize = sizeof(*pcActSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetProperty(device,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync fInput,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferFrameSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync pcActSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return err;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* If both sizes are the same, we are done. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (cReqSize == *pcActSize)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return noErr;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* If not we have to check the limits of the device. First get the size of
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync the buffer size range property. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetPropertyInfo(device,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync fInput,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferSizeRange,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync NULL);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return err;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync pRange = RTMemAllocZ(cSize);
380ba45687546873675bd90cd50c26d1c049fe5dvboxsync if (pRange)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetProperty(device,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync fInput,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferSizeRange,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync pRange);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_LIKELY(err == noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync for (a=0; a < cSize/sizeof(AudioValueRange); ++a)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Search for the absolute minimum. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if ( pRange[a].mMinimum < cMin
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync || cMin == -1)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cMin = pRange[a].mMinimum;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Search for the best maximum which isn't bigger than
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cReqSize. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (pRange[a].mMaximum < cReqSize)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (pRange[a].mMaximum > cMax)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cMax = pRange[a].mMaximum;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (cMax == -1)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cMax = cMin;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cReqSize = cMax;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* First try to set the new frame buffer size. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioDeviceSetProperty(device,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync NULL,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync fInput,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferFrameSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(cReqSize),
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cReqSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Check if it really was set. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cSize = sizeof(*pcActSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetProperty(device,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync fInput,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferFrameSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync pcActSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync else
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return notEnoughMemoryErr;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync RTMemFree(pRange);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return err;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync}
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncDECL_FORCE_INLINE(bool) caIsRunning(AudioDeviceID deviceID)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync OSStatus err = noErr;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uFlag = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uSize = sizeof(uFlag);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioDeviceGetProperty(deviceID,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioDevicePropertyDeviceIsRunning,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uSize,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uFlag);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (err != kAudioHardwareNoError)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: Could not determine whether the device is running (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return uFlag >= 1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic char* caCFStringToCString(const CFStringRef pCFString)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync char *pszResult = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CFIndex cLen;
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync#if 0
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync /**
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.
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync */
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync const char *pszTmp = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* First try to get the pointer directly. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszTmp = CFStringGetCStringPtr(pCFString, kCFStringEncodingUTF8);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (pszTmp)
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* On success make a copy */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszResult = RTStrDup(pszTmp);
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync else
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* If the pointer isn't available directly, we have to make a copy. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync cLen = CFStringGetLength(pCFString) + 1;
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync pszResult = RTMemAllocZTag(cLen * sizeof(char), RTSTR_TAG);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync RTStrFree(pszResult);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszResult = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync#else
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync /* If the pointer isn't available directly, we have to make a copy. */
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync cLen = CFStringGetLength(pCFString) + 1;
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync pszResult = RTMemAllocZTag(cLen * sizeof(char), RTSTR_TAG);
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync {
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync RTStrFree(pszResult);
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync pszResult = NULL;
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync }
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync#endif
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return pszResult;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic AudioDeviceID caDeviceUIDtoID(const char* pszUID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync UInt32 uSize;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync AudioValueTranslation translation;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CFStringRef strUID;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync AudioDeviceID audioId;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Create a CFString out of our CString */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync strUID = CFStringCreateWithCString(NULL,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszUID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kCFStringEncodingMacRoman);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fill the translation structure */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync translation.mInputData = &strUID;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync translation.mInputDataSize = sizeof(CFStringRef);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync translation.mOutputData = &audioId;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync translation.mOutputDataSize = sizeof(AudioDeviceID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uSize = sizeof(AudioValueTranslation);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fetch the translation from the UID to the audio Id */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &uSize,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &translation);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Release the temporary CFString */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CFRelease(strUID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_LIKELY(err == noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return audioId;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Return the unknown device on error */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return kAudioDeviceUnknown;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * Global structures section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
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
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Error code which indicates "End of data" */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstruct
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync const char *pszOutputDeviceUID;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync const char *pszInputDeviceUID;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync} conf =
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync INIT_FIELD(.pszOutputDeviceUID =) NULL,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync INIT_FIELD(.pszInputDeviceUID =) NULL
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync};
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsynctypedef struct caVoiceOut
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* HW voice output structure defined by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync HWVoiceOut hw;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is default on the device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioStreamBasicDescription deviceFormat;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is selected for using by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioStreamBasicDescription streamFormat;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The audio device ID of the currently used device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioDeviceID audioDeviceId;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The AudioUnit used */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioUnit audioUnit;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* A ring buffer for transferring data to the playback thread */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync PIORINGBUFFER pBuf;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialization status tracker. Used when some of the device parameters
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * or the device itself is changed during the runtime. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync volatile uint32_t status;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync} caVoiceOut;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsynctypedef struct caVoiceIn
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* HW voice input structure defined by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync HWVoiceIn hw;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is default on the device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioStreamBasicDescription deviceFormat;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Stream description which is selected for using by VBox */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioStreamBasicDescription streamFormat;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The audio device ID of the currently used device */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioDeviceID audioDeviceId;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* The AudioUnit used */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioUnit audioUnit;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* The audio converter if necessary */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioConverterRef converter;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* A temporary position value used in the caConverterCallback function */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync uint32_t rpos;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* The ratio between the device & the stream sample rate */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Float64 sampleRatio;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* An extra buffer used for render the audio data in the recording thread */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioBufferList bufferList;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* A ring buffer for transferring data from the recording thread */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync PIORINGBUFFER pBuf;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialization status tracker. Used when some of the device parameters
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * or the device itself is changed during the runtime. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync volatile uint32_t status;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync} caVoiceIn;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#ifdef CA_EXTENSIVE_LOGGING
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync# define CA_EXT_DEBUG_LOG(a) Log2(a)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#else
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync# define CA_EXT_DEBUG_LOG(a) do {} while(0)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * CoreAudio output section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* We need some forward declarations */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_run_out(HWVoiceOut *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_write(SWVoiceOut *sw, void *buf, int len);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_out(HWVoiceOut *hw, int cmd, ...);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void coreaudio_fini_out(HWVoiceOut *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int caInitOutput(HWVoiceOut *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void caReinitOutput(HWVoiceOut *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when the default output device was changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caPlaybackDefaultDeviceChanged(AudioHardwarePropertyID inPropertyID,
591df4288696bafbce6353c039bc58143f52cc87vboxsync void *inClientData)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync UInt32 uSize = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync UInt32 ad = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync bool fRun = false;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceOut *caVoice = (caVoiceOut *) inClientData;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync switch (inPropertyID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case kAudioHardwarePropertyDefaultOutputDevice:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* This listener is called on every change of the hardware
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * device. So check if the default device has really changed. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uSize = sizeof(caVoice->audioDeviceId);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &uSize,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &ad);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caVoice->audioDeviceId != ad)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
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 }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when some of the properties of an audio device has changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caPlaybackAudioDevicePropertyChanged(AudioDeviceID inDevice,
591df4288696bafbce6353c039bc58143f52cc87vboxsync UInt32 inChannel,
591df4288696bafbce6353c039bc58143f52cc87vboxsync Boolean isInput,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioDevicePropertyID inPropertyID,
591df4288696bafbce6353c039bc58143f52cc87vboxsync void *inClientData)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync switch (inPropertyID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#ifdef DEBUG
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case kAudioDeviceProcessorOverload:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log2(("CoreAudio: [Output] Processor overload detected!\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync default: break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback to feed audio output buffer */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caPlaybackCallback(void* inRefCon,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioUnitRenderActionFlags* ioActionFlags,
591df4288696bafbce6353c039bc58143f52cc87vboxsync const AudioTimeStamp* inTimeStamp,
591df4288696bafbce6353c039bc58143f52cc87vboxsync UInt32 inBusNumber,
591df4288696bafbce6353c039bc58143f52cc87vboxsync UInt32 inNumberFrames,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioBufferList* ioData)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t csAvail = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t cbToRead = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t csToRead = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t csReads = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync char *pcSrc = NULL;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caVoiceOut *caVoice = (caVoiceOut *) inRefCon;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
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);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Iterate as long as data is available */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync while(csReads < csAvail)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* How much is left? */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync csToRead = csAvail - csReads;
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 if (RT_UNLIKELY(cbToRead == 0))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync break;
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 csReads += csToRead;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
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 */
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return noErr;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int caInitOutput(HWVoiceOut *hw)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync OSStatus err = noErr;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uSize = 0; /* temporary size of properties */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uFlag = 0; /* for setting flags */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync CFStringRef name; /* for the temporary device name fetching */
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync char *pszName = NULL;
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync char *pszUID = NULL;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ComponentDescription cd; /* description for an audio component */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Component cp; /* an audio component */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AURenderCallbackStruct cb; /* holds the callback structure */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 cFrames; /* default frame count */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync UInt32 cSamples; /* samples count */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caVoiceOut *caVoice = (caVoiceOut *) hw;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_INIT);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caVoice->audioDeviceId == kAudioDeviceUnknown)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fetch the default audio output device currently in use */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uSize = sizeof(caVoice->audioDeviceId);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &uSize,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &caVoice->audioDeviceId);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Unable to find default output device (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Try to get the name of the output device and log it. It's not fatal if
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * it fails. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uSize = sizeof(CFStringRef);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioObjectPropertyName,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uSize,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &name);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_LIKELY(err == noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszName = caCFStringToCString(name);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync CFRelease(name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDevicePropertyDeviceUID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &uSize,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_LIKELY(err == noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszUID = caCFStringToCString(name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CFRelease(name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (pszName && pszUID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: Using output device: %s (UID: %s)\n", pszName, pszUID));
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync RTMemFree(pszUID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync RTMemFree(pszName);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync else
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Unable to get output device name (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Get the default frames buffer size, so that we can setup our internal
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * buffers. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync uSize = sizeof(cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync false,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferFrameSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &uSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to get frame buffer size of the audio device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the frame buffer size and honor any minimum/maximum restrictions on
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync the device. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = caSetFrameBufferSize(caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync false,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to set frame buffer size on the audio device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentType = kAudioUnitType_Output;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentSubType = kAudioUnitSubType_HALOutput;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentManufacturer = kAudioUnitManufacturer_Apple;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentFlags = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentFlagsMask = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Try to find the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cp = FindNextComponent(NULL, &cd);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(cp == 0))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to find HAL output component\n"));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Open the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = OpenAComponent(cp, &caVoice->audioUnit);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to open output component (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Switch the I/O mode for output to on. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uFlag = 1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioOutputUnitProperty_EnableIO,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Output,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uFlag,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(uFlag));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to set output I/O mode enabled (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the default audio output device as the device for the new AudioUnit. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioOutputUnitProperty_CurrentDevice,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Output,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(caVoice->audioDeviceId));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to set current device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
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 * data. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cb.inputProc = caPlaybackCallback;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cb.inputProcRefCon = caVoice;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitProperty_SetRenderCallback,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Input,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &cb,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(cb));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to set callback (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
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/* 0,*/
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));*/
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Fetch the current stream format of the device. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uSize = sizeof(caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitGetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitProperty_StreamFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Input,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &caVoice->deviceFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uSize);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create an AudioStreamBasicDescription based on the audio settings of
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * VirtualBox. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caPCMInfoToAudioStreamBasicDescription(&caVoice->hw.info, &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#if DEBUG
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] device", &caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] output", &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#endif /* DEBUG */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Set the device format description for the stream. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitProperty_StreamFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Input,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &caVoice->streamFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(caVoice->streamFormat));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to set stream format (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync uSize = sizeof(caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitGetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitProperty_StreamFormat,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Input,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->deviceFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uSize);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
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 err = AudioUnitSetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitProperty_MaximumFramesPerSlice,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Global,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(cFrames));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Finally initialize the new AudioUnit. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitInitialize(caVoice->audioUnit);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to initialize the AudioUnit (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
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 uSize = sizeof(cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitGetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitProperty_MaximumFramesPerSlice,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Global,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &uSize);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Output] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create the internal ring buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync cSamples = cFrames * caVoice->streamFormat.mChannelsPerFrame;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferCreate(&caVoice->pBuf, cSamples << hw->info.shift);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!RT_VALID_PTR(caVoice->pBuf))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Output] Failed to create internal ring buffer\n"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioUnitUninitialize(caVoice->audioUnit);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if ( hw->samples != 0
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync && hw->samples != (int32_t)cSamples)
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
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#ifdef DEBUG
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync false,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDeviceProcessorOverload,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caPlaybackAudioDevicePropertyChanged,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to add the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_INIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Output] Frame count: %RU32\n", cFrames));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return 0;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void caReinitOutput(HWVoiceOut *hw)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caVoiceOut *caVoice = (caVoiceOut *) hw;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync coreaudio_fini_out(&caVoice->hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caInitOutput(&caVoice->hw);
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync coreaudio_ctl_out(&caVoice->hw, VOICE_ENABLE);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_run_out(HWVoiceOut *hw)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t csAvail = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t cbToWrite = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t csToWrite = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t csWritten = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync char *pcDst = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync st_sample_t *psSrc = NULL;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceOut *caVoice = (caVoiceOut *) hw;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Check if the audio device should be reinitialized. If so do it. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) == CA_STATUS_REINIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caReinitOutput(&caVoice->hw);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
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 return audio_pcm_hw_get_live_out(hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Make sure the device is running */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync coreaudio_ctl_out(&caVoice->hw, VOICE_ENABLE);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much space is available in the ring buffer */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csAvail = IORingBufferFree(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
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
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << hw->info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Iterate as long as data is available */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync while (csWritten < csAvail)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
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 csToWrite = cbToWrite >> hw->info.shift;
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 if (RT_UNLIKELY(cbToWrite == 0))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Copy the data from our mix buffer to the ring buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync psSrc = hw->mix_buf + hw->rpos;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->clip((uint8_t*)pcDst, psSrc, csToWrite);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Release the ring buffer, so the read thread could start reading this data. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferReleaseWriteBlock(caVoice->pBuf, cbToWrite);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->rpos = (hw->rpos + csToWrite) % hw->samples;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much have we written so far. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csWritten += csToWrite;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Output] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << hw->info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Return the count of samples we have processed. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return csWritten;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_write(SWVoiceOut *sw, void *buf, int len)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return audio_pcm_sw_write (sw, buf, len);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_out(HWVoiceOut *hw, int cmd, ...)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t status;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceOut *caVoice = (caVoiceOut *) hw;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync status = ASMAtomicReadU32(&caVoice->status);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!( status == CA_STATUS_INIT
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync || status == CA_STATUS_REINIT))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync switch (cmd)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case VOICE_ENABLE:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only start the device if it is actually stopped */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!caIsRunning(caVoice->audioDeviceId))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioUnitReset(caVoice->audioUnit,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioUnitScope_Input,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferReset(caVoice->pBuf);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioOutputUnitStart(caVoice->audioUnit);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to start playback (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case VOICE_DISABLE:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only stop the device if it is actually running */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caIsRunning(caVoice->audioDeviceId))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioOutputUnitStop(caVoice->audioUnit);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to stop playback (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioUnitReset(caVoice->audioUnit,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioUnitScope_Input,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to reset AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void coreaudio_fini_out(HWVoiceOut *hw)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync int rc = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t status;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceOut *caVoice = (caVoiceOut *) hw;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync status = ASMAtomicReadU32(&caVoice->status);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!( status == CA_STATUS_INIT
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync || status == CA_STATUS_REINIT))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync rc = coreaudio_ctl_out(hw, VOICE_DISABLE);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_LIKELY(rc == 0))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#ifdef DEBUG
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync false,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDeviceProcessorOverload,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caPlaybackAudioDevicePropertyChanged);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to remove the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioUnitUninitialize(caVoice->audioUnit);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_LIKELY(err == noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = CloseComponent(caVoice->audioUnit);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_LIKELY(err == noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferDestroy(caVoice->pBuf);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioUnit = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioDeviceId = kAudioDeviceUnknown;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->pBuf = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync else
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to close the AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync else
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to uninitialize the AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync else
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to stop playback (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync int rc = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync bool fDeviceByUser = false; /* use we a device which was set by the user? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceOut *caVoice = (caVoiceOut *) hw;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioUnit = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioDeviceId = kAudioDeviceUnknown;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->samples = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialize the hardware info section with the audio settings */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync audio_pcm_init_info(&hw->info, as);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
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 if (conf.pszOutputDeviceUID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioDeviceId = caDeviceUIDtoID(conf.pszOutputDeviceUID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caVoice->audioDeviceId == kAudioDeviceUnknown)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Unable to find output device %s. Falling back to the default audio device. \n", conf.pszOutputDeviceUID));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync else
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync fDeviceByUser = true;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync rc = caInitOutput(hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(rc != 0))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return rc;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
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
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* When the devices isn't forced by the user, we want default device change
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * notifications. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!fDeviceByUser)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caPlaybackDefaultDeviceChanged,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Output] Failed to add the default device changed listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Output] HW samples: %d\n", hw->samples));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/*******************************************************************************
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync *
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * CoreAudio input section
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync *
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ******************************************************************************/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* We need some forward declarations */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_run_in(HWVoiceIn *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_read(SWVoiceIn *sw, void *buf, int size);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_in(HWVoiceIn *hw, int cmd, ...);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void coreaudio_fini_in(HWVoiceIn *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int caInitInput(HWVoiceIn *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void caReinitInput(HWVoiceIn *hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when the default input device was changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caRecordingDefaultDeviceChanged(AudioHardwarePropertyID inPropertyID,
591df4288696bafbce6353c039bc58143f52cc87vboxsync void *inClientData)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync UInt32 uSize = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync UInt32 ad = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync bool fRun = false;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceIn *caVoice = (caVoiceIn *) inClientData;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync switch (inPropertyID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case kAudioHardwarePropertyDefaultInputDevice:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* This listener is called on every change of the hardware
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * device. So check if the default device has really changed. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uSize = sizeof(caVoice->audioDeviceId);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &uSize,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &ad);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caVoice->audioDeviceId != ad)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
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 }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback for getting notified when some of the properties of an audio device has changed */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caRecordingAudioDevicePropertyChanged(AudioDeviceID inDevice,
591df4288696bafbce6353c039bc58143f52cc87vboxsync UInt32 inChannel,
591df4288696bafbce6353c039bc58143f52cc87vboxsync Boolean isInput,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioDevicePropertyID inPropertyID,
591df4288696bafbce6353c039bc58143f52cc87vboxsync void *inClientData)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceIn *caVoice = (caVoiceIn *) inClientData;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync switch (inPropertyID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#ifdef DEBUG
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case kAudioDeviceProcessorOverload:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log2(("CoreAudio: [Input] Processor overload detected!\n"));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case kAudioDevicePropertyNominalSampleRate:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
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 break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync default: break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback to convert audio input data from one format to another */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caConverterCallback(AudioConverterRef inAudioConverter,
591df4288696bafbce6353c039bc58143f52cc87vboxsync UInt32 *ioNumberDataPackets,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioBufferList *ioData,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioStreamPacketDescription **outDataPacketDescription,
591df4288696bafbce6353c039bc58143f52cc87vboxsync void *inUserData)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
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 UInt32 cSize = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceIn *caVoice = (caVoiceIn *) inUserData;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync const AudioBufferList *pBufferList = &caVoice->bufferList;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Log2(("converting .... ################ %RU32 %RU32 %RU32 %RU32 %RU32\n", *ioNumberDataPackets, bufferList->mBuffers[i].mNumberChannels, bufferList->mNumberBuffers, bufferList->mBuffers[i].mDataByteSize, ioData->mNumberBuffers));*/
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
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. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (*ioNumberDataPackets == 0)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioData->mBuffers[0].mDataByteSize = 0;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioData->mBuffers[0].mData = NULL;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return caConverterEOFDErr;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync else
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioData->mBuffers[0].mNumberChannels = pBufferList->mBuffers[0].mNumberChannels;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioData->mBuffers[0].mDataByteSize = cSize;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioData->mBuffers[0].mData = (char*)pBufferList->mBuffers[0].mData + caVoice->rpos;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->rpos += cSize;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Log2(("converting .... ################ %RU32 %RU32\n", size, caVoice->rpos));*/
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return noErr;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync}
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync/* Callback to feed audio input buffer */
591df4288696bafbce6353c039bc58143f52cc87vboxsyncstatic DECLCALLBACK(OSStatus) caRecordingCallback(void* inRefCon,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioUnitRenderActionFlags* ioActionFlags,
591df4288696bafbce6353c039bc58143f52cc87vboxsync const AudioTimeStamp* inTimeStamp,
591df4288696bafbce6353c039bc58143f52cc87vboxsync UInt32 inBusNumber,
591df4288696bafbce6353c039bc58143f52cc87vboxsync UInt32 inNumberFrames,
591df4288696bafbce6353c039bc58143f52cc87vboxsync AudioBufferList* ioData)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync OSStatus err = noErr;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t csAvail = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t csToWrite = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t cbToWrite = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uint32_t csWritten = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync char *pcDst = NULL;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioBufferList tmpList;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync UInt32 ioOutputDataPacketSize = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caVoiceIn *caVoice = (caVoiceIn *) inRefCon;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* If nothing is pending return immediately. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (inNumberFrames == 0)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return noErr;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Are we using an converter? */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_VALID_PTR(caVoice->converter))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
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
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitRender(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioActionFlags,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync inTimeStamp,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync inBusNumber,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync inNumberFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->bufferList);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if(RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Log(("CoreAudio: [Input] Failed to render audio data (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync RTMemFree(caVoice->bufferList.mBuffers[0].mData);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return err;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
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));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
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.mNumberBuffers = 1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync tmpList.mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the read position to zero. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->rpos = 0;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Iterate as long as data is available */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync while(csWritten < csAvail)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much is left? */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csToWrite = csAvail - csWritten;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cbToWrite = csToWrite << caVoice->hw.info.shift;
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? */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csToWrite = cbToWrite >> caVoice->hw.info.shift;
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 if (RT_UNLIKELY(cbToWrite == 0))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync break;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Now set how much space is available for output */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioOutputDataPacketSize = cbToWrite / caVoice->streamFormat.mBytesPerPacket;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set our ring buffer as target. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync tmpList.mBuffers[0].mDataByteSize = cbToWrite;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync tmpList.mBuffers[0].mData = pcDst;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioConverterReset(caVoice->converter);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioConverterFillComplexBuffer(caVoice->converter,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caConverterCallback,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &ioOutputDataPacketSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &tmpList,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync NULL);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if( RT_UNLIKELY(err != noErr)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync && err != caConverterEOFDErr)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
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 break;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
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 csToWrite = cbToWrite >> caVoice->hw.info.shift;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Release the ring buffer, so the main thread could start reading this data. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync IORingBufferReleaseWriteBlock(caVoice->pBuf, cbToWrite);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csWritten += csToWrite;
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 if (err == caConverterEOFDErr)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync break;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Cleanup */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync RTMemFree(caVoice->bufferList.mBuffers[0].mData);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync else
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
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
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitRender(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync ioActionFlags,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync inTimeStamp,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync inBusNumber,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync inNumberFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->bufferList);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if(RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync Log(("CoreAudio: [Input] Failed to render audio data (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync RTMemFree(caVoice->bufferList.mBuffers[0].mData);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return err;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
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);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Iterate as long as data is available */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync while(csWritten < csAvail)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* How much is left? */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csToWrite = csAvail - csWritten;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cbToWrite = csToWrite << caVoice->hw.info.shift;
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? */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync csToWrite = cbToWrite >> caVoice->hw.info.shift;
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 if (RT_UNLIKELY(cbToWrite == 0))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync break;
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 csWritten += csToWrite;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Cleanup */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync RTMemFree(caVoice->bufferList.mBuffers[0].mData);
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return err;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync}
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int caInitInput(HWVoiceIn *hw)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync OSStatus err = noErr;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync int rc = -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uSize = 0; /* temporary size of properties */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 uFlag = 0; /* for setting flags */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync CFStringRef name; /* for the temporary device name fetching */
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync char *pszName = NULL;
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync char *pszUID = NULL;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ComponentDescription cd; /* description for an audio component */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Component cp; /* an audio component */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AURenderCallbackStruct cb; /* holds the callback structure */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync UInt32 cFrames; /* default frame count */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync UInt32 cSamples; /* samples count */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caVoiceIn *caVoice = (caVoiceIn *) hw;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_INIT);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caVoice->audioDeviceId == kAudioDeviceUnknown)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Fetch the default audio output device currently in use */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uSize = sizeof(caVoice->audioDeviceId);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &uSize,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &caVoice->audioDeviceId);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Unable to find default input device (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Try to get the name of the input device and log it. It's not fatal if
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * it fails. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uSize = sizeof(CFStringRef);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioObjectPropertyName,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uSize,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &name);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_LIKELY(err == noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszName = caCFStringToCString(name);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync CFRelease(name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDevicePropertyDeviceUID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &uSize,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync &name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_LIKELY(err == noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync pszUID = caCFStringToCString(name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CFRelease(name);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (pszName && pszUID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: Using input device: %s (UID: %s)\n", pszName, pszUID));
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync if (pszUID)
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync RTMemFree(pszUID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
261eba35acbf58a83c47c84b4071bf4b3f38afd8vboxsync if (pszName)
b066837e12ae4b829e65ba18999966acb72e3db0vboxsync RTMemFree(pszName);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync else
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Unable to get input device name (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Get the default frames buffer size, so that we can setup our internal
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync * buffers. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync uSize = sizeof(cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioDeviceGetProperty(caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync true,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioDevicePropertyBufferFrameSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &uSize,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to get frame buffer size of the audio device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the frame buffer size and honor any minimum/maximum restrictions on
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync the device. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = caSetFrameBufferSize(caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync true,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync cFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set frame buffer size on the audio device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentType = kAudioUnitType_Output;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentSubType = kAudioUnitSubType_HALOutput;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentManufacturer = kAudioUnitManufacturer_Apple;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentFlags = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cd.componentFlagsMask = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Try to find the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cp = FindNextComponent(NULL, &cd);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(cp == 0))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to find HAL output component\n"));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Open the default HAL output component. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = OpenAComponent(cp, &caVoice->audioUnit);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to open output component (%RI32)\n", err));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Switch the I/O mode for input to on. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uFlag = 1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioOutputUnitProperty_EnableIO,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Input,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 1,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uFlag,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(uFlag));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to set input I/O mode enabled (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Switch the I/O mode for output to off. This is important, as this is a
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * pure input stream. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uFlag = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioOutputUnitProperty_EnableIO,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Output,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uFlag,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(uFlag));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to set output I/O mode disabled (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the default audio input device as the device for the new AudioUnit. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioOutputUnitProperty_CurrentDevice,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Global,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->audioDeviceId,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(caVoice->audioDeviceId));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set current device (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
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 * */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cb.inputProc = caRecordingCallback;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync cb.inputProcRefCon = caVoice;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioOutputUnitProperty_SetInputCallback,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Global,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 0,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &cb,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(cb));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to set callback (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Fetch the current stream format of the device. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync uSize = sizeof(caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitGetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitProperty_StreamFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Input,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 1,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &caVoice->deviceFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uSize);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create an AudioStreamBasicDescription based on the audio settings of
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * VirtualBox. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caPCMInfoToAudioStreamBasicDescription(&caVoice->hw.info, &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#if DEBUG
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] device", &caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] input", &caVoice->streamFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync#endif /* DEBUG */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
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)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioConverterNew(&caVoice->deviceFormat,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->streamFormat,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->converter);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to create the audio converter (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (caVoice->deviceFormat.mChannelsPerFrame == 1 &&
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->streamFormat.mChannelsPerFrame == 2)
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
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 cases. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioConverterSetProperty(caVoice->converter,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioConverterChannelMap,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(channelMap),
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync channelMap);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to add a channel mapper to the audio converter (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
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
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync Log(("CoreAudio: [Input] Converter in use\n"));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Set the new format description for the stream. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitProperty_StreamFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Output,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 1,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &caVoice->deviceFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(caVoice->deviceFormat));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitProperty_StreamFormat,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Input,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 1,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->deviceFormat,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(caVoice->deviceFormat));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync else
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Set the new format description for the stream. */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitSetProperty(caVoice->audioUnit,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitProperty_StreamFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync kAudioUnitScope_Output,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync 1,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &caVoice->streamFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync sizeof(caVoice->streamFormat));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
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 err = AudioUnitSetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitProperty_MaximumFramesPerSlice,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Global,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 1,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync sizeof(cFrames));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_UNLIKELY(err != noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to set maximum frame buffer size on the AudioUnit (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync return -1;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Finally initialize the new AudioUnit. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitInitialize(caVoice->audioUnit);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to initialize the AudioUnit (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync uSize = sizeof(caVoice->deviceFormat);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitGetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitProperty_StreamFormat,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Output,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 1,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &caVoice->deviceFormat,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync &uSize);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
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 uSize = sizeof(cFrames);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = AudioUnitGetProperty(caVoice->audioUnit,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitProperty_MaximumFramesPerSlice,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync kAudioUnitScope_Global,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync 0,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &cFrames,
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync &uSize);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_UNLIKELY(err != noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to get maximum frame buffer size from the AudioUnit (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Calculate the ratio between the device and the stream sample rate. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->sampleRatio = caVoice->streamFormat.mSampleRate / caVoice->deviceFormat.mSampleRate;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Set to zero first */
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caVoice->pBuf = NULL;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync /* Create the AudioBufferList structure with one buffer. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mNumberBuffers = 1;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync /* Initialize the buffer to nothing. */
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mDataByteSize = 0;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->bufferList.mBuffers[0].mData = NULL;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync
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 cSamples = RT_MAX(cFrames,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync (cFrames * caVoice->deviceFormat.mBytesPerFrame * caVoice->sampleRatio) / caVoice->streamFormat.mBytesPerFrame)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * caVoice->streamFormat.mChannelsPerFrame;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if ( hw->samples != 0
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync && hw->samples != (int32_t)cSamples)
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);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_VALID_PTR(caVoice->pBuf))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync rc = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync else
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to create internal ring buffer\n"));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (rc != 0)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (caVoice->pBuf)
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync IORingBufferDestroy(caVoice->pBuf);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync AudioUnitUninitialize(caVoice->audioUnit);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#ifdef DEBUG
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync true,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDeviceProcessorOverload,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caRecordingAudioDevicePropertyChanged,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to add the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceAddPropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync true,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDevicePropertyNominalSampleRate,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caRecordingAudioDevicePropertyChanged,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to add the sample rate changed listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_INIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Input] Frame count: %RU32\n", cFrames));
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync return 0;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic void caReinitInput(HWVoiceIn *hw)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceIn *caVoice = (caVoiceIn *) hw;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync coreaudio_fini_in(&caVoice->hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caInitInput(&caVoice->hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync coreaudio_ctl_in(&caVoice->hw, VOICE_ENABLE);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_run_in(HWVoiceIn *hw)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t csAvail = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t cbToRead = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t csToRead = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t csReads = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync char *pcSrc;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync st_sample_t *psDst;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceIn *caVoice = (caVoiceIn *) hw;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Check if the audio device should be reinitialized. If so do it. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) == CA_STATUS_REINIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caReinitInput(&caVoice->hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (ASMAtomicReadU32(&caVoice->status) != CA_STATUS_INIT)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
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
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Iterate as long as data is available */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync while (csReads < csAvail)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
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 cbToRead = csToRead << hw->info.shift;
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 csToRead = cbToRead >> hw->info.shift;
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 if (cbToRead == 0)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Copy the data from our ring buffer to the mix buffer. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync psDst = hw->conv_buf + hw->wpos;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->conv(psDst, pcSrc, csToRead, &nominal_volume);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Release the read buffer, so it could be used for new data. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferReleaseReadBlock(caVoice->pBuf, cbToRead);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->wpos = (hw->wpos + csToRead) % hw->samples;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* How much have we reads so far. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync csReads += csToRead;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync CA_EXT_DEBUG_LOG(("CoreAudio: [Input] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return csReads;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_read(SWVoiceIn *sw, void *buf, int size)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return audio_pcm_sw_read (sw, buf, size);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_ctl_in(HWVoiceIn *hw, int cmd, ...)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t status;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceIn *caVoice = (caVoiceIn *) hw;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync status = ASMAtomicReadU32(&caVoice->status);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!( status == CA_STATUS_INIT
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync || status == CA_STATUS_REINIT))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync switch (cmd)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case VOICE_ENABLE:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only start the device if it is actually stopped */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!caIsRunning(caVoice->audioDeviceId))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferReset(caVoice->pBuf);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioOutputUnitStart(caVoice->audioUnit);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to start recording (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync case VOICE_DISABLE:
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Only stop the device if it is actually running */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caIsRunning(caVoice->audioDeviceId))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioOutputUnitStop(caVoice->audioUnit);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to stop recording (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioUnitReset(caVoice->audioUnit,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioUnitScope_Input,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to reset AudioUnit (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync break;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void coreaudio_fini_in(HWVoiceIn *hw)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync int rc = 0;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync uint32_t status;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync caVoiceIn *caVoice = (caVoiceIn *) hw;
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync status = ASMAtomicReadU32(&caVoice->status);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!( status == CA_STATUS_INIT
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync || status == CA_STATUS_REINIT))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync rc = coreaudio_ctl_in(hw, VOICE_DISABLE);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_LIKELY(rc == 0))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_IN_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#ifdef DEBUG
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync true,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDeviceProcessorOverload,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caRecordingAudioDevicePropertyChanged);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to remove the processor overload listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync#endif /* DEBUG */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioDeviceRemovePropertyListener(caVoice->audioDeviceId,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync 0,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync true,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync kAudioDevicePropertyNominalSampleRate,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caRecordingAudioDevicePropertyChanged);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to remove the sample rate changed listener (%RI32)\n", err));
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (caVoice->converter)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync AudioConverterDispose(caVoice->converter);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->converter = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync err = AudioUnitUninitialize(caVoice->audioUnit);
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync if (RT_LIKELY(err == noErr))
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync {
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync err = CloseComponent(caVoice->audioUnit);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync if (RT_LIKELY(err == noErr))
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync IORingBufferDestroy(caVoice->pBuf);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->audioUnit = NULL;
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync caVoice->audioDeviceId = kAudioDeviceUnknown;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->pBuf = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->sampleRatio = 1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->rpos = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync }
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync else
96f1796553fc6784e763caad6545a80ec95d3aacvboxsync LogRel(("CoreAudio: [Input] Failed to close the AudioUnit (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync else
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to uninitialize the AudioUnit (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync }
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync else
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync LogRel(("CoreAudio: [Input] Failed to stop recording (%RI32)\n", err));
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsyncstatic int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync{
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync OSStatus err = noErr;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync int rc = -1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync bool fDeviceByUser = false;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoiceIn *caVoice = (caVoiceIn *) hw;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync ASMAtomicXchgU32(&caVoice->status, CA_STATUS_UNINIT);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioUnit = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioDeviceId = kAudioDeviceUnknown;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->converter = NULL;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->sampleRatio = 1;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->rpos = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync hw->samples = 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Initialize the hardware info section with the audio settings */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync audio_pcm_init_info(&hw->info, as);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Try to find the audio device set by the user */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (conf.pszInputDeviceUID)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice->audioDeviceId = caDeviceUIDtoID(conf.pszInputDeviceUID);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (caVoice->audioDeviceId == kAudioDeviceUnknown)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Unable to find input device %s. Falling back to the default audio device. \n", conf.pszInputDeviceUID));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync else
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync fDeviceByUser = true;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync rc = caInitInput(hw);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(rc != 0))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return rc;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
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
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* When the devices isn't forced by the user, we want default device change
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync * notifications. */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (!fDeviceByUser)
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync {
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultInputDevice,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caRecordingDefaultDeviceChanged,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync caVoice);
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync /* Not Fatal */
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync if (RT_UNLIKELY(err != noErr))
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync LogRel(("CoreAudio: [Input] Failed to add the default device changed listener (%RI32)\n", err));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync }
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync Log(("CoreAudio: [Input] HW samples: %d\n", hw->samples));
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync return 0;
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync}
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync/*******************************************************************************
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync * CoreAudio global section
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync *
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync ******************************************************************************/
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void *coreaudio_audio_init(void)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync return &conf;
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic void coreaudio_audio_fini(void *opaque)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync NOREF(opaque);
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic struct audio_option coreaudio_options[] =
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
4a7922a63c8704e7d5cb5af1264745117d037d1dvboxsync {"OutputDeviceUID", AUD_OPT_STR, &conf.pszOutputDeviceUID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync "UID of the output device to use", NULL, 0},
4a7922a63c8704e7d5cb5af1264745117d037d1dvboxsync {"InputDeviceUID", AUD_OPT_STR, &conf.pszInputDeviceUID,
ba5ef640ebdf159e95a5bda2b6f069eba163f2c3vboxsync "UID of the input device to use", NULL, 0},
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync {NULL, 0, NULL, NULL, NULL, 0}
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync};
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstatic struct audio_pcm_ops coreaudio_pcm_ops =
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync coreaudio_init_out,
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync coreaudio_fini_out,
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync coreaudio_run_out,
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync coreaudio_write,
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync coreaudio_ctl_out,
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync coreaudio_init_in,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync coreaudio_fini_in,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync coreaudio_run_in,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync coreaudio_read,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync coreaudio_ctl_in
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync};
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsyncstruct audio_driver coreaudio_audio_driver =
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync{
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(name =) "coreaudio",
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(descr =)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync "CoreAudio http://developer.apple.com/audio/coreaudio.html",
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(options =) coreaudio_options,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(init =) coreaudio_audio_init,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(fini =) coreaudio_audio_fini,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(pcm_ops =) &coreaudio_pcm_ops,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(can_be_default =) 1,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(max_voices_out =) 1,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(max_voices_in =) 1,
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(voice_size_out =) sizeof(caVoiceOut),
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync INIT_FIELD(voice_size_in =) sizeof(caVoiceIn)
7d316d97708975eb00420c5b65a2c8b716f07a77vboxsync};
fa21f74d4e464d01e3cfe51b5c41573765e1781dvboxsync