VirtualBox

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

Last change on this file since 54988 was 54851, checked in by vboxsync, 10 years ago

Audio/CoreAudio: Better fix for the leak, addresses both code paths taken in the method

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