VirtualBox

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

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

Audio: Warn on non-available audio streams based on the host audio backend's capabilities -- now the device emulation can ask the backend for its configuration to figure out how many input/output streams are supported by that backend. As the device emulation knows how many channels it finally implements, we there then can decide how to deal with the missing streams.

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