VirtualBox

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

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

Audio/DrvHostCoreAudio.cpp: Recording fixes for OS X.

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