VirtualBox

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

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

Audio/DrvHostCoreAudio.cpp: Fixes for playback/recording.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 86.1 KB
 
1/* $Id: DrvHostCoreAudio.cpp 61698 2016-06-14 16:44:16Z 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 {
774 /* This is not fatal, can happen for some Macs. */
775 LogRel2(("CoreAudio: Unable to determine input device name (%RI32)\n", err));
776 }
777
778 /* Get the default frames buffer size, so that we can setup our internal buffers. */
779 UInt32 cFrames;
780 uSize = sizeof(cFrames);
781 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
782 propAdr.mScope = kAudioDevicePropertyScopeInput;
783 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
784 if (err != noErr)
785 {
786 /* Can happen if no input device is available by default. Happens on some Macs,
787 * so don't log this by default to not scare people. */
788 LogRel2(("CoreAudio: Failed to determine frame buffer size of the audio input device (%RI32)\n", err));
789 return VERR_AUDIO_BACKEND_INIT_FAILED;
790 }
791
792 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
793 err = coreAudioSetFrameBufferSize(pStreamIn->deviceID, true /* fInput */, cFrames, &cFrames);
794 if (err != noErr)
795 {
796 LogRel(("CoreAudio: Failed to set frame buffer size for the audio input device (%RI32)\n", err));
797 return VERR_AUDIO_BACKEND_INIT_FAILED;
798 }
799
800 LogFlowFunc(("cFrames=%RU32\n", cFrames));
801
802 ComponentDescription cd;
803 RT_ZERO(cd);
804 cd.componentType = kAudioUnitType_Output;
805 cd.componentSubType = kAudioUnitSubType_HALOutput;
806 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
807
808 /* Try to find the default HAL output component. */
809 Component cp = FindNextComponent(NULL, &cd);
810 if (cp == 0)
811 {
812 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
813 return VERR_AUDIO_BACKEND_INIT_FAILED;
814 }
815
816 /* Open the default HAL output component. */
817 err = OpenAComponent(cp, &pStreamIn->audioUnit);
818 if (err != noErr)
819 {
820 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
821 return VERR_AUDIO_BACKEND_INIT_FAILED;
822 }
823
824 /* Switch the I/O mode for input to on. */
825 UInt32 uFlag = 1;
826 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
827 1, &uFlag, sizeof(uFlag));
828 if (err != noErr)
829 {
830 LogRel(("CoreAudio: Failed to disable input I/O mode for input stream (%RI32)\n", err));
831 return VERR_AUDIO_BACKEND_INIT_FAILED;
832 }
833
834 /* Switch the I/O mode for output to off. This is important, as this is a pure input stream. */
835 uFlag = 0;
836 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
837 0, &uFlag, sizeof(uFlag));
838 if (err != noErr)
839 {
840 LogRel(("CoreAudio: Failed to disable output I/O mode for input stream (%RI32)\n", err));
841 return VERR_AUDIO_BACKEND_INIT_FAILED;
842 }
843
844 /* Set the default audio input device as the device for the new AudioUnit. */
845 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
846 0, &pStreamIn->deviceID, sizeof(pStreamIn->deviceID));
847 if (err != noErr)
848 {
849 LogRel(("CoreAudio: Failed to set current device (%RI32)\n", err));
850 return VERR_AUDIO_BACKEND_INIT_FAILED;
851 }
852
853 /*
854 * CoreAudio will inform us on a second thread for new incoming audio data.
855 * Therefor register a callback function which will process the new data.
856 */
857 AURenderCallbackStruct cb;
858 RT_ZERO(cb);
859 cb.inputProc = coreAudioRecordingCb;
860 cb.inputProcRefCon = pStreamIn;
861
862 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,
863 0, &cb, sizeof(cb));
864 if (err != noErr)
865 {
866 LogRel(("CoreAudio: Failed to register input callback (%RI32)\n", err));
867 return VERR_AUDIO_BACKEND_INIT_FAILED;
868 }
869
870 /* Fetch the current stream format of the device. */
871 uSize = sizeof(pStreamIn->deviceFormat);
872 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
873 1, &pStreamIn->deviceFormat, &uSize);
874 if (err != noErr)
875 {
876 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
877 return VERR_AUDIO_BACKEND_INIT_FAILED;
878 }
879
880 /* Create an AudioStreamBasicDescription based on our required audio settings. */
881 coreAudioPCMInfoToASBDesc(&pStreamIn->Stream.Props, &pStreamIn->streamFormat);
882
883 coreAudioPrintASBDesc("CoreAudio: Input device", &pStreamIn->deviceFormat);
884 coreAudioPrintASBDesc("CoreAudio: Input stream", &pStreamIn->streamFormat);
885
886 /* If the frequency of the device is different from the requested one we
887 * need a converter. The same count if the number of channels is different. */
888 if ( pStreamIn->deviceFormat.mSampleRate != pStreamIn->streamFormat.mSampleRate
889 || pStreamIn->deviceFormat.mChannelsPerFrame != pStreamIn->streamFormat.mChannelsPerFrame)
890 {
891 LogRel(("CoreAudio: Input converter is active\n"));
892
893 err = AudioConverterNew(&pStreamIn->deviceFormat, &pStreamIn->streamFormat, &pStreamIn->pConverter);
894 if (RT_UNLIKELY(err != noErr))
895 {
896 LogRel(("CoreAudio: Failed to create the audio converter (%RI32)\n", err));
897 return VERR_AUDIO_BACKEND_INIT_FAILED;
898 }
899
900 if ( pStreamIn->deviceFormat.mChannelsPerFrame == 1 /* Mono */
901 && pStreamIn->streamFormat.mChannelsPerFrame == 2 /* Stereo */)
902 {
903 /*
904 * If the channel count is different we have to tell this the converter
905 * and supply a channel mapping. For now we only support mapping
906 * from mono to stereo. For all other cases the core audio defaults
907 * are used, which means dropping additional channels in most
908 * cases.
909 */
910 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo, */
911
912 err = AudioConverterSetProperty(pStreamIn->pConverter, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
913 if (err != noErr)
914 {
915 LogRel(("CoreAudio: Failed to set channel mapping (mono -> stereo) for the audio input converter (%RI32)\n", err));
916 return VERR_AUDIO_BACKEND_INIT_FAILED;
917 }
918 }
919#if 0
920 /* Set sample rate converter quality to maximum */
921 uFlag = kAudioConverterQuality_Max;
922 err = AudioConverterSetProperty(pStreamIn->converter, kAudioConverterSampleRateConverterQuality,
923 sizeof(uFlag), &uFlag);
924 if (err != noErr)
925 LogRel(("CoreAudio: Failed to set input audio converter quality to the maximum (%RI32)\n", err));
926#endif
927
928 /* Set the new format description for the stream. */
929 err = AudioUnitSetProperty(pStreamIn->audioUnit,
930 kAudioUnitProperty_StreamFormat,
931 kAudioUnitScope_Output,
932 1,
933 &pStreamIn->deviceFormat,
934 sizeof(pStreamIn->deviceFormat));
935 if (RT_UNLIKELY(err != noErr))
936 {
937 LogRel(("CoreAudio: Failed to set input stream output format (%RI32)\n", err));
938 return VERR_AUDIO_BACKEND_INIT_FAILED;
939 }
940
941 err = AudioUnitSetProperty(pStreamIn->audioUnit,
942 kAudioUnitProperty_StreamFormat,
943 kAudioUnitScope_Input,
944 1,
945 &pStreamIn->deviceFormat,
946 sizeof(pStreamIn->deviceFormat));
947 if (RT_UNLIKELY(err != noErr))
948 {
949 LogRel(("CoreAudio: Failed to set stream input format (%RI32)\n", err));
950 return VERR_AUDIO_BACKEND_INIT_FAILED;
951 }
952 }
953 else
954 {
955
956 /* Set the new output format description for the input stream. */
957 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
958 1, &pStreamIn->streamFormat, sizeof(pStreamIn->streamFormat));
959 if (err != noErr)
960 {
961 LogRel(("CoreAudio: Failed to set output format for input stream (%RI32)\n", err));
962 return VERR_AUDIO_BACKEND_INIT_FAILED;
963 }
964 }
965
966 /*
967 * Also set the frame buffer size off the device on our AudioUnit. This
968 * should make sure that the frames count which we receive in the render
969 * thread is as we like.
970 */
971 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
972 1, &cFrames, sizeof(cFrames));
973 if (err != noErr) {
974 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err));
975 return VERR_AUDIO_BACKEND_INIT_FAILED;
976 }
977
978 /* Finally initialize the new AudioUnit. */
979 err = AudioUnitInitialize(pStreamIn->audioUnit);
980 if (err != noErr)
981 {
982 LogRel(("CoreAudio: Failed to initialize audio unit for input stream (%RI32)\n", err));
983 return VERR_AUDIO_BACKEND_INIT_FAILED;
984 }
985
986 uSize = sizeof(pStreamIn->deviceFormat);
987 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
988 1, &pStreamIn->deviceFormat, &uSize);
989 if (err != noErr)
990 {
991 LogRel(("CoreAudio: Failed to get input device format (%RI32)\n", err));
992 return VERR_AUDIO_BACKEND_INIT_FAILED;
993 }
994
995 /*
996 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
997 * the frame buffer size set in the previous calls. So finally get the
998 * frame buffer size after the AudioUnit was initialized.
999 */
1000 uSize = sizeof(cFrames);
1001 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1002 0, &cFrames, &uSize);
1003 if (err != noErr)
1004 {
1005 LogRel(("CoreAudio: Failed to get maximum frame buffer size from input audio device (%RI32)\n", err));
1006 return VERR_AUDIO_BACKEND_INIT_FAILED;
1007 }
1008
1009 /* Destroy any former internal ring buffer. */
1010 if (pStreamIn->pBuf)
1011 {
1012 RTCircBufDestroy(pStreamIn->pBuf);
1013 pStreamIn->pBuf = NULL;
1014 }
1015
1016 /* Calculate the ratio between the device and the stream sample rate. */
1017 pStreamIn->sampleRatio = pStreamIn->streamFormat.mSampleRate / pStreamIn->deviceFormat.mSampleRate;
1018
1019 /* Create the AudioBufferList structure with one buffer. */
1020 pStreamIn->bufferList.mNumberBuffers = 1;
1021 /* Initialize the buffer to nothing. */
1022 pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
1023 pStreamIn->bufferList.mBuffers[0].mDataByteSize = 0;
1024 pStreamIn->bufferList.mBuffers[0].mData = NULL;
1025
1026 int rc = VINF_SUCCESS;
1027
1028 /*
1029 * Make sure that the ring buffer is big enough to hold the recording
1030 * data. Compare the maximum frames per slice value with the frames
1031 * necessary when using the converter where the sample rate could differ.
1032 * The result is always multiplied by the channels per frame to get the
1033 * samples count.
1034 */
1035 UInt32 cSamples = RT_MAX(cFrames,
1036 (cFrames * pStreamIn->deviceFormat.mBytesPerFrame * pStreamIn->sampleRatio)
1037 / pStreamIn->streamFormat.mBytesPerFrame)
1038 * pStreamIn->streamFormat.mChannelsPerFrame;
1039 if (!cSamples)
1040 {
1041 LogRel(("CoreAudio: Failed to determine samples buffer count input stream\n"));
1042 rc = VERR_INVALID_PARAMETER;
1043 }
1044
1045 /* Create the internal ring buffer. */
1046 if (RT_SUCCESS(rc))
1047 rc = RTCircBufCreate(&pStreamIn->pBuf, cSamples << pStream->Props.cShift);
1048 if (RT_SUCCESS(rc))
1049 {
1050#ifdef DEBUG
1051 propAdr.mSelector = kAudioDeviceProcessorOverload;
1052 propAdr.mScope = kAudioUnitScope_Global;
1053 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
1054 coreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
1055 if (RT_UNLIKELY(err != noErr))
1056 LogRel(("CoreAudio: Failed to add the processor overload listener for input stream (%RI32)\n", err));
1057#endif /* DEBUG */
1058 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1059 propAdr.mScope = kAudioUnitScope_Global;
1060 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
1061 coreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
1062 /* Not fatal. */
1063 if (RT_UNLIKELY(err != noErr))
1064 LogRel(("CoreAudio: Failed to register sample rate changed listener for input stream (%RI32)\n", err));
1065 }
1066
1067 if (RT_SUCCESS(rc))
1068 {
1069 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_INIT);
1070
1071 if (pcSamples)
1072 *pcSamples = cSamples;
1073 }
1074 else
1075 {
1076 AudioUnitUninitialize(pStreamIn->audioUnit);
1077
1078 if (pStreamIn->pBuf)
1079 {
1080 RTCircBufDestroy(pStreamIn->pBuf);
1081 pStreamIn->pBuf = NULL;
1082 }
1083 }
1084
1085 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
1086 return rc;
1087}
1088
1089/** @todo Eventually split up this function, as this already is huge! */
1090static int coreAudioInitOut(PPDMAUDIOSTREAM pStream, uint32_t *pcSamples)
1091{
1092 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1093
1094 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_INIT);
1095
1096 OSStatus err = noErr;
1097
1098 UInt32 uSize = 0;
1099 if (pStreamOut->deviceID == kAudioDeviceUnknown)
1100 {
1101 /* Fetch the default audio input device currently in use. */
1102 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
1103 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1104 uSize = sizeof(pStreamOut->deviceID);
1105 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &pStreamOut->deviceID);
1106 if (err != noErr)
1107 {
1108 LogRel(("CoreAudio: Unable to determine default output device (%RI32)\n", err));
1109 return VERR_NOT_FOUND;
1110 }
1111 }
1112
1113 /*
1114 * Try to get the name of the output device and log it. It's not fatal if it fails.
1115 */
1116 CFStringRef strTemp;
1117
1118 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
1119 kAudioObjectPropertyElementMaster };
1120 uSize = sizeof(CFStringRef);
1121 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1122 if (err == noErr)
1123 {
1124 char *pszDevName = NULL;
1125 err = coreAudioCFStringToCString(strTemp, &pszDevName);
1126 if (err == noErr)
1127 {
1128 CFRelease(strTemp);
1129
1130 /* Get the device' UUID. */
1131 propAdr.mSelector = kAudioDevicePropertyDeviceUID;
1132 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1133 if (err == noErr)
1134 {
1135 char *pszUID = NULL;
1136 err = coreAudioCFStringToCString(strTemp, &pszUID);
1137 if (err == noErr)
1138 {
1139 CFRelease(strTemp);
1140 LogRel(("CoreAudio: Using output device: %s (UID: %s)\n", pszDevName, pszUID));
1141
1142 RTMemFree(pszUID);
1143 }
1144 }
1145
1146 RTMemFree(pszDevName);
1147 }
1148 }
1149 else
1150 LogRel(("CoreAudio: Unable to determine output device name (%RI32)\n", err));
1151
1152 /* Get the default frames buffer size, so that we can setup our internal buffers. */
1153 UInt32 cFrames;
1154 uSize = sizeof(cFrames);
1155 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
1156 propAdr.mScope = kAudioDevicePropertyScopeInput;
1157 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
1158 if (err != noErr)
1159 {
1160 LogRel(("CoreAudio: Failed to determine frame buffer size of the audio output device (%RI32)\n", err));
1161 return VERR_AUDIO_BACKEND_INIT_FAILED;
1162 }
1163
1164 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
1165 err = coreAudioSetFrameBufferSize(pStreamOut->deviceID, false /* fInput */, cFrames, &cFrames);
1166 if (err != noErr)
1167 {
1168 LogRel(("CoreAudio: Failed to set frame buffer size for the audio output device (%RI32)\n", err));
1169 return VERR_AUDIO_BACKEND_INIT_FAILED;
1170 }
1171
1172 ComponentDescription cd;
1173 RT_ZERO(cd);
1174 cd.componentType = kAudioUnitType_Output;
1175 cd.componentSubType = kAudioUnitSubType_HALOutput;
1176 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
1177
1178 /* Try to find the default HAL output component. */
1179 Component cp = FindNextComponent(NULL, &cd);
1180 if (cp == 0)
1181 {
1182 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
1183 return VERR_AUDIO_BACKEND_INIT_FAILED;
1184 }
1185
1186 /* Open the default HAL output component. */
1187 err = OpenAComponent(cp, &pStreamOut->audioUnit);
1188 if (err != noErr)
1189 {
1190 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
1191 return VERR_AUDIO_BACKEND_INIT_FAILED;
1192 }
1193
1194 /* Switch the I/O mode for output to on. */
1195 UInt32 uFlag = 1;
1196 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
1197 0, &uFlag, sizeof(uFlag));
1198 if (err != noErr)
1199 {
1200 LogRel(("CoreAudio: Failed to disable I/O mode for output stream (%RI32)\n", err));
1201 return VERR_AUDIO_BACKEND_INIT_FAILED;
1202 }
1203
1204 /* Set the default audio output device as the device for the new AudioUnit. */
1205 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
1206 0, &pStreamOut->deviceID, sizeof(pStreamOut->deviceID));
1207 if (err != noErr)
1208 {
1209 LogRel(("CoreAudio: Failed to set current device for output stream (%RI32)\n", err));
1210 return VERR_AUDIO_BACKEND_INIT_FAILED;
1211 }
1212
1213 /*
1214 * CoreAudio will inform us on a second thread for new incoming audio data.
1215 * Therefor register a callback function which will process the new data.
1216 */
1217 AURenderCallbackStruct cb;
1218 RT_ZERO(cb);
1219 cb.inputProc = coreAudioPlaybackCb; /* pvUser */
1220 cb.inputProcRefCon = pStreamOut;
1221
1222 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1223 0, &cb, sizeof(cb));
1224 if (err != noErr)
1225 {
1226 LogRel(("CoreAudio: Failed to register output callback (%RI32)\n", err));
1227 return VERR_AUDIO_BACKEND_INIT_FAILED;
1228 }
1229
1230 /* Fetch the current stream format of the device. */
1231 uSize = sizeof(pStreamOut->deviceFormat);
1232 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1233 0, &pStreamOut->deviceFormat, &uSize);
1234 if (err != noErr)
1235 {
1236 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
1237 return VERR_AUDIO_BACKEND_INIT_FAILED;
1238 }
1239
1240 /* Create an AudioStreamBasicDescription based on our required audio settings. */
1241 coreAudioPCMInfoToASBDesc(&pStreamOut->Stream.Props, &pStreamOut->streamFormat);
1242
1243 coreAudioPrintASBDesc("CoreAudio: Output device", &pStreamOut->deviceFormat);
1244 coreAudioPrintASBDesc("CoreAudio: Output format", &pStreamOut->streamFormat);
1245
1246 /* Set the new output format description for the stream. */
1247 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1248 0, &pStreamOut->streamFormat, sizeof(pStreamOut->streamFormat));
1249 if (err != noErr)
1250 {
1251 LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err));
1252 return VERR_AUDIO_BACKEND_INIT_FAILED;
1253 }
1254
1255 uSize = sizeof(pStreamOut->deviceFormat);
1256 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1257 0, &pStreamOut->deviceFormat, &uSize);
1258 if (err != noErr)
1259 {
1260 LogRel(("CoreAudio: Failed to retrieve device format for output stream (%RI32)\n", err));
1261 return VERR_AUDIO_BACKEND_INIT_FAILED;
1262 }
1263
1264 /*
1265 * Also set the frame buffer size off the device on our AudioUnit. This
1266 * should make sure that the frames count which we receive in the render
1267 * thread is as we like.
1268 */
1269 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1270 0, &cFrames, sizeof(cFrames));
1271 if (err != noErr)
1272 {
1273 LogRel(("CoreAudio: Failed to set maximum frame buffer size for output AudioUnit (%RI32)\n", err));
1274 return VERR_AUDIO_BACKEND_INIT_FAILED;
1275 }
1276
1277 /* Finally initialize the new AudioUnit. */
1278 err = AudioUnitInitialize(pStreamOut->audioUnit);
1279 if (err != noErr)
1280 {
1281 LogRel(("CoreAudio: Failed to initialize the output audio device (%RI32)\n", err));
1282 return VERR_AUDIO_BACKEND_INIT_FAILED;
1283 }
1284
1285 /*
1286 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
1287 * the frame buffer size set in the previous calls. So finally get the
1288 * frame buffer size after the AudioUnit was initialized.
1289 */
1290 uSize = sizeof(cFrames);
1291 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1292 0, &cFrames, &uSize);
1293 if (err != noErr)
1294 {
1295 LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err));
1296
1297 AudioUnitUninitialize(pStreamOut->audioUnit);
1298 return VERR_AUDIO_BACKEND_INIT_FAILED;
1299 }
1300
1301 /*
1302 * Make sure that the ring buffer is big enough to hold the recording
1303 * data. Compare the maximum frames per slice value with the frames
1304 * necessary when using the converter where the sample rate could differ.
1305 * The result is always multiplied by the channels per frame to get the
1306 * samples count.
1307 */
1308 int rc = VINF_SUCCESS;
1309
1310 UInt32 cSamples = cFrames * pStreamOut->streamFormat.mChannelsPerFrame;
1311 if (!cSamples)
1312 {
1313 LogRel(("CoreAudio: Failed to determine samples buffer count output stream\n"));
1314 rc = VERR_INVALID_PARAMETER;
1315 }
1316
1317 /* Destroy any former internal ring buffer. */
1318 if (pStreamOut->pBuf)
1319 {
1320 RTCircBufDestroy(pStreamOut->pBuf);
1321 pStreamOut->pBuf = NULL;
1322 }
1323
1324 /* Create the internal ring buffer. */
1325 rc = RTCircBufCreate(&pStreamOut->pBuf, cSamples << pStream->Props.cShift);
1326 if (RT_SUCCESS(rc))
1327 {
1328 /*
1329 * Register callbacks.
1330 */
1331#ifdef DEBUG
1332 propAdr.mSelector = kAudioDeviceProcessorOverload;
1333 propAdr.mScope = kAudioUnitScope_Global;
1334 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1335 coreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1336 if (err != noErr)
1337 LogRel(("CoreAudio: Failed to register processor overload listener for output stream (%RI32)\n", err));
1338#endif /* DEBUG */
1339
1340 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1341 propAdr.mScope = kAudioUnitScope_Global;
1342 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1343 coreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1344 /* Not fatal. */
1345 if (err != noErr)
1346 LogRel(("CoreAudio: Failed to register sample rate changed listener for output stream (%RI32)\n", err));
1347 }
1348
1349 if (RT_SUCCESS(rc))
1350 {
1351 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_INIT);
1352
1353 if (pcSamples)
1354 *pcSamples = cSamples;
1355 }
1356 else
1357 {
1358 AudioUnitUninitialize(pStreamOut->audioUnit);
1359
1360 if (pStreamOut->pBuf)
1361 {
1362 RTCircBufDestroy(pStreamOut->pBuf);
1363 pStreamOut->pBuf = NULL;
1364 }
1365 }
1366
1367 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
1368 return rc;
1369}
1370
1371
1372/* Callback for getting notified when some of the properties of an audio device has changed. */
1373static DECLCALLBACK(OSStatus) coreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID,
1374 UInt32 nAddresses,
1375 const AudioObjectPropertyAddress properties[],
1376 void *pvUser)
1377{
1378 switch (propertyID)
1379 {
1380#ifdef DEBUG
1381 case kAudioDeviceProcessorOverload:
1382 {
1383 Log2(("CoreAudio: [Output] Processor overload detected!\n"));
1384 break;
1385 }
1386#endif /* DEBUG */
1387 default:
1388 break;
1389 }
1390
1391 return noErr;
1392}
1393
1394/* Callback to feed audio output buffer. */
1395static DECLCALLBACK(OSStatus) coreAudioPlaybackCb(void *pvUser,
1396 AudioUnitRenderActionFlags *pActionFlags,
1397 const AudioTimeStamp *pAudioTS,
1398 UInt32 uBusID,
1399 UInt32 cFrames,
1400 AudioBufferList *pBufData)
1401{
1402 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pvUser;
1403 PPDMAUDIOSTREAM pStream = &pStreamOut->Stream;
1404
1405 if (ASMAtomicReadU32(&pStreamOut->status) != CA_STATUS_INIT)
1406 {
1407 pBufData->mBuffers[0].mDataByteSize = 0;
1408 return noErr;
1409 }
1410
1411 /* How much space is used in the ring buffer? */
1412 size_t cbDataAvail = RT_MIN(RTCircBufUsed(pStreamOut->pBuf), pBufData->mBuffers[0].mDataByteSize);
1413 if (!cbDataAvail)
1414 {
1415 pBufData->mBuffers[0].mDataByteSize = 0;
1416 return noErr;
1417 }
1418
1419 uint8_t *pbSrc = NULL;
1420 size_t cbRead = 0;
1421 size_t cbToRead;
1422 while (cbDataAvail)
1423 {
1424 /* Try to acquire the necessary block from the ring buffer. */
1425 RTCircBufAcquireReadBlock(pStreamOut->pBuf, cbDataAvail, (void **)&pbSrc, &cbToRead);
1426
1427 /* Break if nothing is used anymore. */
1428 if (!cbToRead)
1429 break;
1430
1431 /* Copy the data from our ring buffer to the core audio buffer. */
1432 memcpy((uint8_t *)pBufData->mBuffers[0].mData + cbRead, pbSrc, cbToRead);
1433
1434 /* Release the read buffer, so it could be used for new data. */
1435 RTCircBufReleaseReadBlock(pStreamOut->pBuf, cbToRead);
1436
1437 /* Move offset. */
1438 cbRead += cbToRead;
1439 Assert(pBufData->mBuffers[0].mDataByteSize >= cbRead);
1440
1441 Assert(cbToRead <= cbDataAvail);
1442 cbDataAvail -= cbToRead;
1443 }
1444
1445 /* Write the bytes to the core audio buffer which where really written. */
1446 pBufData->mBuffers[0].mDataByteSize = cbRead;
1447
1448 LogFlowFunc(("CoreAudio: [Output] Read %zu / %zu bytes\n", cbRead, cbDataAvail));
1449
1450 return noErr;
1451}
1452
1453static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)
1454{
1455 NOREF(pInterface);
1456
1457 LogFlowFuncEnter();
1458
1459 return VINF_SUCCESS;
1460}
1461
1462static DECLCALLBACK(int) drvHostCoreAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1463 uint32_t *pcSamplesCaptured)
1464{
1465 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1466 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1467
1468 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1469
1470 size_t csReads = 0;
1471 char *pcSrc;
1472 PPDMAUDIOSAMPLE psDst;
1473
1474 /* Check if the audio device should be reinitialized. If so do it. */
1475 if (ASMAtomicReadU32(&pStreamIn->status) == CA_STATUS_REINIT)
1476 coreAudioReinitIn(pInterface, &pStreamIn->Stream);
1477
1478 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
1479 {
1480 if (pcSamplesCaptured)
1481 *pcSamplesCaptured = 0;
1482 return VINF_SUCCESS;
1483 }
1484
1485 int rc = VINF_SUCCESS;
1486 uint32_t cbWrittenTotal = 0;
1487
1488 do
1489 {
1490 size_t cbBuf = AudioMixBufSizeBytes(&pStream->MixBuf);
1491 size_t cbToWrite = RT_MIN(cbBuf, RTCircBufUsed(pStreamIn->pBuf));
1492
1493 uint32_t cWritten, cbWritten;
1494 uint8_t *puBuf;
1495 size_t cbToRead;
1496
1497 LogFlowFunc(("cbBuf=%zu, cbToWrite=%zu\n", cbBuf, cbToWrite));
1498
1499 while (cbToWrite)
1500 {
1501 /* Try to acquire the necessary block from the ring buffer. */
1502 RTCircBufAcquireReadBlock(pStreamIn->pBuf, cbToWrite, (void **)&puBuf, &cbToRead);
1503 if (!cbToRead)
1504 {
1505 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbToRead);
1506 break;
1507 }
1508
1509 rc = AudioMixBufWriteCirc(&pStream->MixBuf, puBuf, cbToRead, &cWritten);
1510 if ( RT_FAILURE(rc)
1511 || !cWritten)
1512 {
1513 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbToRead);
1514 break;
1515 }
1516
1517 cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
1518
1519 /* Release the read buffer, so it could be used for new data. */
1520 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbWritten);
1521
1522 Assert(cbToWrite >= cbWritten);
1523 cbToWrite -= cbWritten;
1524 cbWrittenTotal += cbWritten;
1525 }
1526
1527 LogFlowFunc(("cbToWrite=%zu, cbToRead=%zu, cbWrittenTotal=%RU32, rc=%Rrc\n", cbToWrite, cbToRead, cbWrittenTotal, rc));
1528 }
1529 while (0);
1530
1531 if (RT_SUCCESS(rc))
1532 {
1533 uint32_t cCaptured = 0;
1534 uint32_t cWrittenTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbWrittenTotal);
1535 if (cWrittenTotal)
1536 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal, &cCaptured);
1537
1538 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 bytes), cCaptured=%RU32, rc=%Rrc\n", cWrittenTotal, cbWrittenTotal, cCaptured, rc));
1539
1540 if (pcSamplesCaptured)
1541 *pcSamplesCaptured = cCaptured;
1542 }
1543
1544 LogFlowFuncLeaveRC(rc);
1545 return rc;
1546}
1547
1548static DECLCALLBACK(int) drvHostCoreAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1549 uint32_t *pcSamplesPlayed)
1550{
1551 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1552 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1553
1554 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1555
1556 int rc = VINF_SUCCESS;
1557
1558 /* Check if the audio device should be reinitialized. If so do it. */
1559 if (ASMAtomicReadU32(&pStreamOut->status) == CA_STATUS_REINIT)
1560 {
1561 rc = coreAudioReinitOut(pInterface, &pStreamOut->Stream);
1562 if (RT_FAILURE(rc))
1563 return rc;
1564 }
1565
1566 /* Not much else to do here. */
1567
1568 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
1569 if (!cLive) /* Not live samples to play? Bail out. */
1570 {
1571 if (pcSamplesPlayed)
1572 *pcSamplesPlayed = 0;
1573 return VINF_SUCCESS;
1574 }
1575
1576 size_t cbLive = AUDIOMIXBUF_S2B(&pStream->MixBuf, cLive);
1577
1578 uint32_t cbReadTotal = 0;
1579
1580 size_t cbToRead = RT_MIN(cbLive, RTCircBufFree(pStreamOut->pBuf));
1581 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
1582
1583 while (cbToRead)
1584 {
1585 uint32_t cRead, cbRead;
1586 uint8_t *puBuf;
1587 size_t cbCopy;
1588
1589 /* Try to acquire the necessary space from the ring buffer. */
1590 RTCircBufAcquireWriteBlock(pStreamOut->pBuf, cbToRead, (void **)&puBuf, &cbCopy);
1591 if (!cbCopy)
1592 {
1593 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbCopy);
1594 break;
1595 }
1596
1597 Assert(cbCopy <= cbToRead);
1598
1599 rc = AudioMixBufReadCirc(&pStream->MixBuf,
1600 puBuf, cbCopy, &cRead);
1601
1602 if ( RT_FAILURE(rc)
1603 || !cRead)
1604 {
1605 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, 0);
1606 break;
1607 }
1608
1609 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
1610
1611 /* Release the ring buffer, so the read thread could start reading this data. */
1612 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbRead);
1613
1614 Assert(cbToRead >= cbRead);
1615 cbToRead -= cbRead;
1616 cbReadTotal += cbRead;
1617 }
1618
1619 if (RT_SUCCESS(rc))
1620 {
1621 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
1622 if (cReadTotal)
1623 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
1624
1625 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes)\n", cReadTotal, cbReadTotal));
1626
1627 if (pcSamplesPlayed)
1628 *pcSamplesPlayed = cReadTotal;
1629 }
1630
1631 return rc;
1632}
1633
1634static DECLCALLBACK(int) coreAudioControlStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1635 PDMAUDIOSTREAMCMD enmStreamCmd)
1636{
1637 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1638
1639 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1640
1641 uint32_t uStatus = ASMAtomicReadU32(&pStreamOut->status);
1642 if (!( uStatus == CA_STATUS_INIT
1643 || uStatus == CA_STATUS_REINIT))
1644 {
1645 return VINF_SUCCESS;
1646 }
1647
1648 int rc = VINF_SUCCESS;
1649 OSStatus err;
1650
1651 switch (enmStreamCmd)
1652 {
1653 case PDMAUDIOSTREAMCMD_ENABLE:
1654 case PDMAUDIOSTREAMCMD_RESUME:
1655 {
1656 /* Only start the device if it is actually stopped */
1657 if (!coreAudioIsRunning(pStreamOut->deviceID))
1658 {
1659 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1660 if (err != noErr)
1661 {
1662 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1663 /* Keep going. */
1664 }
1665 RTCircBufReset(pStreamOut->pBuf);
1666
1667 err = AudioOutputUnitStart(pStreamOut->audioUnit);
1668 if (RT_UNLIKELY(err != noErr))
1669 {
1670 LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));
1671 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1672 }
1673 }
1674 break;
1675 }
1676
1677 case PDMAUDIOSTREAMCMD_DISABLE:
1678 case PDMAUDIOSTREAMCMD_PAUSE:
1679 {
1680 /* Only stop the device if it is actually running */
1681 if (coreAudioIsRunning(pStreamOut->deviceID))
1682 {
1683 err = AudioOutputUnitStop(pStreamOut->audioUnit);
1684 if (err != noErr)
1685 {
1686 LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
1687 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1688 break;
1689 }
1690
1691 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1692 if (err != noErr)
1693 {
1694 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1695 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1696 }
1697 }
1698 break;
1699 }
1700
1701 default:
1702 rc = VERR_NOT_SUPPORTED;
1703 break;
1704 }
1705
1706 LogFlowFuncLeaveRC(rc);
1707 return rc;
1708}
1709
1710static int coreAudioControlStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1711 PDMAUDIOSTREAMCMD enmStreamCmd)
1712{
1713 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1714
1715 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1716
1717 uint32_t uStatus = ASMAtomicReadU32(&pStreamIn->status);
1718 if (!( uStatus == CA_STATUS_INIT
1719 || uStatus == CA_STATUS_REINIT))
1720 {
1721 return VINF_SUCCESS;
1722 }
1723
1724 int rc = VINF_SUCCESS;
1725 OSStatus err = noErr;
1726
1727 switch (enmStreamCmd)
1728 {
1729 case PDMAUDIOSTREAMCMD_ENABLE:
1730 case PDMAUDIOSTREAMCMD_RESUME:
1731 {
1732 /* Only start the device if it is actually stopped */
1733 if (!coreAudioIsRunning(pStreamIn->deviceID))
1734 {
1735 RTCircBufReset(pStreamIn->pBuf);
1736 err = AudioOutputUnitStart(pStreamIn->audioUnit);
1737 if (err != noErr)
1738 {
1739 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1740 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1741 break;
1742 }
1743 }
1744
1745 if (err != noErr)
1746 {
1747 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1748 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1749 }
1750 break;
1751 }
1752
1753 case PDMAUDIOSTREAMCMD_DISABLE:
1754 case PDMAUDIOSTREAMCMD_PAUSE:
1755 {
1756 /* Only stop the device if it is actually running */
1757 if (coreAudioIsRunning(pStreamIn->deviceID))
1758 {
1759 err = AudioOutputUnitStop(pStreamIn->audioUnit);
1760 if (err != noErr)
1761 {
1762 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
1763 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1764 break;
1765 }
1766
1767 err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);
1768 if (err != noErr)
1769 {
1770 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1771 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1772 break;
1773 }
1774 }
1775 break;
1776 }
1777
1778 default:
1779 rc = VERR_NOT_SUPPORTED;
1780 break;
1781 }
1782
1783 LogFlowFuncLeaveRC(rc);
1784 return rc;
1785}
1786
1787static int coreAudioDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1788{
1789 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN) pStream;
1790
1791 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1792 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1793
1794 LogFlowFuncEnter();
1795
1796 uint32_t status = ASMAtomicReadU32(&pStreamIn->status);
1797 if (!( status == CA_STATUS_INIT
1798 || status == CA_STATUS_REINIT))
1799 {
1800 return VINF_SUCCESS;
1801 }
1802
1803 OSStatus err = noErr;
1804
1805 int rc = coreAudioControlStreamIn(pInterface, &pStreamIn->Stream, PDMAUDIOSTREAMCMD_DISABLE);
1806 if (RT_SUCCESS(rc))
1807 {
1808 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);
1809
1810 /*
1811 * Unregister input device callbacks.
1812 */
1813 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
1814 kAudioObjectPropertyElementMaster };
1815#ifdef DEBUG
1816 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
1817 coreAudioRecordingAudioDevicePropertyChanged, pStreamIn);
1818 /* Not Fatal */
1819 if (RT_UNLIKELY(err != noErr))
1820 LogRel(("CoreAudio: Failed to remove the processor overload listener (%RI32)\n", err));
1821#endif /* DEBUG */
1822
1823 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1824 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
1825 coreAudioRecordingAudioDevicePropertyChanged, pStreamIn);
1826 /* Not Fatal */
1827 if (RT_UNLIKELY(err != noErr))
1828 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
1829
1830 if (pStreamIn->fDefDevChgListReg)
1831 {
1832 propAdr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
1833 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
1834 coreAudioDefaultDeviceChanged, pStreamIn);
1835 if (RT_LIKELY(err == noErr))
1836 {
1837 pStreamIn->fDefDevChgListReg = false;
1838 }
1839 else
1840 LogRel(("CoreAudio: [Output] Failed to remove the default input device changed listener (%RI32)\n", err));
1841 }
1842
1843 if (pStreamIn->pConverter)
1844 {
1845 AudioConverterDispose(pStreamIn->pConverter);
1846 pStreamIn->pConverter = NULL;
1847 }
1848
1849 err = AudioUnitUninitialize(pStreamIn->audioUnit);
1850 if (RT_LIKELY(err == noErr))
1851 {
1852 err = CloseComponent(pStreamIn->audioUnit);
1853 if (RT_LIKELY(err == noErr))
1854 {
1855 pStreamIn->deviceID = kAudioDeviceUnknown;
1856 pStreamIn->audioUnit = NULL;
1857 pStreamIn->offBufferRead = 0;
1858 pStreamIn->sampleRatio = 1;
1859 if (pStreamIn->pBuf)
1860 {
1861 RTCircBufDestroy(pStreamIn->pBuf);
1862 pStreamIn->pBuf = NULL;
1863 }
1864
1865 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
1866 }
1867 else
1868 {
1869 LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
1870 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1871 }
1872 }
1873 else
1874 {
1875 LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
1876 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1877 }
1878 }
1879 else
1880 {
1881 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
1882 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1883 }
1884
1885 LogFlowFuncLeaveRC(rc);
1886 return rc;
1887}
1888
1889static int coreAudioDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1890{
1891 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1892 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1893
1894 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1895
1896 LogFlowFuncEnter();
1897
1898 uint32_t status = ASMAtomicReadU32(&pStreamOut->status);
1899 if (!( status == CA_STATUS_INIT
1900 || status == CA_STATUS_REINIT))
1901 {
1902 return VINF_SUCCESS;
1903 }
1904
1905 int rc = coreAudioControlStreamOut(pInterface, &pStreamOut->Stream, PDMAUDIOSTREAMCMD_DISABLE);
1906 if (RT_SUCCESS(rc))
1907 {
1908 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_UNINIT);
1909
1910 OSStatus err;
1911
1912 /*
1913 * Unregister playback device callbacks.
1914 */
1915 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
1916 kAudioObjectPropertyElementMaster };
1917#ifdef DEBUG
1918 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
1919 coreAudioPlaybackAudioDevicePropertyChanged, pStreamOut);
1920 /* Not Fatal */
1921 if (RT_UNLIKELY(err != noErr))
1922 LogRel(("CoreAudio: Failed to remove the processor overload listener (%RI32)\n", err));
1923#endif /* DEBUG */
1924
1925 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1926 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
1927 coreAudioPlaybackAudioDevicePropertyChanged, pStreamOut);
1928 /* Not Fatal */
1929 if (RT_UNLIKELY(err != noErr))
1930 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
1931
1932 if (pStreamOut->fDefDevChgListReg)
1933 {
1934 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
1935 propAdr.mScope = kAudioObjectPropertyScopeGlobal;
1936 propAdr.mElement = kAudioObjectPropertyElementMaster;
1937 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
1938 coreAudioDefaultDeviceChanged, pStreamOut);
1939 if (RT_LIKELY(err == noErr))
1940 {
1941 pStreamOut->fDefDevChgListReg = false;
1942 }
1943 else
1944 LogRel(("CoreAudio: [Output] Failed to remove the default playback device changed listener (%RI32)\n", err));
1945 }
1946
1947 err = AudioUnitUninitialize(pStreamOut->audioUnit);
1948 if (err == noErr)
1949 {
1950 err = CloseComponent(pStreamOut->audioUnit);
1951 if (err == noErr)
1952 {
1953 pStreamOut->deviceID = kAudioDeviceUnknown;
1954 pStreamOut->audioUnit = NULL;
1955 if (pStreamOut->pBuf)
1956 {
1957 RTCircBufDestroy(pStreamOut->pBuf);
1958 pStreamOut->pBuf = NULL;
1959 }
1960
1961 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
1962 }
1963 else
1964 LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
1965 }
1966 else
1967 LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
1968 }
1969 else
1970 LogRel(("CoreAudio: Failed to stop playback, rc=%Rrc\n", rc));
1971
1972 LogFlowFuncLeaveRC(rc);
1973 return rc;
1974}
1975
1976static int coreAudioCreateStreamIn(PPDMIHOSTAUDIO pInterface,
1977 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
1978{
1979 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1980
1981 LogFlowFunc(("enmRecSource=%ld\n", pCfg->DestSource.Source));
1982
1983 pStreamIn->deviceID = kAudioDeviceUnknown;
1984 pStreamIn->audioUnit = NULL;
1985 pStreamIn->pConverter = NULL;
1986 pStreamIn->bufferList.mNumberBuffers = 0;
1987 pStreamIn->offBufferRead = 0;
1988 pStreamIn->sampleRatio = 1;
1989 pStreamIn->pBuf = NULL;
1990 pStreamIn->status = CA_STATUS_UNINIT;
1991 pStreamIn->fDefDevChgListReg = false;
1992
1993 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
1994
1995 /* Initialize the hardware info section with the audio settings */
1996 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStreamIn->Stream.Props);
1997 if (RT_SUCCESS(rc))
1998 {
1999#if 0
2000 /* Try to find the audio device set by the user */
2001 if (DeviceUID.pszInputDeviceUID)
2002 {
2003 pStreamIn->deviceID = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszInputDeviceUID);
2004 /* Not fatal */
2005 if (pStreamIn->deviceID == kAudioDeviceUnknown)
2006 LogRel(("CoreAudio: Unable to find input device %s. Falling back to the default audio device. \n", DeviceUID.pszInputDeviceUID));
2007 else
2008 fDeviceByUser = true;
2009 }
2010#endif
2011 rc = coreAudioInitIn(&pStreamIn->Stream, pcSamples);
2012 }
2013
2014 if (RT_SUCCESS(rc))
2015 {
2016 /* When the devices isn't forced by the user, we want default device change notifications. */
2017 if (!fDeviceByUser)
2018 {
2019 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
2020 kAudioObjectPropertyElementMaster };
2021 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2022 coreAudioDefaultDeviceChanged, (void *)pStreamIn);
2023 /* Not fatal. */
2024 if (RT_LIKELY(err == noErr))
2025 {
2026 pStreamIn->fDefDevChgListReg = true;
2027 }
2028 else
2029 LogRel(("CoreAudio: Failed to add the default input device changed listener (%RI32)\n", err));
2030 }
2031 }
2032
2033 LogFlowFuncLeaveRC(rc);
2034 return rc;
2035}
2036
2037static int coreAudioCreateStreamOut(PPDMIHOSTAUDIO pInterface,
2038 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg,
2039 uint32_t *pcSamples)
2040{
2041 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2042
2043 LogFlowFuncEnter();
2044
2045 pStreamOut->deviceID = kAudioDeviceUnknown;
2046 pStreamOut->audioUnit = NULL;
2047 pStreamOut->pBuf = NULL;
2048 pStreamOut->status = CA_STATUS_UNINIT;
2049 pStreamOut->fDefDevChgListReg = false;
2050
2051 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
2052
2053 /* Initialize the hardware info section with the audio settings */
2054 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStreamOut->Stream.Props);
2055 if (RT_SUCCESS(rc))
2056 {
2057#if 0
2058 /* Try to find the audio device set by the user. Use
2059 * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0
2060 * to set it. */
2061 if (DeviceUID.pszOutputDeviceUID)
2062 {
2063 pStreamOut->audioDeviceId = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszOutputDeviceUID);
2064 /* Not fatal */
2065 if (pStreamOut->audioDeviceId == kAudioDeviceUnknown)
2066 LogRel(("CoreAudio: Unable to find output device %s. Falling back to the default audio device. \n", DeviceUID.pszOutputDeviceUID));
2067 else
2068 fDeviceByUser = true;
2069 }
2070#endif
2071 rc = coreAudioInitOut(pStream, pcSamples);
2072 }
2073
2074 if (RT_SUCCESS(rc))
2075 {
2076 /* When the devices isn't forced by the user, we want default device change notifications. */
2077 if (!fDeviceByUser)
2078 {
2079 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
2080 kAudioObjectPropertyElementMaster };
2081 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2082 coreAudioDefaultDeviceChanged, (void *)pStreamOut);
2083 /* Not fatal. */
2084 if (RT_LIKELY(err == noErr))
2085 {
2086 pStreamOut->fDefDevChgListReg = true;
2087 }
2088 else
2089 LogRel(("CoreAudio: Failed to add the default output device changed listener (%RI32)\n", err));
2090 }
2091 }
2092
2093 LogFlowFuncLeaveRC(rc);
2094 return rc;
2095}
2096
2097static DECLCALLBACK(int) drvHostCoreAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
2098{
2099 NOREF(pInterface);
2100 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2101
2102 LogFlowFuncEnter();
2103
2104 pCfg->cbStreamIn = sizeof(COREAUDIOSTREAMIN);
2105 pCfg->cbStreamOut = sizeof(COREAUDIOSTREAMOUT);
2106 pCfg->cMaxStreamsIn = UINT32_MAX;
2107 pCfg->cMaxStreamsOut = UINT32_MAX;
2108
2109 /** @todo Implement a proper device detection. */
2110 pCfg->cSources = 1;
2111 pCfg->cSinks = 1;
2112
2113 return VINF_SUCCESS;
2114}
2115
2116static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2117{
2118 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2119
2120 return PDMAUDIOBACKENDSTS_RUNNING;
2121}
2122
2123static DECLCALLBACK(int) drvHostCoreAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
2124 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
2125{
2126 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2127 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2128 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2129
2130 int rc;
2131 if (pCfg->enmDir == PDMAUDIODIR_IN)
2132 rc = coreAudioCreateStreamIn(pInterface, pStream, pCfg, pcSamples);
2133 else
2134 rc = coreAudioCreateStreamOut(pInterface, pStream, pCfg, pcSamples);
2135
2136 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
2137 return rc;
2138}
2139
2140static DECLCALLBACK(int) drvHostCoreAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2141{
2142 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2143 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2144
2145 int rc;
2146 if (pStream->enmDir == PDMAUDIODIR_IN)
2147 rc = coreAudioDestroyStreamIn(pInterface, pStream);
2148 else
2149 rc = coreAudioDestroyStreamOut(pInterface, pStream);
2150
2151 return rc;
2152}
2153
2154static DECLCALLBACK(int) drvHostCoreAudioStreamControl(PPDMIHOSTAUDIO pInterface,
2155 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2156{
2157 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2158 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2159
2160 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
2161
2162 int rc;
2163 if (pStream->enmDir == PDMAUDIODIR_IN)
2164 rc = coreAudioControlStreamIn(pInterface, pStream, enmStreamCmd);
2165 else
2166 rc = coreAudioControlStreamOut(pInterface, pStream, enmStreamCmd);
2167
2168 return rc;
2169}
2170
2171static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostCoreAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2172{
2173 NOREF(pInterface);
2174 NOREF(pStream);
2175
2176 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_INITIALIZED
2177 | PDMAUDIOSTRMSTS_FLAG_ENABLED;
2178
2179 if (pStream->enmDir == PDMAUDIODIR_IN)
2180 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_READABLE;
2181 else
2182 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
2183
2184 return strmSts;
2185}
2186
2187static DECLCALLBACK(int) drvHostCoreAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2188{
2189 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2190 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2191
2192 LogFlowFuncEnter();
2193
2194 /* Nothing to do here for Core Audio. */
2195 return VINF_SUCCESS;
2196}
2197
2198static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
2199{
2200 NOREF(pInterface);
2201}
2202
2203static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2204{
2205 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2206 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2207
2208 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2209 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2210
2211 return NULL;
2212}
2213
2214 /* Construct a DirectSound Audio driver instance.
2215 *
2216 * @copydoc FNPDMDRVCONSTRUCT
2217 */
2218static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2219{
2220 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2221 LogRel(("Audio: Initializing Core Audio driver\n"));
2222
2223 /*
2224 * Init the static parts.
2225 */
2226 pThis->pDrvIns = pDrvIns;
2227 /* IBase */
2228 pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
2229 /* IHostAudio */
2230 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
2231
2232 return VINF_SUCCESS;
2233}
2234
2235/**
2236 * Char driver registration record.
2237 */
2238const PDMDRVREG g_DrvHostCoreAudio =
2239{
2240 /* u32Version */
2241 PDM_DRVREG_VERSION,
2242 /* szName */
2243 "CoreAudio",
2244 /* szRCMod */
2245 "",
2246 /* szR0Mod */
2247 "",
2248 /* pszDescription */
2249 "Core Audio host driver",
2250 /* fFlags */
2251 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2252 /* fClass. */
2253 PDM_DRVREG_CLASS_AUDIO,
2254 /* cMaxInstances */
2255 ~0U,
2256 /* cbInstance */
2257 sizeof(DRVHOSTCOREAUDIO),
2258 /* pfnConstruct */
2259 drvHostCoreAudioConstruct,
2260 /* pfnDestruct */
2261 NULL,
2262 /* pfnRelocate */
2263 NULL,
2264 /* pfnIOCtl */
2265 NULL,
2266 /* pfnPowerOn */
2267 NULL,
2268 /* pfnReset */
2269 NULL,
2270 /* pfnSuspend */
2271 NULL,
2272 /* pfnResume */
2273 NULL,
2274 /* pfnAttach */
2275 NULL,
2276 /* pfnDetach */
2277 NULL,
2278 /* pfnPowerOff */
2279 NULL,
2280 /* pfnSoftReset */
2281 NULL,
2282 /* u32EndVersion */
2283 PDM_DRVREG_VERSION
2284};
2285
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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