VirtualBox

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

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

Audio: Renaming.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.4 KB
Line 
1/* $Id: DrvHostCoreAudio.cpp 61167 2016-05-24 15:48:51Z vboxsync $ */
2/** @file
3 * VBox audio devices: Mac OS X CoreAudio audio driver.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
18#include <VBox/log.h>
19#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 = AudioMixBufAvail(&pStream->MixBuf);;
1564 if (!cLive) /* Not samples to play? Bail out. */
1565 {
1566 if (pcSamplesPlayed)
1567 *pcSamplesPlayed = 0;
1568 return VINF_SUCCESS;
1569 }
1570
1571 uint32_t cbReadTotal = 0;
1572 uint32_t cAvail = AudioMixBufAvail(&pStream->MixBuf);
1573 size_t cbAvail = AUDIOMIXBUF_S2B(&pStream->MixBuf, cAvail);
1574 size_t cbToRead = RT_MIN(cbAvail, RTCircBufFree(pStreamOut->pBuf));
1575 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
1576
1577 while (cbToRead)
1578 {
1579 uint32_t cRead, cbRead;
1580 uint8_t *puBuf;
1581 size_t cbCopy;
1582
1583 /* Try to acquire the necessary space from the ring buffer. */
1584 RTCircBufAcquireWriteBlock(pStreamOut->pBuf, cbToRead, (void **)&puBuf, &cbCopy);
1585 if (!cbCopy)
1586 {
1587 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbCopy);
1588 break;
1589 }
1590
1591 Assert(cbCopy <= cbToRead);
1592
1593 rc = AudioMixBufReadCirc(&pStream->MixBuf,
1594 puBuf, cbCopy, &cRead);
1595
1596 if ( RT_FAILURE(rc)
1597 || !cRead)
1598 {
1599 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, 0);
1600 break;
1601 }
1602
1603 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
1604
1605 /* Release the ring buffer, so the read thread could start reading this data. */
1606 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbRead);
1607
1608 Assert(cbToRead >= cbRead);
1609 cbToRead -= cbRead;
1610 cbReadTotal += cbRead;
1611 }
1612
1613 if (RT_SUCCESS(rc))
1614 {
1615 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
1616 if (cReadTotal)
1617 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
1618
1619 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes)\n", cReadTotal, cbReadTotal));
1620
1621 if (pcSamplesPlayed)
1622 *pcSamplesPlayed = cReadTotal;
1623 }
1624
1625 return rc;
1626}
1627
1628static DECLCALLBACK(int) coreAudioControlStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1629 PDMAUDIOSTREAMCMD enmStreamCmd)
1630{
1631 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1632
1633 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1634
1635 uint32_t uStatus = ASMAtomicReadU32(&pStreamOut->status);
1636 if (!( uStatus == CA_STATUS_INIT
1637 || uStatus == CA_STATUS_REINIT))
1638 {
1639 return VINF_SUCCESS;
1640 }
1641
1642 int rc = VINF_SUCCESS;
1643 OSStatus err;
1644
1645 switch (enmStreamCmd)
1646 {
1647 case PDMAUDIOSTREAMCMD_ENABLE:
1648 case PDMAUDIOSTREAMCMD_RESUME:
1649 {
1650 /* Only start the device if it is actually stopped */
1651 if (!coreAudioIsRunning(pStreamOut->deviceID))
1652 {
1653 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1654 if (err != noErr)
1655 {
1656 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1657 /* Keep going. */
1658 }
1659 RTCircBufReset(pStreamOut->pBuf);
1660
1661 err = AudioOutputUnitStart(pStreamOut->audioUnit);
1662 if (RT_UNLIKELY(err != noErr))
1663 {
1664 LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));
1665 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1666 }
1667 }
1668 break;
1669 }
1670
1671 case PDMAUDIOSTREAMCMD_DISABLE:
1672 case PDMAUDIOSTREAMCMD_PAUSE:
1673 {
1674 /* Only stop the device if it is actually running */
1675 if (coreAudioIsRunning(pStreamOut->deviceID))
1676 {
1677 err = AudioOutputUnitStop(pStreamOut->audioUnit);
1678 if (err != noErr)
1679 {
1680 LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
1681 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1682 break;
1683 }
1684
1685 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1686 if (err != noErr)
1687 {
1688 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1689 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1690 }
1691 }
1692 break;
1693 }
1694
1695 default:
1696 rc = VERR_NOT_SUPPORTED;
1697 break;
1698 }
1699
1700 LogFlowFuncLeaveRC(rc);
1701 return rc;
1702}
1703
1704static int coreAudioControlStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1705 PDMAUDIOSTREAMCMD enmStreamCmd)
1706{
1707 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1708
1709 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1710
1711 uint32_t uStatus = ASMAtomicReadU32(&pStreamIn->status);
1712 if (!( uStatus == CA_STATUS_INIT
1713 || uStatus == CA_STATUS_REINIT))
1714 {
1715 return VINF_SUCCESS;
1716 }
1717
1718 int rc = VINF_SUCCESS;
1719 OSStatus err = noErr;
1720
1721 switch (enmStreamCmd)
1722 {
1723 case PDMAUDIOSTREAMCMD_ENABLE:
1724 case PDMAUDIOSTREAMCMD_RESUME:
1725 {
1726 /* Only start the device if it is actually stopped */
1727 if (!coreAudioIsRunning(pStreamIn->deviceID))
1728 {
1729 RTCircBufReset(pStreamIn->pBuf);
1730 err = AudioOutputUnitStart(pStreamIn->audioUnit);
1731 if (err != noErr)
1732 {
1733 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1734 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1735 break;
1736 }
1737 }
1738
1739 if (err != noErr)
1740 {
1741 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1742 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1743 }
1744 break;
1745 }
1746
1747 case PDMAUDIOSTREAMCMD_DISABLE:
1748 case PDMAUDIOSTREAMCMD_PAUSE:
1749 {
1750 /* Only stop the device if it is actually running */
1751 if (coreAudioIsRunning(pStreamIn->deviceID))
1752 {
1753 err = AudioOutputUnitStop(pStreamIn->audioUnit);
1754 if (err != noErr)
1755 {
1756 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
1757 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1758 break;
1759 }
1760
1761 err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);
1762 if (err != noErr)
1763 {
1764 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1765 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1766 break;
1767 }
1768 }
1769 break;
1770 }
1771
1772 default:
1773 rc = VERR_NOT_SUPPORTED;
1774 break;
1775 }
1776
1777 LogFlowFuncLeaveRC(rc);
1778 return rc;
1779}
1780
1781static int coreAudioDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1782{
1783 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN) pStream;
1784
1785 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1786 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1787
1788 LogFlowFuncEnter();
1789
1790 uint32_t status = ASMAtomicReadU32(&pStreamIn->status);
1791 if (!( status == CA_STATUS_INIT
1792 || status == CA_STATUS_REINIT))
1793 {
1794 return VINF_SUCCESS;
1795 }
1796
1797 OSStatus err = noErr;
1798
1799 int rc = coreAudioControlStreamIn(pInterface, &pStreamIn->Stream, PDMAUDIOSTREAMCMD_DISABLE);
1800 if (RT_SUCCESS(rc))
1801 {
1802 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);
1803
1804 /*
1805 * Unregister input device callbacks.
1806 */
1807 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
1808 kAudioObjectPropertyElementMaster };
1809#ifdef DEBUG
1810 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
1811 coreAudioRecordingAudioDevicePropertyChanged, pStreamIn);
1812 /* Not Fatal */
1813 if (RT_UNLIKELY(err != noErr))
1814 LogRel(("CoreAudio: Failed to remove the processor overload listener (%RI32)\n", err));
1815#endif /* DEBUG */
1816
1817 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1818 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
1819 coreAudioRecordingAudioDevicePropertyChanged, pStreamIn);
1820 /* Not Fatal */
1821 if (RT_UNLIKELY(err != noErr))
1822 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
1823
1824 if (pStreamIn->fDefDevChgListReg)
1825 {
1826 propAdr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
1827 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
1828 coreAudioDefaultDeviceChanged, pStreamIn);
1829 if (RT_LIKELY(err == noErr))
1830 {
1831 pStreamIn->fDefDevChgListReg = false;
1832 }
1833 else
1834 LogRel(("CoreAudio: [Output] Failed to remove the default input device changed listener (%RI32)\n", err));
1835 }
1836
1837 if (pStreamIn->pConverter)
1838 {
1839 AudioConverterDispose(pStreamIn->pConverter);
1840 pStreamIn->pConverter = NULL;
1841 }
1842
1843 err = AudioUnitUninitialize(pStreamIn->audioUnit);
1844 if (RT_LIKELY(err == noErr))
1845 {
1846 err = CloseComponent(pStreamIn->audioUnit);
1847 if (RT_LIKELY(err == noErr))
1848 {
1849 pStreamIn->deviceID = kAudioDeviceUnknown;
1850 pStreamIn->audioUnit = NULL;
1851 pStreamIn->offBufferRead = 0;
1852 pStreamIn->sampleRatio = 1;
1853 if (pStreamIn->pBuf)
1854 {
1855 RTCircBufDestroy(pStreamIn->pBuf);
1856 pStreamIn->pBuf = NULL;
1857 }
1858
1859 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
1860 }
1861 else
1862 {
1863 LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
1864 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1865 }
1866 }
1867 else
1868 {
1869 LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
1870 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1871 }
1872 }
1873 else
1874 {
1875 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
1876 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1877 }
1878
1879 LogFlowFuncLeaveRC(rc);
1880 return rc;
1881}
1882
1883static int coreAudioDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1884{
1885 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1886 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1887
1888 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1889
1890 LogFlowFuncEnter();
1891
1892 uint32_t status = ASMAtomicReadU32(&pStreamOut->status);
1893 if (!( status == CA_STATUS_INIT
1894 || status == CA_STATUS_REINIT))
1895 {
1896 return VINF_SUCCESS;
1897 }
1898
1899 int rc = coreAudioControlStreamOut(pInterface, &pStreamOut->Stream, PDMAUDIOSTREAMCMD_DISABLE);
1900 if (RT_SUCCESS(rc))
1901 {
1902 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_UNINIT);
1903
1904 OSStatus err;
1905
1906 /*
1907 * Unregister playback device callbacks.
1908 */
1909 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
1910 kAudioObjectPropertyElementMaster };
1911#ifdef DEBUG
1912 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
1913 coreAudioPlaybackAudioDevicePropertyChanged, pStreamOut);
1914 /* Not Fatal */
1915 if (RT_UNLIKELY(err != noErr))
1916 LogRel(("CoreAudio: Failed to remove the processor overload listener (%RI32)\n", err));
1917#endif /* DEBUG */
1918
1919 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1920 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
1921 coreAudioPlaybackAudioDevicePropertyChanged, pStreamOut);
1922 /* Not Fatal */
1923 if (RT_UNLIKELY(err != noErr))
1924 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
1925
1926 if (pStreamOut->fDefDevChgListReg)
1927 {
1928 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
1929 propAdr.mScope = kAudioObjectPropertyScopeGlobal;
1930 propAdr.mElement = kAudioObjectPropertyElementMaster;
1931 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
1932 coreAudioDefaultDeviceChanged, pStreamOut);
1933 if (RT_LIKELY(err == noErr))
1934 {
1935 pStreamOut->fDefDevChgListReg = false;
1936 }
1937 else
1938 LogRel(("CoreAudio: [Output] Failed to remove the default playback device changed listener (%RI32)\n", err));
1939 }
1940
1941 err = AudioUnitUninitialize(pStreamOut->audioUnit);
1942 if (err == noErr)
1943 {
1944 err = CloseComponent(pStreamOut->audioUnit);
1945 if (err == noErr)
1946 {
1947 pStreamOut->deviceID = kAudioDeviceUnknown;
1948 pStreamOut->audioUnit = NULL;
1949 if (pStreamOut->pBuf)
1950 {
1951 RTCircBufDestroy(pStreamOut->pBuf);
1952 pStreamOut->pBuf = NULL;
1953 }
1954
1955 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
1956 }
1957 else
1958 LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
1959 }
1960 else
1961 LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
1962 }
1963 else
1964 LogRel(("CoreAudio: Failed to stop playback, rc=%Rrc\n", rc));
1965
1966 LogFlowFuncLeaveRC(rc);
1967 return rc;
1968}
1969
1970static int coreAudioCreateStreamIn(PPDMIHOSTAUDIO pInterface,
1971 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
1972{
1973 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1974
1975 LogFlowFunc(("enmRecSource=%ld\n", pCfg->DestSource.Source));
1976
1977 pStreamIn->deviceID = kAudioDeviceUnknown;
1978 pStreamIn->audioUnit = NULL;
1979 pStreamIn->pConverter = NULL;
1980 pStreamIn->bufferList.mNumberBuffers = 0;
1981 pStreamIn->offBufferRead = 0;
1982 pStreamIn->sampleRatio = 1;
1983 pStreamIn->pBuf = NULL;
1984 pStreamIn->status = CA_STATUS_UNINIT;
1985 pStreamIn->fDefDevChgListReg = false;
1986
1987 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
1988
1989 /* Initialize the hardware info section with the audio settings */
1990 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStreamIn->Stream.Props);
1991 if (RT_SUCCESS(rc))
1992 {
1993#if 0
1994 /* Try to find the audio device set by the user */
1995 if (DeviceUID.pszInputDeviceUID)
1996 {
1997 pStreamIn->deviceID = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszInputDeviceUID);
1998 /* Not fatal */
1999 if (pStreamIn->deviceID == kAudioDeviceUnknown)
2000 LogRel(("CoreAudio: Unable to find input device %s. Falling back to the default audio device. \n", DeviceUID.pszInputDeviceUID));
2001 else
2002 fDeviceByUser = true;
2003 }
2004#endif
2005 rc = coreAudioInitIn(&pStreamIn->Stream, pcSamples);
2006 }
2007
2008 if (RT_SUCCESS(rc))
2009 {
2010 /* When the devices isn't forced by the user, we want default device change notifications. */
2011 if (!fDeviceByUser)
2012 {
2013 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
2014 kAudioObjectPropertyElementMaster };
2015 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2016 coreAudioDefaultDeviceChanged, (void *)pStreamIn);
2017 /* Not fatal. */
2018 if (RT_LIKELY(err == noErr))
2019 {
2020 pStreamIn->fDefDevChgListReg = true;
2021 }
2022 else
2023 LogRel(("CoreAudio: Failed to add the default input device changed listener (%RI32)\n", err));
2024 }
2025 }
2026
2027 LogFlowFuncLeaveRC(rc);
2028 return rc;
2029}
2030
2031static int coreAudioCreateStreamOut(PPDMIHOSTAUDIO pInterface,
2032 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg,
2033 uint32_t *pcSamples)
2034{
2035 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2036
2037 LogFlowFuncEnter();
2038
2039 pStreamOut->deviceID = kAudioDeviceUnknown;
2040 pStreamOut->audioUnit = NULL;
2041 pStreamOut->pBuf = NULL;
2042 pStreamOut->status = CA_STATUS_UNINIT;
2043 pStreamOut->fDefDevChgListReg = false;
2044
2045 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
2046
2047 /* Initialize the hardware info section with the audio settings */
2048 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStreamOut->Stream.Props);
2049 if (RT_SUCCESS(rc))
2050 {
2051#if 0
2052 /* Try to find the audio device set by the user. Use
2053 * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0
2054 * to set it. */
2055 if (DeviceUID.pszOutputDeviceUID)
2056 {
2057 pStreamOut->audioDeviceId = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszOutputDeviceUID);
2058 /* Not fatal */
2059 if (pStreamOut->audioDeviceId == kAudioDeviceUnknown)
2060 LogRel(("CoreAudio: Unable to find output device %s. Falling back to the default audio device. \n", DeviceUID.pszOutputDeviceUID));
2061 else
2062 fDeviceByUser = true;
2063 }
2064#endif
2065 rc = coreAudioInitOut(pStream, pcSamples);
2066 }
2067
2068 if (RT_SUCCESS(rc))
2069 {
2070 /* When the devices isn't forced by the user, we want default device change notifications. */
2071 if (!fDeviceByUser)
2072 {
2073 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
2074 kAudioObjectPropertyElementMaster };
2075 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2076 coreAudioDefaultDeviceChanged, (void *)pStreamOut);
2077 /* Not fatal. */
2078 if (RT_LIKELY(err == noErr))
2079 {
2080 pStreamOut->fDefDevChgListReg = true;
2081 }
2082 else
2083 LogRel(("CoreAudio: Failed to add the default output device changed listener (%RI32)\n", err));
2084 }
2085 }
2086
2087 LogFlowFuncLeaveRC(rc);
2088 return rc;
2089}
2090
2091static DECLCALLBACK(int) drvHostCoreAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
2092{
2093 NOREF(pInterface);
2094 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2095
2096 LogFlowFuncEnter();
2097
2098 pCfg->cbStreamIn = sizeof(COREAUDIOSTREAMIN);
2099 pCfg->cbStreamOut = sizeof(COREAUDIOSTREAMOUT);
2100 pCfg->cMaxStreamsIn = UINT32_MAX;
2101 pCfg->cMaxStreamsOut = UINT32_MAX;
2102
2103 /** @todo Implement a proper device detection. */
2104 pCfg->cSources = 1;
2105 pCfg->cSinks = 1;
2106
2107 return VINF_SUCCESS;
2108}
2109
2110static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2111{
2112 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2113
2114 return PDMAUDIOBACKENDSTS_RUNNING;
2115}
2116
2117static DECLCALLBACK(int) drvHostCoreAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
2118 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
2119{
2120 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2121 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2122 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2123
2124 int rc;
2125 if (pCfg->enmDir == PDMAUDIODIR_IN)
2126 rc = coreAudioCreateStreamIn(pInterface, pStream, pCfg, pcSamples);
2127 else
2128 rc = coreAudioCreateStreamOut(pInterface, pStream, pCfg, pcSamples);
2129
2130 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
2131 return rc;
2132}
2133
2134static DECLCALLBACK(int) drvHostCoreAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2135{
2136 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2137 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2138
2139 int rc;
2140 if (pStream->enmDir == PDMAUDIODIR_IN)
2141 rc = coreAudioDestroyStreamIn(pInterface, pStream);
2142 else
2143 rc = coreAudioDestroyStreamOut(pInterface, pStream);
2144
2145 return rc;
2146}
2147
2148static DECLCALLBACK(int) drvHostCoreAudioStreamControl(PPDMIHOSTAUDIO pInterface,
2149 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2150{
2151 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2152 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2153
2154 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
2155
2156 int rc;
2157 if (pStream->enmDir == PDMAUDIODIR_IN)
2158 rc = coreAudioControlStreamIn(pInterface, pStream, enmStreamCmd);
2159 else
2160 rc = coreAudioControlStreamOut(pInterface, pStream, enmStreamCmd);
2161
2162 return rc;
2163}
2164
2165static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostCoreAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2166{
2167 NOREF(pInterface);
2168 NOREF(pStream);
2169
2170 return (PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED);
2171}
2172
2173static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
2174{
2175 NOREF(pInterface);
2176}
2177
2178static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2179{
2180 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2181 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2182 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2183 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2184
2185 return NULL;
2186}
2187
2188 /* Construct a DirectSound Audio driver instance.
2189 *
2190 * @copydoc FNPDMDRVCONSTRUCT
2191 */
2192static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2193{
2194 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2195 LogRel(("Audio: Initializing Core Audio driver\n"));
2196
2197 /*
2198 * Init the static parts.
2199 */
2200 pThis->pDrvIns = pDrvIns;
2201 /* IBase */
2202 pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
2203 /* IHostAudio */
2204 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
2205
2206 return VINF_SUCCESS;
2207}
2208
2209/**
2210 * Char driver registration record.
2211 */
2212const PDMDRVREG g_DrvHostCoreAudio =
2213{
2214 /* u32Version */
2215 PDM_DRVREG_VERSION,
2216 /* szName */
2217 "CoreAudio",
2218 /* szRCMod */
2219 "",
2220 /* szR0Mod */
2221 "",
2222 /* pszDescription */
2223 "Core Audio host driver",
2224 /* fFlags */
2225 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2226 /* fClass. */
2227 PDM_DRVREG_CLASS_AUDIO,
2228 /* cMaxInstances */
2229 ~0U,
2230 /* cbInstance */
2231 sizeof(DRVHOSTCOREAUDIO),
2232 /* pfnConstruct */
2233 drvHostCoreAudioConstruct,
2234 /* pfnDestruct */
2235 NULL,
2236 /* pfnRelocate */
2237 NULL,
2238 /* pfnIOCtl */
2239 NULL,
2240 /* pfnPowerOn */
2241 NULL,
2242 /* pfnReset */
2243 NULL,
2244 /* pfnSuspend */
2245 NULL,
2246 /* pfnResume */
2247 NULL,
2248 /* pfnAttach */
2249 NULL,
2250 /* pfnDetach */
2251 NULL,
2252 /* pfnPowerOff */
2253 NULL,
2254 /* pfnSoftReset */
2255 NULL,
2256 /* u32EndVersion */
2257 PDM_DRVREG_VERSION
2258};
2259
Note: See TracBrowser for help on using the repository browser.

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