VirtualBox

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

Last change on this file since 57199 was 56649, checked in by vboxsync, 9 years ago

RT_MIN is a macro

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