VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp@ 55482

Last change on this file since 55482 was 55035, checked in by vboxsync, 10 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 79.3 KB
Line 
1/* $Id: DrvHostCoreAudio.cpp 55035 2015-03-31 13:42:20Z vboxsync $ */
2/** @file
3 * VBox audio devices: Mac OS X CoreAudio audio driver.
4 */
5
6/*
7 * Copyright (C) 2010-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include "DrvAudio.h"
18#include "AudioMixBuffer.h"
19
20#include <iprt/asm.h>
21#include <iprt/cdefs.h>
22#include <iprt/circbuf.h>
23#include <iprt/mem.h>
24
25#include "vl_vbox.h"
26#include <iprt/uuid.h>
27
28#include <CoreAudio/CoreAudio.h>
29#include <CoreServices/CoreServices.h>
30#include <AudioUnit/AudioUnit.h>
31#include <AudioToolbox/AudioConverter.h>
32
33#ifdef LOG_GROUP
34# undef LOG_GROUP
35#endif
36#define LOG_GROUP LOG_GROUP_DEV_AUDIO
37#include <VBox/log.h>
38
39/* TODO:
40 * - Maybe make sure the threads are immediately stopped if playing/recording stops.
41 */
42
43/*
44 * Most of this is based on:
45 * http://developer.apple.com/mac/library/technotes/tn2004/tn2097.html
46 * http://developer.apple.com/mac/library/technotes/tn2002/tn2091.html
47 * http://developer.apple.com/mac/library/qa/qa2007/qa1533.html
48 * http://developer.apple.com/mac/library/qa/qa2001/qa1317.html
49 * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
50 */
51
52/**
53 * Host Coreaudio driver instance data.
54 * @implements PDMIAUDIOCONNECTOR
55 */
56typedef struct DRVHOSTCOREAUDIO
57{
58 /** Pointer to the driver instance structure. */
59 PPDMDRVINS pDrvIns;
60 /** Pointer to host audio interface. */
61 PDMIHOSTAUDIO IHostAudio;
62} DRVHOSTCOREAUDIO, *PDRVHOSTCOREAUDIO;
63
64/*******************************************************************************
65 *
66 * Helper function section
67 *
68 ******************************************************************************/
69
70#ifdef DEBUG
71static void drvHostCoreAudioPrintASBDesc(const char *pszDesc, const AudioStreamBasicDescription *pStreamDesc)
72{
73 char pszSampleRate[32];
74 Log(("%s AudioStreamBasicDescription:\n", pszDesc));
75 LogFlowFunc(("Format ID: %RU32 (%c%c%c%c)\n", pStreamDesc->mFormatID,
76 RT_BYTE4(pStreamDesc->mFormatID), RT_BYTE3(pStreamDesc->mFormatID),
77 RT_BYTE2(pStreamDesc->mFormatID), RT_BYTE1(pStreamDesc->mFormatID)));
78 LogFlowFunc(("Flags: %RU32", pStreamDesc->mFormatFlags));
79 if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsFloat)
80 Log((" Float"));
81 if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsBigEndian)
82 Log((" BigEndian"));
83 if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsSignedInteger)
84 Log((" SignedInteger"));
85 if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsPacked)
86 Log((" Packed"));
87 if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
88 Log((" AlignedHigh"));
89 if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
90 Log((" NonInterleaved"));
91 if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonMixable)
92 Log((" NonMixable"));
93 if (pStreamDesc->mFormatFlags & kAudioFormatFlagsAreAllClear)
94 Log((" AllClear"));
95 Log(("\n"));
96 snprintf(pszSampleRate, 32, "%.2f", (float)pStreamDesc->mSampleRate); /** @todo r=andy Use RTStrPrint*. */
97 LogFlowFunc(("SampleRate : %s\n", pszSampleRate));
98 LogFlowFunc(("ChannelsPerFrame: %RU32\n", pStreamDesc->mChannelsPerFrame));
99 LogFlowFunc(("FramesPerPacket : %RU32\n", pStreamDesc->mFramesPerPacket));
100 LogFlowFunc(("BitsPerChannel : %RU32\n", pStreamDesc->mBitsPerChannel));
101 LogFlowFunc(("BytesPerFrame : %RU32\n", pStreamDesc->mBytesPerFrame));
102 LogFlowFunc(("BytesPerPacket : %RU32\n", pStreamDesc->mBytesPerPacket));
103}
104#endif /* DEBUG */
105
106static void drvHostCoreAudioPCMInfoToASBDesc(PDMPCMPROPS *pPcmProperties, AudioStreamBasicDescription *pStreamDesc)
107{
108 pStreamDesc->mFormatID = kAudioFormatLinearPCM;
109 pStreamDesc->mFormatFlags = kAudioFormatFlagIsPacked;
110 pStreamDesc->mFramesPerPacket = 1;
111 pStreamDesc->mSampleRate = (Float64)pPcmProperties->uHz;
112 pStreamDesc->mChannelsPerFrame = pPcmProperties->cChannels;
113 pStreamDesc->mBitsPerChannel = pPcmProperties->cBits;
114 if (pPcmProperties->fSigned)
115 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
116 pStreamDesc->mBytesPerFrame = pStreamDesc->mChannelsPerFrame * (pStreamDesc->mBitsPerChannel / 8);
117 pStreamDesc->mBytesPerPacket = pStreamDesc->mFramesPerPacket * pStreamDesc->mBytesPerFrame;
118}
119
120static OSStatus drvHostCoreAudioSetFrameBufferSize(AudioDeviceID deviceID, bool fInput, UInt32 cReqSize, UInt32 *pcActSize)
121{
122 AudioObjectPropertyScope propScope = fInput
123 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
124 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyBufferFrameSize, propScope,
125 kAudioObjectPropertyElementMaster };
126
127 /* First try to set the new frame buffer size. */
128 OSStatus err = AudioObjectSetPropertyData(deviceID, &propAdr, NULL, 0, sizeof(cReqSize), &cReqSize);
129
130 /* Check if it really was set. */
131 UInt32 cSize = sizeof(*pcActSize);
132 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
133 if (RT_UNLIKELY(err != noErr))
134 return err;
135
136 /* If both sizes are the same, we are done. */
137 if (cReqSize == *pcActSize)
138 return noErr;
139
140 /* If not we have to check the limits of the device. First get the size of
141 the buffer size range property. */
142 propAdr.mSelector = kAudioDevicePropertyBufferSizeRange;
143 err = AudioObjectGetPropertyDataSize(deviceID, &propAdr, 0, NULL, &cSize);
144 if (RT_UNLIKELY(err != noErr))
145 return err;
146
147 Assert(cSize);
148 AudioValueRange *pRange = (AudioValueRange *)RTMemAllocZ(cSize);
149 if (pRange)
150 {
151 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pRange);
152 if (err == noErr)
153 {
154 Float64 cMin = -1;
155 Float64 cMax = -1;
156 for (size_t a = 0; a < cSize / sizeof(AudioValueRange); a++)
157 {
158 /* Search for the absolute minimum. */
159 if ( pRange[a].mMinimum < cMin
160 || cMin == -1)
161 cMin = pRange[a].mMinimum;
162
163 /* Search for the best maximum which isn't bigger than cReqSize. */
164 if (pRange[a].mMaximum < cReqSize)
165 {
166 if (pRange[a].mMaximum > cMax)
167 cMax = pRange[a].mMaximum;
168 }
169 }
170 if (cMax == -1)
171 cMax = cMin;
172 cReqSize = cMax;
173
174 /* First try to set the new frame buffer size. */
175 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
176 err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);
177 if (err == noErr)
178 {
179 /* Check if it really was set. */
180 cSize = sizeof(*pcActSize);
181 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
182 }
183 }
184
185 RTMemFree(pRange);
186 }
187 else
188 err = notEnoughMemoryErr;
189
190 return err;
191}
192
193DECL_FORCE_INLINE(bool) drvHostCoreAudioIsRunning(AudioDeviceID deviceID)
194{
195 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsRunning, kAudioObjectPropertyScopeGlobal,
196 kAudioObjectPropertyElementMaster };
197 UInt32 uFlag = 0;
198 UInt32 uSize = sizeof(uFlag);
199 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uFlag);
200 if (err != kAudioHardwareNoError)
201 LogRel(("CoreAudio: Could not determine whether the device is running (%RI32)\n", err));
202
203 return (uFlag >= 1);
204}
205
206static int drvHostCoreAudioCFStringToCString(const CFStringRef pCFString, char **ppszString)
207{
208 CFIndex cLen = CFStringGetLength(pCFString) + 1;
209 char *pszResult = (char *)RTMemAllocZ(cLen * sizeof(char));
210 if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
211 {
212 RTMemFree(pszResult);
213 return VERR_NOT_FOUND;
214 }
215
216 *ppszString = pszResult;
217 return VINF_SUCCESS;
218}
219
220static AudioDeviceID drvHostCoreAudioDeviceUIDtoID(const char* pszUID)
221{
222 /* Create a CFString out of our CString. */
223 CFStringRef strUID = CFStringCreateWithCString(NULL, pszUID, kCFStringEncodingMacRoman);
224
225 /* Fill the translation structure. */
226 AudioDeviceID deviceID;
227
228 AudioValueTranslation translation;
229 translation.mInputData = &strUID;
230 translation.mInputDataSize = sizeof(CFStringRef);
231 translation.mOutputData = &deviceID;
232 translation.mOutputDataSize = sizeof(AudioDeviceID);
233
234 /* Fetch the translation from the UID to the device ID. */
235 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDeviceForUID, kAudioObjectPropertyScopeGlobal,
236 kAudioObjectPropertyElementMaster };
237
238 UInt32 uSize = sizeof(AudioValueTranslation);
239 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &translation);
240
241 /* Release the temporary CFString */
242 CFRelease(strUID);
243
244 if (RT_LIKELY(err == noErr))
245 return deviceID;
246
247 /* Return the unknown device on error. */
248 return kAudioDeviceUnknown;
249}
250
251/*******************************************************************************
252 *
253 * Global structures section
254 *
255 ******************************************************************************/
256
257/* Initialization status indicator used for the recreation of the AudioUnits. */
258#define CA_STATUS_UNINIT UINT32_C(0) /* The device is uninitialized */
259#define CA_STATUS_IN_INIT UINT32_C(1) /* The device is currently initializing */
260#define CA_STATUS_INIT UINT32_C(2) /* The device is initialized */
261#define CA_STATUS_IN_UNINIT UINT32_C(3) /* The device is currently uninitializing */
262#define CA_STATUS_REINIT UINT32_C(4) /* The device has to be reinitialized */
263
264/* Error code which indicates "End of data" */
265static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */
266
267typedef struct COREAUDIOSTREAMOUT
268{
269 /** Host stream out. */
270 PDMAUDIOHSTSTRMOUT streamOut;
271 /* Stream description which is default on the device */
272 AudioStreamBasicDescription deviceFormat;
273 /* Stream description which is selected for using by VBox */
274 AudioStreamBasicDescription streamFormat;
275 /* The audio device ID of the currently used device */
276 AudioDeviceID deviceID;
277 /* The AudioUnit used */
278 AudioUnit audioUnit;
279 /* A ring buffer for transferring data to the playback thread. */
280 PRTCIRCBUF pBuf;
281 /* Temporary buffer for copying over audio data into Core Audio. */
282 void *pvPCMBuf;
283 /** Size of the temporary buffer. */
284 size_t cbPCMBuf;
285 /* Initialization status tracker. Used when some of the device parameters
286 * or the device itself is changed during the runtime. */
287 volatile uint32_t status;
288 /** Flag whether the "default device changed" listener was registered. */
289 bool fDefDevChgListReg;
290} COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT;
291
292typedef struct COREAUDIOSTREAMIN
293{
294 /** Host stream in. */
295 PDMAUDIOHSTSTRMIN streamIn;
296 /* Stream description which is default on the device */
297 AudioStreamBasicDescription deviceFormat;
298 /* Stream description which is selected for using by VBox */
299 AudioStreamBasicDescription streamFormat;
300 /* The audio device ID of the currently used device */
301 AudioDeviceID deviceID;
302 /* The AudioUnit used */
303 AudioUnit audioUnit;
304 /* The audio converter if necessary */
305 AudioConverterRef converter;
306 /* A temporary position value used in the caConverterCallback function */
307 uint32_t rpos;
308 /* The ratio between the device & the stream sample rate */
309 Float64 sampleRatio;
310 /* An extra buffer used for render the audio data in the recording thread */
311 AudioBufferList bufferList;
312 /* A ring buffer for transferring data from the recording thread */
313 PRTCIRCBUF pBuf;
314 /* Initialization status tracker. Used when some of the device parameters
315 * or the device itself is changed during the runtime. */
316 volatile uint32_t status;
317 /** Flag whether the "default device changed" listener was registered. */
318 bool fDefDevChgListReg;
319} COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN;
320
321static int drvHostCoreAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn, PDMAUDIOSTREAMCMD enmStreamCmd);
322static int drvHostCoreAudioInitInput(PPDMAUDIOHSTSTRMIN pHstStrmIn, uint32_t *pcSamples);
323static int drvHostCoreAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn);
324static int drvHostCoreAudioReinitInput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn);
325
326static int drvHostCoreAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd);
327static int drvHostCoreAudioInitOutput(PPDMAUDIOHSTSTRMOUT pHstStrmOut, uint32_t *pcSamples);
328static int drvHostCoreAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
329static int drvHostCoreAudioReinitOutput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
330static OSStatus drvHostCoreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void *pvUser);
331static OSStatus drvHostCoreAudioPlaybackCallback(void *pvUser, AudioUnitRenderActionFlags *pActionFlags, const AudioTimeStamp *pAudioTS, UInt32 uBusID, UInt32 cFrames, AudioBufferList* pBufData);
332
333/* Callback for getting notified when the default input/output device has been changed. */
334static DECLCALLBACK(OSStatus) drvHostCoreAudioDefaultDeviceChanged(AudioObjectID propertyID,
335 UInt32 nAddresses,
336 const AudioObjectPropertyAddress properties[],
337 void *pvUser)
338{
339 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
340
341 OSStatus err = noErr;
342
343 switch (propertyID)
344 {
345 case kAudioHardwarePropertyDefaultInputDevice:
346 {
347 /* This listener is called on every change of the hardware
348 * device. So check if the default device has really changed. */
349 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice,
350 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
351
352 UInt32 uSize = sizeof(pStreamIn->deviceID);
353 UInt32 uResp;
354 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &uResp);
355
356 if (err == noErr)
357 {
358 if (pStreamIn->deviceID != uResp)
359 {
360 LogRel(("CoreAudio: Default input device has changed\n"));
361
362 /* We move the reinitialization to the next input event.
363 * This make sure this thread isn't blocked and the
364 * reinitialization is done when necessary only. */
365 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
366 }
367 }
368 break;
369 }
370
371 default:
372 break;
373 }
374
375 return noErr;
376}
377
378static int drvHostCoreAudioReinitInput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
379{
380 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
381 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
382
383 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
384
385 drvHostCoreAudioFiniIn(pInterface, &pStreamIn->streamIn);
386
387 drvHostCoreAudioInitInput(&pStreamIn->streamIn, NULL /* pcSamples */);
388 drvHostCoreAudioControlIn(pInterface, &pStreamIn->streamIn, PDMAUDIOSTREAMCMD_ENABLE);
389
390 return VINF_SUCCESS;
391}
392
393static int drvHostCoreAudioReinitOutput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
394{
395 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
396 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
397
398 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
399
400 drvHostCoreAudioFiniOut(pInterface, &pStreamOut->streamOut);
401
402 drvHostCoreAudioInitOutput(&pStreamOut->streamOut, NULL /* pcSamples */);
403 drvHostCoreAudioControlOut(pInterface, &pStreamOut->streamOut, PDMAUDIOSTREAMCMD_ENABLE);
404
405 return VINF_SUCCESS;
406}
407
408/* Callback for getting notified when some of the properties of an audio device has changed. */
409static DECLCALLBACK(OSStatus) drvHostCoreAudioRecordingAudioDevicePropertyChanged(AudioObjectID propertyID,
410 UInt32 cAdresses,
411 const AudioObjectPropertyAddress aProperties[],
412 void *pvUser)
413{
414 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
415
416 switch (propertyID)
417 {
418#ifdef DEBUG
419 case kAudioDeviceProcessorOverload:
420 {
421 LogFunc(("Processor overload detected!\n"));
422 break;
423 }
424#endif /* DEBUG */
425 case kAudioDevicePropertyNominalSampleRate:
426 {
427 LogRel(("CoreAudio: Recording sample rate changed\n"));
428
429 /* We move the reinitialization to the next input event.
430 * This make sure this thread isn't blocked and the
431 * reinitialization is done when necessary only. */
432 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
433 break;
434 }
435
436 default:
437 break;
438 }
439
440 return noErr;
441}
442
443/* Callback to convert audio input data from one format to another. */
444static DECLCALLBACK(OSStatus) drvHostCoreAudioConverterCallback(AudioConverterRef converterID,
445 UInt32 *pcPackets,
446 AudioBufferList *pBufData,
447 AudioStreamPacketDescription **ppPacketDesc,
448 void *pvUser)
449{
450 /** @todo Check incoming pointers. */
451
452 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
453
454 /** @todo Check converter ID? */
455
456 const AudioBufferList *pBufferList = &pStreamIn->bufferList;
457
458 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
459 return noErr;
460
461 /** @todo In principle we had to check here if the source is non interleaved, and if so,
462 * so go through all buffers not only the first one like now. */
463
464 /* Use the lower one of the packets to process & the available packets in the buffer. */
465 Assert(pBufferList->mBuffers[0].mDataByteSize >= pStreamIn->rpos);
466 UInt32 cSize = RT_MIN(*pcPackets * pStreamIn->deviceFormat.mBytesPerPacket,
467 pBufferList->mBuffers[0].mDataByteSize - pStreamIn->rpos);
468
469 /* Set the new size on output, so the caller know what we have processed. */
470 Assert(pStreamIn->deviceFormat.mBytesPerPacket);
471 *pcPackets = cSize / pStreamIn->deviceFormat.mBytesPerPacket;
472
473 OSStatus err;
474
475 /* If no data is available anymore we return with an error code. This error code will be returned
476 * from AudioConverterFillComplexBuffer. */
477 if (*pcPackets == 0)
478 {
479 pBufData->mBuffers[0].mDataByteSize = 0;
480 pBufData->mBuffers[0].mData = NULL;
481
482 err = caConverterEOFDErr;
483 }
484 else
485 {
486 pBufData->mBuffers[0].mNumberChannels = pBufferList->mBuffers[0].mNumberChannels;
487 pBufData->mBuffers[0].mDataByteSize = cSize;
488 pBufData->mBuffers[0].mData = (uint8_t *)pBufferList->mBuffers[0].mData + pStreamIn->rpos;
489
490 pStreamIn->rpos += cSize;
491
492 err = noErr;
493 }
494
495 return err;
496}
497
498/* Callback to feed audio input buffer. */
499static DECLCALLBACK(OSStatus) drvHostCoreAudioRecordingCallback(void *pvUser,
500 AudioUnitRenderActionFlags *pActionFlags,
501 const AudioTimeStamp *pAudioTS,
502 UInt32 uBusID,
503 UInt32 cFrames,
504 AudioBufferList *pBufData)
505{
506 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
507 PPDMAUDIOHSTSTRMIN pHstStrmIN = &pStreamIn->streamIn;
508
509 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
510 return noErr;
511
512 /* If nothing is pending return immediately. */
513 if (cFrames == 0)
514 return noErr;
515
516 OSStatus err = noErr;
517 int rc = VINF_SUCCESS;
518
519 do
520 {
521 /* Are we using a converter? */
522 if (pStreamIn->converter)
523 {
524 /* First, render the data as usual. */
525 pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->deviceFormat.mChannelsPerFrame;
526 pStreamIn->bufferList.mBuffers[0].mDataByteSize = pStreamIn->deviceFormat.mBytesPerFrame * cFrames;
527 AssertBreakStmt(pStreamIn->bufferList.mBuffers[0].mDataByteSize, rc = VERR_INVALID_PARAMETER);
528 pStreamIn->bufferList.mBuffers[0].mData = RTMemAlloc(pStreamIn->bufferList.mBuffers[0].mDataByteSize);
529 if (!pStreamIn->bufferList.mBuffers[0].mData)
530 {
531 rc = VERR_NO_MEMORY;
532 break;
533 }
534
535 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pStreamIn->bufferList);
536 if (err != noErr)
537 {
538 LogFlowFunc(("Failed rendering audio data (%RI32)\n", err));
539 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
540 break;
541 }
542
543 size_t cbAvail = RT_MIN(RTCircBufFree(pStreamIn->pBuf), pStreamIn->bufferList.mBuffers[0].mDataByteSize);
544
545 /* Initialize the temporary output buffer */
546 AudioBufferList tmpList;
547 tmpList.mNumberBuffers = 1;
548 tmpList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
549
550 /* Iterate as long as data is available. */
551 uint8_t *puDst = NULL;
552 while (cbAvail)
553 {
554 /* Try to acquire the necessary space from the ring buffer. */
555 size_t cbToWrite = 0;
556 RTCircBufAcquireWriteBlock(pStreamIn->pBuf, cbAvail, (void **)&puDst, &cbToWrite);
557 if (!cbToWrite)
558 break;
559
560 /* Now set how much space is available for output. */
561 Assert(pStreamIn->streamFormat.mBytesPerPacket);
562
563 UInt32 ioOutputDataPacketSize = cbToWrite / pStreamIn->streamFormat.mBytesPerPacket;
564
565 /* Set our ring buffer as target. */
566 tmpList.mBuffers[0].mDataByteSize = cbToWrite;
567 tmpList.mBuffers[0].mData = puDst;
568
569 AudioConverterReset(pStreamIn->converter);
570
571 err = AudioConverterFillComplexBuffer(pStreamIn->converter, drvHostCoreAudioConverterCallback, pStreamIn,
572 &ioOutputDataPacketSize, &tmpList, NULL);
573 if( err != noErr
574 && err != caConverterEOFDErr)
575 {
576 LogFlowFunc(("Failed to convert audio data (%RI32:%c%c%c%c)\n", err,
577 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
578 rc = VERR_IO_GEN_FAILURE;
579 break;
580 }
581
582 /* Check in any case what processed size is returned. It could be less than we expected. */
583 cbToWrite = ioOutputDataPacketSize * pStreamIn->streamFormat.mBytesPerPacket;
584
585 /* Release the ring buffer, so the main thread could start reading this data. */
586 RTCircBufReleaseWriteBlock(pStreamIn->pBuf, cbToWrite);
587
588 /* If the error is "End of Data" it means there is no data anymore
589 * which could be converted. So end here now. */
590 if (err == caConverterEOFDErr)
591 break;
592
593 Assert(cbAvail >= cbToWrite);
594 cbAvail -= cbToWrite;
595 }
596 }
597 else /* No converter being used. */
598 {
599 pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
600 pStreamIn->bufferList.mBuffers[0].mDataByteSize = pStreamIn->streamFormat.mBytesPerFrame * cFrames;
601 AssertBreakStmt(pStreamIn->bufferList.mBuffers[0].mDataByteSize, rc = VERR_INVALID_PARAMETER);
602 pStreamIn->bufferList.mBuffers[0].mData = RTMemAlloc(pStreamIn->bufferList.mBuffers[0].mDataByteSize);
603 if (!pStreamIn->bufferList.mBuffers[0].mData)
604 {
605 rc = VERR_NO_MEMORY;
606 break;
607 }
608
609 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pStreamIn->bufferList);
610 if (err != noErr)
611 {
612 LogFlowFunc(("Failed rendering audio data (%RI32)\n", err));
613 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
614 break;
615 }
616
617 size_t cbAvail = RT_MIN(RTCircBufFree(pStreamIn->pBuf), pStreamIn->bufferList.mBuffers[0].mDataByteSize);
618
619 /* Iterate as long as data is available. */
620 uint8_t *puDst = NULL;
621 uint32_t cbWrittenTotal = 0;
622 while(cbAvail)
623 {
624 /* Try to acquire the necessary space from the ring buffer. */
625 size_t cbToWrite = 0;
626 RTCircBufAcquireWriteBlock(pStreamIn->pBuf, cbAvail, (void **)&puDst, &cbToWrite);
627 if (!cbToWrite)
628 break;
629
630 /* Copy the data from the core audio buffer to the ring buffer. */
631 memcpy(puDst, (uint8_t *)pStreamIn->bufferList.mBuffers[0].mData + cbWrittenTotal, cbToWrite);
632
633 /* Release the ring buffer, so the main thread could start reading this data. */
634 RTCircBufReleaseWriteBlock(pStreamIn->pBuf, cbToWrite);
635
636 cbWrittenTotal += cbToWrite;
637
638 Assert(cbAvail >= cbToWrite);
639 cbAvail -= cbToWrite;
640 }
641 }
642
643 } while (0);
644
645 if (pStreamIn->bufferList.mBuffers[0].mData)
646 {
647 RTMemFree(pStreamIn->bufferList.mBuffers[0].mData);
648 pStreamIn->bufferList.mBuffers[0].mData = NULL;
649 }
650
651 return err;
652}
653
654/** @todo Eventually split up this function, as this already is huge! */
655static int drvHostCoreAudioInitInput(PPDMAUDIOHSTSTRMIN pHstStrmIn, uint32_t *pcSamples)
656{
657 OSStatus err = noErr;
658
659 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
660
661 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_INIT);
662
663 UInt32 uSize = 0;
664 if (pStreamIn->deviceID == kAudioDeviceUnknown)
665 {
666 /* Fetch the default audio input device currently in use. */
667 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice,
668 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
669 uSize = sizeof(pStreamIn->deviceID);
670 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &pStreamIn->deviceID);
671 if (err != noErr)
672 {
673 LogRel(("CoreAudio: Unable to determine default input device (%RI32)\n", err));
674 return VERR_NOT_FOUND;
675 }
676 }
677
678 /*
679 * Try to get the name of the input device and log it. It's not fatal if it fails.
680 */
681 CFStringRef strTemp;
682
683 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
684 kAudioObjectPropertyElementMaster };
685 uSize = sizeof(CFStringRef);
686 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
687 if (err == noErr)
688 {
689 char *pszDevName = NULL;
690 err = drvHostCoreAudioCFStringToCString(strTemp, &pszDevName);
691 if (err == noErr)
692 {
693 CFRelease(strTemp);
694
695 /* Get the device' UUID. */
696 propAdr.mSelector = kAudioDevicePropertyDeviceUID;
697 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
698 if (err == noErr)
699 {
700 char *pszUID = NULL;
701 err = drvHostCoreAudioCFStringToCString(strTemp, &pszUID);
702 if (err == noErr)
703 {
704 CFRelease(strTemp);
705 LogRel(("CoreAudio: Using input device: %s (UID: %s)\n", pszDevName, pszUID));
706
707 RTMemFree(pszUID);
708 }
709 }
710
711 RTMemFree(pszDevName);
712 }
713 }
714 else
715 LogRel(("CoreAudio: Unable to determine input device name (%RI32)\n", err));
716
717 /* Get the default frames buffer size, so that we can setup our internal buffers. */
718 UInt32 cFrames;
719 uSize = sizeof(cFrames);
720 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
721 propAdr.mScope = kAudioDevicePropertyScopeInput;
722 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
723 if (err != noErr)
724 {
725 LogRel(("CoreAudio: Failed to determine frame buffer size of the audio input device (%RI32)\n", err));
726 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
727 }
728
729 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
730 err = drvHostCoreAudioSetFrameBufferSize(pStreamIn->deviceID, true /* fInput */, cFrames, &cFrames);
731 if (err != noErr)
732 {
733 LogRel(("CoreAudio: Failed to set frame buffer size for the audio input device (%RI32)\n", err));
734 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
735 }
736
737 ComponentDescription cd;
738 RT_ZERO(cd);
739 cd.componentType = kAudioUnitType_Output;
740 cd.componentSubType = kAudioUnitSubType_HALOutput;
741 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
742
743 /* Try to find the default HAL output component. */
744 Component cp = FindNextComponent(NULL, &cd);
745 if (cp == 0)
746 {
747 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
748 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
749 }
750
751 /* Open the default HAL output component. */
752 err = OpenAComponent(cp, &pStreamIn->audioUnit);
753 if (err != noErr)
754 {
755 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
756 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
757 }
758
759 /* Switch the I/O mode for input to on. */
760 UInt32 uFlag = 1;
761 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
762 1, &uFlag, sizeof(uFlag));
763 if (err != noErr)
764 {
765 LogRel(("CoreAudio: Failed to disable input I/O mode for input stream (%RI32)\n", err));
766 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
767 }
768
769 /* Switch the I/O mode for input to off. This is important, as this is a pure input stream. */
770 uFlag = 0;
771 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
772 0, &uFlag, sizeof(uFlag));
773 if (err != noErr)
774 {
775 LogRel(("CoreAudio: Failed to disable output I/O mode for input stream (%RI32)\n", err));
776 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
777 }
778
779 /* Set the default audio input device as the device for the new AudioUnit. */
780 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
781 0, &pStreamIn->deviceID, sizeof(pStreamIn->deviceID));
782 if (err != noErr)
783 {
784 LogRel(("CoreAudio: Failed to set current device (%RI32)\n", err));
785 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
786 }
787
788 /*
789 * CoreAudio will inform us on a second thread for new incoming audio data.
790 * Therefor register a callback function which will process the new data.
791 */
792 AURenderCallbackStruct cb;
793 RT_ZERO(cb);
794 cb.inputProc = drvHostCoreAudioRecordingCallback;
795 cb.inputProcRefCon = pStreamIn;
796
797 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,
798 0, &cb, sizeof(cb));
799 if (err != noErr)
800 {
801 LogRel(("CoreAudio: Failed to register input callback (%RI32)\n", err));
802 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
803 }
804
805 /* Fetch the current stream format of the device. */
806 uSize = sizeof(pStreamIn->deviceFormat);
807 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
808 1, &pStreamIn->deviceFormat, &uSize);
809 if (err != noErr)
810 {
811 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
812 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
813 }
814
815 /* Create an AudioStreamBasicDescription based on our required audio settings. */
816 drvHostCoreAudioPCMInfoToASBDesc(&pStreamIn->streamIn.Props, &pStreamIn->streamFormat);
817
818#ifdef DEBUG
819 drvHostCoreAudioPrintASBDesc("CoreAudio: Input device", &pStreamIn->deviceFormat);
820 drvHostCoreAudioPrintASBDesc("CoreAudio: Input stream", &pStreamIn->streamFormat);
821#endif /* DEBUG */
822
823 /* If the frequency of the device is different from the requested one we
824 * need a converter. The same count if the number of channels is different. */
825 if ( pStreamIn->deviceFormat.mSampleRate != pStreamIn->streamFormat.mSampleRate
826 || pStreamIn->deviceFormat.mChannelsPerFrame != pStreamIn->streamFormat.mChannelsPerFrame)
827 {
828 err = AudioConverterNew(&pStreamIn->deviceFormat, &pStreamIn->streamFormat, &pStreamIn->converter);
829 if (RT_UNLIKELY(err != noErr))
830 {
831 LogRel(("CoreAudio: Failed to create the audio converte(%RI32). Input Format=%d, Output Foramt=%d\n",
832 err, pStreamIn->deviceFormat, pStreamIn->streamFormat));
833 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
834 }
835
836 if ( pStreamIn->deviceFormat.mChannelsPerFrame == 1 /* Mono */
837 && pStreamIn->streamFormat.mChannelsPerFrame == 2 /* Stereo */)
838 {
839 /*
840 * If the channel count is different we have to tell this the converter
841 * and supply a channel mapping. For now we only support mapping
842 * from mono to stereo. For all other cases the core audio defaults
843 * are used, which means dropping additional channels in most
844 * cases.
845 */
846 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo, */
847
848 err = AudioConverterSetProperty(pStreamIn->converter, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
849 if (err != noErr)
850 {
851 LogRel(("CoreAudio: Failed to set channel mapping for the audio input converter (%RI32)\n", err));
852 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
853 }
854 }
855
856 /* Set the new input format description for the stream. */
857 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
858 1, &pStreamIn->deviceFormat, sizeof(pStreamIn->deviceFormat));
859 if (err != noErr)
860 {
861 LogRel(("CoreAudio: Failed to set input format for input stream (%RI32)\n", err));
862 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
863 }
864#if 0
865 /* Set sample rate converter quality to maximum */
866 uFlag = kAudioConverterQuality_Max;
867 err = AudioConverterSetProperty(pStreamIn->converter, kAudioConverterSampleRateConverterQuality,
868 sizeof(uFlag), &uFlag);
869 if (err != noErr)
870 LogRel(("CoreAudio: Failed to set input audio converter quality to the maximum (%RI32)\n", err));
871#endif
872 LogRel(("CoreAudio: Input converter is active\n"));
873 }
874
875 /* Set the new output format description for the stream. */
876 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
877 1, &pStreamIn->deviceFormat, sizeof(pStreamIn->deviceFormat));
878 if (err != noErr)
879 {
880 LogRel(("CoreAudio: Failed to set output format for input stream (%RI32)\n", err));
881 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
882 }
883
884 /*
885 * Also set the frame buffer size off the device on our AudioUnit. This
886 * should make sure that the frames count which we receive in the render
887 * thread is as we like.
888 */
889 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
890 1, &cFrames, sizeof(cFrames));
891 if (err != noErr) {
892 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err));
893 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
894 }
895
896 /* Finally initialize the new AudioUnit. */
897 err = AudioUnitInitialize(pStreamIn->audioUnit);
898 if (err != noErr)
899 {
900 LogRel(("CoreAudio: Failed to initialize audio unit for input stream (%RI32)\n", err));
901 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
902 }
903
904 uSize = sizeof(pStreamIn->deviceFormat);
905 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
906 1, &pStreamIn->deviceFormat, &uSize);
907 if (err != noErr)
908 {
909 LogRel(("CoreAudio: Failed to get input device format (%RI32)\n", err));
910 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
911 }
912
913 /*
914 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
915 * the frame buffer size set in the previous calls. So finally get the
916 * frame buffer size after the AudioUnit was initialized.
917 */
918 uSize = sizeof(cFrames);
919 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice,kAudioUnitScope_Global,
920 0, &cFrames, &uSize);
921 if (err != noErr)
922 {
923 LogRel(("CoreAudio: Failed to get maximum frame buffer size from input audio device (%RI32)\n", err));
924 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
925 }
926
927 /* Calculate the ratio between the device and the stream sample rate. */
928 pStreamIn->sampleRatio = pStreamIn->streamFormat.mSampleRate / pStreamIn->deviceFormat.mSampleRate;
929
930 /* Set to zero first */
931 pStreamIn->pBuf = NULL;
932 /* Create the AudioBufferList structure with one buffer. */
933 pStreamIn->bufferList.mNumberBuffers = 1;
934 /* Initialize the buffer to nothing. */
935 pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
936 pStreamIn->bufferList.mBuffers[0].mDataByteSize = 0;
937 pStreamIn->bufferList.mBuffers[0].mData = NULL;
938
939 int rc = VINF_SUCCESS;
940
941 /*
942 * Make sure that the ring buffer is big enough to hold the recording
943 * data. Compare the maximum frames per slice value with the frames
944 * necessary when using the converter where the sample rate could differ.
945 * The result is always multiplied by the channels per frame to get the
946 * samples count.
947 */
948 UInt32 cSamples = cFrames * pStreamIn->streamFormat.mChannelsPerFrame;
949 if (!cSamples)
950 {
951 LogRel(("CoreAudio: Failed to determine samples buffer count input stream\n"));
952 rc = VERR_INVALID_PARAMETER;
953 }
954
955 /* Create the internal ring buffer. */
956 if (RT_SUCCESS(rc))
957 rc = RTCircBufCreate(&pStreamIn->pBuf, cSamples << pHstStrmIn->Props.cShift);
958 if (RT_SUCCESS(rc))
959 {
960#ifdef DEBUG
961 propAdr.mSelector = kAudioDeviceProcessorOverload;
962 propAdr.mScope = kAudioUnitScope_Global;
963 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
964 drvHostCoreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
965 if (RT_UNLIKELY(err != noErr))
966 LogRel(("CoreAudio: Failed to add the processor overload listener for input stream (%RI32)\n", err));
967#endif /* DEBUG */
968 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
969 propAdr.mScope = kAudioUnitScope_Global;
970 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
971 drvHostCoreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
972 /* Not fatal. */
973 if (RT_UNLIKELY(err != noErr))
974 LogRel(("CoreAudio: Failed to register sample rate changed listener for input stream (%RI32)\n", err));
975 }
976
977 if (RT_SUCCESS(rc))
978 {
979 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_INIT);
980
981 if (pcSamples)
982 *pcSamples = cSamples;
983 }
984 else
985 {
986 AudioUnitUninitialize(pStreamIn->audioUnit);
987
988 if (pStreamIn->pBuf)
989 {
990 RTCircBufDestroy(pStreamIn->pBuf);
991 pStreamIn->pBuf = NULL;
992 }
993 }
994
995 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
996 return rc;
997}
998
999/** @todo Eventually split up this function, as this already is huge! */
1000static int drvHostCoreAudioInitOutput(PPDMAUDIOHSTSTRMOUT pHstStrmOut, uint32_t *pcSamples)
1001{
1002 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
1003
1004 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_INIT);
1005
1006 OSStatus err = noErr;
1007
1008 UInt32 uSize = 0;
1009 if (pStreamOut->deviceID == kAudioDeviceUnknown)
1010 {
1011 /* Fetch the default audio input device currently in use. */
1012 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
1013 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1014 uSize = sizeof(pStreamOut->deviceID);
1015 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &pStreamOut->deviceID);
1016 if (err != noErr)
1017 {
1018 LogRel(("CoreAudio: Unable to determine default output device (%RI32)\n", err));
1019 return VERR_NOT_FOUND;
1020 }
1021 }
1022
1023 /*
1024 * Try to get the name of the output device and log it. It's not fatal if it fails.
1025 */
1026 CFStringRef strTemp;
1027
1028 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
1029 kAudioObjectPropertyElementMaster };
1030 uSize = sizeof(CFStringRef);
1031 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1032 if (err == noErr)
1033 {
1034 char *pszDevName = NULL;
1035 err = drvHostCoreAudioCFStringToCString(strTemp, &pszDevName);
1036 if (err == noErr)
1037 {
1038 CFRelease(strTemp);
1039
1040 /* Get the device' UUID. */
1041 propAdr.mSelector = kAudioDevicePropertyDeviceUID;
1042 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1043 if (err == noErr)
1044 {
1045 char *pszUID = NULL;
1046 err = drvHostCoreAudioCFStringToCString(strTemp, &pszUID);
1047 if (err == noErr)
1048 {
1049 CFRelease(strTemp);
1050 LogRel(("CoreAudio: Using output device: %s (UID: %s)\n", pszDevName, pszUID));
1051
1052 RTMemFree(pszUID);
1053 }
1054 }
1055
1056 RTMemFree(pszDevName);
1057 }
1058 }
1059 else
1060 LogRel(("CoreAudio: Unable to determine output device name (%RI32)\n", err));
1061
1062 /* Get the default frames buffer size, so that we can setup our internal buffers. */
1063 UInt32 cFrames;
1064 uSize = sizeof(cFrames);
1065 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
1066 propAdr.mScope = kAudioDevicePropertyScopeInput;
1067 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
1068 if (err != noErr)
1069 {
1070 LogRel(("CoreAudio: Failed to determine frame buffer size of the audio output device (%RI32)\n", err));
1071 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1072 }
1073
1074 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
1075 err = drvHostCoreAudioSetFrameBufferSize(pStreamOut->deviceID, false /* fInput */, cFrames, &cFrames);
1076 if (err != noErr)
1077 {
1078 LogRel(("CoreAudio: Failed to set frame buffer size for the audio output device (%RI32)\n", err));
1079 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1080 }
1081
1082 ComponentDescription cd;
1083 RT_ZERO(cd);
1084 cd.componentType = kAudioUnitType_Output;
1085 cd.componentSubType = kAudioUnitSubType_HALOutput;
1086 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
1087
1088 /* Try to find the default HAL output component. */
1089 Component cp = FindNextComponent(NULL, &cd);
1090 if (cp == 0)
1091 {
1092 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
1093 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1094 }
1095
1096 /* Open the default HAL output component. */
1097 err = OpenAComponent(cp, &pStreamOut->audioUnit);
1098 if (err != noErr)
1099 {
1100 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
1101 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1102 }
1103
1104 /* Switch the I/O mode for output to on. */
1105 UInt32 uFlag = 1;
1106 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
1107 0, &uFlag, sizeof(uFlag));
1108 if (err != noErr)
1109 {
1110 LogRel(("CoreAudio: Failed to disable I/O mode for output stream (%RI32)\n", err));
1111 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1112 }
1113
1114 /* Set the default audio output device as the device for the new AudioUnit. */
1115 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
1116 0, &pStreamOut->deviceID, sizeof(pStreamOut->deviceID));
1117 if (err != noErr)
1118 {
1119 LogRel(("CoreAudio: Failed to set current device for output stream (%RI32)\n", err));
1120 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1121 }
1122
1123 /*
1124 * CoreAudio will inform us on a second thread for new incoming audio data.
1125 * Therefor register a callback function which will process the new data.
1126 */
1127 AURenderCallbackStruct cb;
1128 RT_ZERO(cb);
1129 cb.inputProc = drvHostCoreAudioPlaybackCallback; /* pvUser */
1130 cb.inputProcRefCon = pStreamOut;
1131
1132 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1133 0, &cb, sizeof(cb));
1134 if (err != noErr)
1135 {
1136 LogRel(("CoreAudio: Failed to register output callback (%RI32)\n", err));
1137 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1138 }
1139
1140 /* Fetch the current stream format of the device. */
1141 uSize = sizeof(pStreamOut->deviceFormat);
1142 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1143 0, &pStreamOut->deviceFormat, &uSize);
1144 if (err != noErr)
1145 {
1146 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
1147 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1148 }
1149
1150 /* Create an AudioStreamBasicDescription based on our required audio settings. */
1151 drvHostCoreAudioPCMInfoToASBDesc(&pStreamOut->streamOut.Props, &pStreamOut->streamFormat);
1152
1153#ifdef DEBUG
1154 drvHostCoreAudioPrintASBDesc("CoreAudio: Output device", &pStreamOut->deviceFormat);
1155 drvHostCoreAudioPrintASBDesc("CoreAudio: Output format", &pStreamOut->streamFormat);
1156#endif /* DEBUG */
1157
1158 /* Set the new output format description for the stream. */
1159 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1160 0, &pStreamOut->streamFormat, sizeof(pStreamOut->streamFormat));
1161 if (err != noErr)
1162 {
1163 LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err));
1164 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1165 }
1166
1167 uSize = sizeof(pStreamOut->deviceFormat);
1168 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1169 0, &pStreamOut->deviceFormat, &uSize);
1170 if (err != noErr)
1171 {
1172 LogRel(("CoreAudio: Failed to retrieve device format for output stream (%RI32)\n", err));
1173 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1174 }
1175
1176 /*
1177 * Also set the frame buffer size off the device on our AudioUnit. This
1178 * should make sure that the frames count which we receive in the render
1179 * thread is as we like.
1180 */
1181 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1182 0, &cFrames, sizeof(cFrames));
1183 if (err != noErr)
1184 {
1185 LogRel(("CoreAudio: Failed to set maximum frame buffer size for output AudioUnit (%RI32)\n", err));
1186 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1187 }
1188
1189 /* Finally initialize the new AudioUnit. */
1190 err = AudioUnitInitialize(pStreamOut->audioUnit);
1191 if (err != noErr)
1192 {
1193 LogRel(("CoreAudio: Failed to initialize the output audio device (%RI32)\n", err));
1194 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1195 }
1196
1197 /*
1198 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
1199 * the frame buffer size set in the previous calls. So finally get the
1200 * frame buffer size after the AudioUnit was initialized.
1201 */
1202 uSize = sizeof(cFrames);
1203 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1204 0, &cFrames, &uSize);
1205 if (err != noErr)
1206 {
1207 LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err));
1208
1209 AudioUnitUninitialize(pStreamOut->audioUnit);
1210 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1211 }
1212
1213 /*
1214 * Make sure that the ring buffer is big enough to hold the recording
1215 * data. Compare the maximum frames per slice value with the frames
1216 * necessary when using the converter where the sample rate could differ.
1217 * The result is always multiplied by the channels per frame to get the
1218 * samples count.
1219 */
1220 int rc = VINF_SUCCESS;
1221
1222 UInt32 cSamples = cFrames * pStreamOut->streamFormat.mChannelsPerFrame;
1223 if (!cSamples)
1224 {
1225 LogRel(("CoreAudio: Failed to determine samples buffer count output stream\n"));
1226 rc = VERR_INVALID_PARAMETER;
1227 }
1228
1229 /* Create the internal ring buffer. */
1230 rc = RTCircBufCreate(&pStreamOut->pBuf, cSamples << pHstStrmOut->Props.cShift);
1231 if (RT_SUCCESS(rc))
1232 {
1233 /* Allocate temporary buffer. */
1234 pStreamOut->cbPCMBuf = _4K; /** @todo Make this configurable. */
1235 pStreamOut->pvPCMBuf = RTMemAlloc(pStreamOut->cbPCMBuf);
1236 if (!pStreamOut->pvPCMBuf)
1237 rc = VERR_NO_MEMORY;
1238 }
1239
1240 if (RT_SUCCESS(rc))
1241 {
1242 /*
1243 * Register callbacks.
1244 */
1245#ifdef DEBUG
1246 propAdr.mSelector = kAudioDeviceProcessorOverload;
1247 propAdr.mScope = kAudioUnitScope_Global;
1248 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1249 drvHostCoreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1250 if (err != noErr)
1251 LogRel(("CoreAudio: Failed to register processor overload listener for output stream (%RI32)\n", err));
1252#endif /* DEBUG */
1253
1254 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1255 propAdr.mScope = kAudioUnitScope_Global;
1256 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1257 drvHostCoreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1258 /* Not fatal. */
1259 if (err != noErr)
1260 LogRel(("CoreAudio: Failed to register sample rate changed listener for output stream (%RI32)\n", err));
1261 }
1262
1263 if (RT_SUCCESS(rc))
1264 {
1265 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_INIT);
1266
1267 if (pcSamples)
1268 *pcSamples = cSamples;
1269 }
1270 else
1271 {
1272 AudioUnitUninitialize(pStreamOut->audioUnit);
1273
1274 if (pStreamOut->pBuf)
1275 {
1276 RTCircBufDestroy(pStreamOut->pBuf);
1277 pStreamOut->pBuf = NULL;
1278 }
1279 }
1280
1281 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
1282 return rc;
1283}
1284
1285static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)
1286{
1287 NOREF(pInterface);
1288
1289 LogFlowFuncEnter();
1290
1291 return VINF_SUCCESS;
1292}
1293
1294static DECLCALLBACK(int) drvHostCoreAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1295 uint32_t *pcSamplesCaptured)
1296{
1297 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1298 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1299
1300 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
1301
1302 size_t csReads = 0;
1303 char *pcSrc;
1304 PPDMAUDIOSAMPLE psDst;
1305
1306 /* Check if the audio device should be reinitialized. If so do it. */
1307 if (ASMAtomicReadU32(&pStreamIn->status) == CA_STATUS_REINIT)
1308 drvHostCoreAudioReinitInput(pInterface, &pStreamIn->streamIn);
1309
1310 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
1311 {
1312 if (pcSamplesCaptured)
1313 *pcSamplesCaptured = 0;
1314 return VINF_SUCCESS;
1315 }
1316
1317 int rc = VINF_SUCCESS;
1318 uint32_t cbWrittenTotal = 0;
1319
1320 do
1321 {
1322 size_t cbBuf = audioMixBufSizeBytes(&pHstStrmIn->MixBuf);
1323 size_t cbToWrite = RT_MIN(cbBuf, RTCircBufFree(pStreamIn->pBuf));
1324 LogFlowFunc(("cbToWrite=%zu\n", cbToWrite));
1325
1326 uint32_t cWritten, cbWritten;
1327 uint8_t *puBuf;
1328 size_t cbToRead;
1329
1330 while (cbToWrite)
1331 {
1332 /* Try to acquire the necessary block from the ring buffer. */
1333 RTCircBufAcquireReadBlock(pStreamIn->pBuf, cbToWrite, (void **)&puBuf, &cbToRead);
1334 if (!cbToRead)
1335 {
1336 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbToRead);
1337 break;
1338 }
1339
1340 rc = audioMixBufWriteCirc(&pHstStrmIn->MixBuf, puBuf, cbToRead, &cWritten);
1341 if (RT_FAILURE(rc))
1342 break;
1343
1344 cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
1345
1346 /* Release the read buffer, so it could be used for new data. */
1347 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbWritten);
1348
1349 Assert(cbToWrite >= cbWritten);
1350 cbToWrite -= cbWritten;
1351 cbWrittenTotal += cbWritten;
1352 }
1353 }
1354 while (0);
1355
1356 if (RT_SUCCESS(rc))
1357 {
1358 uint32_t cWrittenTotal = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cbWrittenTotal);
1359 if (cWrittenTotal)
1360 audioMixBufFinish(&pHstStrmIn->MixBuf, cWrittenTotal);
1361
1362 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 bytes)\n", cWrittenTotal, cbWrittenTotal));
1363
1364 if (pcSamplesCaptured)
1365 *pcSamplesCaptured = cWrittenTotal;
1366 }
1367
1368 return rc;
1369}
1370
1371/* Callback for getting notified when some of the properties of an audio device has changed. */
1372static DECLCALLBACK(OSStatus) drvHostCoreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID,
1373 UInt32 nAddresses,
1374 const AudioObjectPropertyAddress properties[],
1375 void *pvUser)
1376{
1377 switch (propertyID)
1378 {
1379#ifdef DEBUG
1380 case kAudioDeviceProcessorOverload:
1381 {
1382 Log2(("CoreAudio: [Output] Processor overload detected!\n"));
1383 break;
1384 }
1385#endif /* DEBUG */
1386 default:
1387 break;
1388 }
1389
1390 return noErr;
1391}
1392
1393/* Callback to feed audio output buffer. */
1394static DECLCALLBACK(OSStatus) drvHostCoreAudioPlaybackCallback(void *pvUser,
1395 AudioUnitRenderActionFlags *pActionFlags,
1396 const AudioTimeStamp *pAudioTS,
1397 UInt32 uBusID,
1398 UInt32 cFrames,
1399 AudioBufferList *pBufData)
1400{
1401 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pvUser;
1402 PPDMAUDIOHSTSTRMOUT pHstStrmOut = &pStreamOut->streamOut;
1403
1404 if (ASMAtomicReadU32(&pStreamOut->status) != CA_STATUS_INIT)
1405 {
1406 pBufData->mBuffers[0].mDataByteSize = 0;
1407 return noErr;
1408 }
1409
1410 /* How much space is used in the ring buffer? */
1411 size_t cbDataAvail = RT_MIN(RTCircBufUsed(pStreamOut->pBuf), pBufData->mBuffers[0].mDataByteSize);
1412 if (!cbDataAvail)
1413 {
1414 pBufData->mBuffers[0].mDataByteSize = 0;
1415 return noErr;
1416 }
1417
1418 uint8_t *pbSrc = NULL;
1419 size_t cbRead = 0;
1420 size_t cbToRead;
1421 while (cbDataAvail)
1422 {
1423 /* Try to acquire the necessary block from the ring buffer. */
1424 RTCircBufAcquireReadBlock(pStreamOut->pBuf, cbDataAvail, (void **)&pbSrc, &cbToRead);
1425
1426 /* Break if nothing is used anymore. */
1427 if (!cbToRead)
1428 break;
1429
1430 /* Copy the data from our ring buffer to the core audio buffer. */
1431 memcpy((uint8_t *)pBufData->mBuffers[0].mData + cbRead, pbSrc, cbToRead);
1432
1433 /* Release the read buffer, so it could be used for new data. */
1434 RTCircBufReleaseReadBlock(pStreamOut->pBuf, cbToRead);
1435
1436 /* Move offset. */
1437 cbRead += cbToRead;
1438 Assert(pBufData->mBuffers[0].mDataByteSize >= cbRead);
1439
1440 Assert(cbToRead <= cbDataAvail);
1441 cbDataAvail -= cbToRead;
1442 }
1443
1444 /* Write the bytes to the core audio buffer which where really written. */
1445 pBufData->mBuffers[0].mDataByteSize = cbRead;
1446
1447 LogFlowFunc(("CoreAudio: [Output] Read %zu / %zu bytes\n", cbRead, cbDataAvail));
1448
1449 return noErr;
1450}
1451
1452static DECLCALLBACK(int) drvHostCoreAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1453 uint32_t *pcSamplesPlayed)
1454{
1455 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1456 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1457
1458 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
1459
1460 /* Check if the audio device should be reinitialized. If so do it. */
1461 if (ASMAtomicReadU32(&pStreamOut->status) == CA_STATUS_REINIT)
1462 drvHostCoreAudioReinitOutput(pInterface, &pStreamOut->streamOut);
1463
1464 /* Not much else to do here. */
1465
1466 uint32_t cLive = drvAudioHstOutSamplesLive(pHstStrmOut, NULL /* pcStreamsLive */);
1467 if (!cLive) /* Not samples to play? Bail out. */
1468 {
1469 if (pcSamplesPlayed)
1470 *pcSamplesPlayed = 0;
1471 return VINF_SUCCESS;
1472 }
1473
1474 int rc = VINF_SUCCESS;
1475 uint32_t cbReadTotal = 0;
1476
1477 do
1478 {
1479 size_t cbBuf = RT_MIN(audioMixBufSizeBytes(&pHstStrmOut->MixBuf), pStreamOut->cbPCMBuf);
1480 size_t cbToRead = RT_MIN(cbBuf, RTCircBufFree(pStreamOut->pBuf));
1481 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
1482
1483 uint32_t cRead, cbRead;
1484 uint8_t *puBuf;
1485 size_t cbToWrite;
1486
1487 while (cbToRead)
1488 {
1489 rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf,
1490 pStreamOut->pvPCMBuf, cbToRead, &cRead);
1491 if ( RT_FAILURE(rc)
1492 || !cRead)
1493 break;
1494
1495 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
1496
1497 /* Try to acquire the necessary space from the ring buffer. */
1498 RTCircBufAcquireWriteBlock(pStreamOut->pBuf, cbRead, (void **)&puBuf, &cbToWrite);
1499 if (!cbToWrite)
1500 {
1501 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbToWrite);
1502 break;
1503 }
1504
1505 /* Transfer data into stream's own ring buffer. The playback will operate on this
1506 * own ring buffer separately. */
1507 Assert(cbToWrite <= cbRead);
1508 memcpy(puBuf, pStreamOut->pvPCMBuf, cbToWrite);
1509
1510 /* Release the ring buffer, so the read thread could start reading this data. */
1511 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbToWrite);
1512
1513 Assert(cbToRead >= cRead);
1514 cbToRead -= cbRead;
1515 cbReadTotal += cbRead;
1516 }
1517 }
1518 while (0);
1519
1520 if (RT_SUCCESS(rc))
1521 {
1522 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
1523 if (cReadTotal)
1524 audioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
1525
1526 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes)\n", cReadTotal, cbReadTotal));
1527
1528 if (pcSamplesPlayed)
1529 *pcSamplesPlayed = cReadTotal;
1530 }
1531
1532 return rc;
1533}
1534
1535static DECLCALLBACK(int) drvHostCoreAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
1536 PDMAUDIOSTREAMCMD enmStreamCmd)
1537{
1538 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
1539
1540 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1541
1542 uint32_t uStatus = ASMAtomicReadU32(&pStreamOut->status);
1543 if (!( uStatus == CA_STATUS_INIT
1544 || uStatus == CA_STATUS_REINIT))
1545 {
1546 return VINF_SUCCESS;
1547 }
1548
1549 int rc = VINF_SUCCESS;
1550 OSStatus err;
1551
1552 switch (enmStreamCmd)
1553 {
1554 case PDMAUDIOSTREAMCMD_ENABLE:
1555 {
1556 /* Only start the device if it is actually stopped */
1557 if (!drvHostCoreAudioIsRunning(pStreamOut->deviceID))
1558 {
1559 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1560 if (err != noErr)
1561 {
1562 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1563 /* Keep going. */
1564 }
1565 RTCircBufReset(pStreamOut->pBuf);
1566
1567 err = AudioOutputUnitStart(pStreamOut->audioUnit);
1568 if (RT_UNLIKELY(err != noErr))
1569 {
1570 LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));
1571 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1572 }
1573 }
1574 break;
1575 }
1576
1577 case PDMAUDIOSTREAMCMD_DISABLE:
1578 {
1579 /* Only stop the device if it is actually running */
1580 if (drvHostCoreAudioIsRunning(pStreamOut->deviceID))
1581 {
1582 err = AudioOutputUnitStop(pStreamOut->audioUnit);
1583 if (err != noErr)
1584 {
1585 LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
1586 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1587 break;
1588 }
1589
1590 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1591 if (err != noErr)
1592 {
1593 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1594 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1595 }
1596 }
1597 break;
1598 }
1599
1600 default:
1601 rc = VERR_NOT_SUPPORTED;
1602 break;
1603 }
1604
1605 LogFlowFuncLeaveRC(rc);
1606 return rc;
1607}
1608
1609static DECLCALLBACK(int) drvHostCoreAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1610 PDMAUDIOSTREAMCMD enmStreamCmd)
1611{
1612 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
1613
1614 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1615
1616 uint32_t uStatus = ASMAtomicReadU32(&pStreamIn->status);
1617 if (!( uStatus == CA_STATUS_INIT
1618 || uStatus == CA_STATUS_REINIT))
1619 {
1620 return VINF_SUCCESS;
1621 }
1622
1623 int rc = VINF_SUCCESS;
1624 OSStatus err;
1625
1626 switch (enmStreamCmd)
1627 {
1628 case PDMAUDIOSTREAMCMD_ENABLE:
1629 {
1630 /* Only start the device if it is actually stopped */
1631 if (!drvHostCoreAudioIsRunning(pStreamIn->deviceID))
1632 {
1633 RTCircBufReset(pStreamIn->pBuf);
1634 err = AudioOutputUnitStart(pStreamIn->audioUnit);
1635 if (err != noErr)
1636 {
1637 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1638 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1639 break;
1640 }
1641 }
1642
1643 if (err != noErr)
1644 {
1645 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1646 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1647 }
1648 break;
1649 }
1650
1651 case PDMAUDIOSTREAMCMD_DISABLE:
1652 {
1653 /* Only stop the device if it is actually running */
1654 if (drvHostCoreAudioIsRunning(pStreamIn->deviceID))
1655 {
1656 err = AudioOutputUnitStop(pStreamIn->audioUnit);
1657 if (err != noErr)
1658 {
1659 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
1660 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1661 break;
1662 }
1663
1664 err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);
1665 if (err != noErr)
1666 {
1667 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1668 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1669 break;
1670 }
1671 }
1672 break;
1673 }
1674
1675 default:
1676 rc = VERR_NOT_SUPPORTED;
1677 break;
1678 }
1679
1680 LogFlowFuncLeaveRC(rc);
1681 return rc;
1682}
1683
1684static DECLCALLBACK(int) drvHostCoreAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1685{
1686 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN) pHstStrmIn;
1687
1688 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1689 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1690
1691 LogFlowFuncEnter();
1692
1693 uint32_t status = ASMAtomicReadU32(&pStreamIn->status);
1694 if (!( status == CA_STATUS_INIT
1695 || status == CA_STATUS_REINIT))
1696 {
1697 return VINF_SUCCESS;
1698 }
1699
1700 OSStatus err = noErr;
1701
1702 int rc = drvHostCoreAudioControlIn(pInterface, &pStreamIn->streamIn, PDMAUDIOSTREAMCMD_DISABLE);
1703 if (RT_SUCCESS(rc))
1704 {
1705 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);
1706
1707 /*
1708 * Unregister input device callbacks.
1709 */
1710 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioUnitScope_Global,
1711 kAudioObjectPropertyElementMaster };
1712#ifdef DEBUG
1713 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
1714 drvHostCoreAudioRecordingAudioDevicePropertyChanged, NULL);
1715 /* Not Fatal */
1716 if (RT_UNLIKELY(err != noErr))
1717 LogRel(("CoreAudio: Failed to remove the processor overload listener (%RI32)\n", err));
1718#endif /* DEBUG */
1719
1720 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1721 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
1722 drvHostCoreAudioRecordingAudioDevicePropertyChanged, NULL);
1723 /* Not Fatal */
1724 if (RT_UNLIKELY(err != noErr))
1725 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
1726
1727 if (pStreamIn->fDefDevChgListReg)
1728 {
1729 propAdr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
1730 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
1731 drvHostCoreAudioDefaultDeviceChanged, NULL);
1732 if (RT_LIKELY(err == noErr))
1733 {
1734 pStreamIn->fDefDevChgListReg = false;
1735 }
1736 else
1737 LogRel(("CoreAudio: [Output] Failed to remove the default input device changed listener (%RI32)\n", err));
1738 }
1739
1740 if (pStreamIn->converter)
1741 {
1742 AudioConverterDispose(pStreamIn->converter);
1743 pStreamIn->converter = NULL;
1744 }
1745
1746 err = AudioUnitUninitialize(pStreamIn->audioUnit);
1747 if (RT_LIKELY(err == noErr))
1748 {
1749 err = CloseComponent(pStreamIn->audioUnit);
1750 if (RT_LIKELY(err == noErr))
1751 {
1752 RTCircBufDestroy(pStreamIn->pBuf);
1753
1754 pStreamIn->audioUnit = NULL;
1755 pStreamIn->deviceID = kAudioDeviceUnknown;
1756 pStreamIn->pBuf = NULL;
1757 pStreamIn->sampleRatio = 1;
1758 pStreamIn->rpos = 0;
1759
1760 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
1761 }
1762 else
1763 {
1764 LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
1765 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1766 }
1767 }
1768 else
1769 {
1770 LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
1771 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1772 }
1773 }
1774 else
1775 {
1776 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
1777 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1778 }
1779
1780 LogFlowFuncLeaveRC(rc);
1781 return rc;
1782}
1783
1784static DECLCALLBACK(int) drvHostCoreAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1785{
1786 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1787 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1788
1789 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
1790
1791 LogFlowFuncEnter();
1792
1793 uint32_t status = ASMAtomicReadU32(&pStreamOut->status);
1794 if (!( status == CA_STATUS_INIT
1795 || status == CA_STATUS_REINIT))
1796 {
1797 return VINF_SUCCESS;
1798 }
1799
1800 int rc = drvHostCoreAudioControlOut(pInterface, &pStreamOut->streamOut, PDMAUDIOSTREAMCMD_DISABLE);
1801 if (RT_SUCCESS(rc))
1802 {
1803 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_UNINIT);
1804
1805 OSStatus err;
1806 if (pStreamOut->fDefDevChgListReg)
1807 {
1808 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioUnitScope_Global,
1809 kAudioObjectPropertyElementMaster };
1810 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
1811 drvHostCoreAudioDefaultDeviceChanged, NULL);
1812 if (RT_LIKELY(err == noErr))
1813 {
1814 pStreamOut->fDefDevChgListReg = false;
1815 }
1816 else
1817 LogRel(("CoreAudio: [Output] Failed to remove the default playback device changed listener (%RI32)\n", err));
1818 }
1819
1820 err = AudioUnitUninitialize(pStreamOut->audioUnit);
1821 if (err == noErr)
1822 {
1823 err = CloseComponent(pStreamOut->audioUnit);
1824 if (err == noErr)
1825 {
1826 RTCircBufDestroy(pStreamOut->pBuf);
1827 pStreamOut->pBuf = NULL;
1828
1829 pStreamOut->audioUnit = NULL;
1830 pStreamOut->deviceID = kAudioDeviceUnknown;
1831
1832 if (pStreamOut->pvPCMBuf)
1833 {
1834 RTMemFree(pStreamOut->pvPCMBuf);
1835 pStreamOut->pvPCMBuf = NULL;
1836 pStreamOut->cbPCMBuf = 0;
1837 }
1838
1839 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
1840 }
1841 else
1842 LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
1843 }
1844 else
1845 LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
1846 }
1847 else
1848 LogRel(("CoreAudio: Failed to stop playback, rc=%Rrc\n", rc));
1849
1850 LogFlowFuncLeaveRC(rc);
1851 return rc;
1852}
1853
1854static DECLCALLBACK(int) drvHostCoreAudioInitIn(PPDMIHOSTAUDIO pInterface,
1855 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
1856 PDMAUDIORECSOURCE enmRecSource,
1857 uint32_t *pcSamples)
1858{
1859 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
1860
1861 LogFlowFunc(("enmRecSource=%ld\n"));
1862
1863 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
1864
1865 pStreamIn->audioUnit = NULL;
1866 pStreamIn->deviceID = kAudioDeviceUnknown;
1867 pStreamIn->converter = NULL;
1868 pStreamIn->sampleRatio = 1;
1869 pStreamIn->rpos = 0;
1870
1871 bool fDeviceByUser = false;
1872
1873 /* Initialize the hardware info section with the audio settings */
1874 int rc = drvAudioStreamCfgToProps(pCfg, &pStreamIn->streamIn.Props);
1875 if (RT_SUCCESS(rc))
1876 {
1877#if 0
1878 /* Try to find the audio device set by the user */
1879 if (DeviceUID.pszInputDeviceUID)
1880 {
1881 pStreamIn->deviceID = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszInputDeviceUID);
1882 /* Not fatal */
1883 if (pStreamIn->deviceID == kAudioDeviceUnknown)
1884 LogRel(("CoreAudio: Unable to find input device %s. Falling back to the default audio device. \n", DeviceUID.pszInputDeviceUID));
1885 else
1886 fDeviceByUser = true;
1887 }
1888#endif
1889 rc = drvHostCoreAudioInitInput(&pStreamIn->streamIn, pcSamples);
1890 }
1891
1892 if (RT_SUCCESS(rc))
1893 {
1894 /* When the devices isn't forced by the user, we want default device change notifications. */
1895 if (!fDeviceByUser)
1896 {
1897 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
1898 kAudioObjectPropertyElementMaster };
1899 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
1900 drvHostCoreAudioDefaultDeviceChanged, (void *)pStreamIn);
1901 /* Not fatal. */
1902 if (RT_LIKELY(err == noErr))
1903 {
1904 pStreamIn->fDefDevChgListReg = true;
1905 }
1906 else
1907 LogRel(("CoreAudio: Failed to add the default input device changed listener (%RI32)\n", err));
1908 }
1909 }
1910
1911 LogFlowFuncLeaveRC(rc);
1912 return rc;
1913}
1914
1915static DECLCALLBACK(int) drvHostCoreAudioInitOut(PPDMIHOSTAUDIO pInterface,
1916 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
1917 uint32_t *pcSamples)
1918{
1919 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
1920
1921 OSStatus err = noErr;
1922 int rc = 0;
1923 bool fDeviceByUser = false; /* use we a device which was set by the user? */
1924
1925 LogFlowFuncEnter();
1926
1927 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
1928
1929 pStreamOut->audioUnit = NULL;
1930 pStreamOut->deviceID = kAudioDeviceUnknown;
1931
1932 /* Initialize the hardware info section with the audio settings */
1933 drvAudioStreamCfgToProps(pCfg, &pStreamOut->streamOut.Props);
1934
1935#if 0
1936 /* Try to find the audio device set by the user. Use
1937 * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0
1938 * to set it. */
1939 if (DeviceUID.pszOutputDeviceUID)
1940 {
1941 pStreamOut->audioDeviceId = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszOutputDeviceUID);
1942 /* Not fatal */
1943 if (pStreamOut->audioDeviceId == kAudioDeviceUnknown)
1944 LogRel(("CoreAudio: Unable to find output device %s. Falling back to the default audio device. \n", DeviceUID.pszOutputDeviceUID));
1945 else
1946 fDeviceByUser = true;
1947 }
1948#endif
1949
1950 rc = drvHostCoreAudioInitOutput(pHstStrmOut, pcSamples);
1951 if (RT_FAILURE(rc))
1952 return rc;
1953
1954 /* When the devices isn't forced by the user, we want default device change notifications. */
1955 if (!fDeviceByUser)
1956 {
1957 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
1958 kAudioObjectPropertyElementMaster };
1959 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
1960 drvHostCoreAudioDefaultDeviceChanged, (void *)pStreamOut);
1961 /* Not fatal. */
1962 if (RT_LIKELY(err == noErr))
1963 {
1964 pStreamOut->fDefDevChgListReg = true;
1965 }
1966 else
1967 LogRel(("CoreAudio: Failed to add the default output device changed listener (%RI32)\n", err));
1968 }
1969
1970 return VINF_SUCCESS;
1971}
1972
1973static DECLCALLBACK(bool) drvHostCoreAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1974{
1975 NOREF(pInterface);
1976 NOREF(enmDir);
1977 return true; /* Always all enabled. */
1978}
1979
1980static DECLCALLBACK(int) drvHostCoreAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pAudioConf)
1981{
1982 pAudioConf->cbStreamOut = sizeof(COREAUDIOSTREAMOUT);
1983 pAudioConf->cbStreamIn = sizeof(COREAUDIOSTREAMIN);
1984 pAudioConf->cMaxHstStrmsOut = 1;
1985 pAudioConf->cMaxHstStrmsIn = 2;
1986
1987 return VINF_SUCCESS;
1988}
1989
1990static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
1991{
1992 NOREF(pInterface);
1993}
1994
1995static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1996{
1997 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1998 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1999 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2000 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2001
2002 return NULL;
2003}
2004
2005 /* Construct a DirectSound Audio driver instance.
2006 *
2007 * @copydoc FNPDMDRVCONSTRUCT
2008 */
2009static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2010{
2011 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2012 LogRel(("Audio: Initializing Core Audio driver\n"));
2013
2014 /*
2015 * Init the static parts.
2016 */
2017 pThis->pDrvIns = pDrvIns;
2018 /* IBase */
2019 pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
2020 /* IHostAudio */
2021 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
2022
2023 return VINF_SUCCESS;
2024}
2025
2026/**
2027 * Char driver registration record.
2028 */
2029const PDMDRVREG g_DrvHostCoreAudio =
2030{
2031 /* u32Version */
2032 PDM_DRVREG_VERSION,
2033 /* szName */
2034 "CoreAudio",
2035 /* szRCMod */
2036 "",
2037 /* szR0Mod */
2038 "",
2039 /* pszDescription */
2040 "Core Audio host driver",
2041 /* fFlags */
2042 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2043 /* fClass. */
2044 PDM_DRVREG_CLASS_AUDIO,
2045 /* cMaxInstances */
2046 ~0U,
2047 /* cbInstance */
2048 sizeof(DRVHOSTCOREAUDIO),
2049 /* pfnConstruct */
2050 drvHostCoreAudioConstruct,
2051 /* pfnDestruct */
2052 NULL,
2053 /* pfnRelocate */
2054 NULL,
2055 /* pfnIOCtl */
2056 NULL,
2057 /* pfnPowerOn */
2058 NULL,
2059 /* pfnReset */
2060 NULL,
2061 /* pfnSuspend */
2062 NULL,
2063 /* pfnResume */
2064 NULL,
2065 /* pfnAttach */
2066 NULL,
2067 /* pfnDetach */
2068 NULL,
2069 /* pfnPowerOff */
2070 NULL,
2071 /* pfnSoftReset */
2072 NULL,
2073 /* u32EndVersion */
2074 PDM_DRVREG_VERSION
2075};
2076
Note: See TracBrowser for help on using the repository browser.

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