VirtualBox

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

最後變更 在這個檔案從61609是 61609,由 vboxsync 提交於 9 年 前

Audio: Update.

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

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