VirtualBox

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

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

Audio: Implemented backend-independent handling of added/removed audio devices.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 99.0 KB
 
1/* $Id: DrvHostCoreAudio.cpp 61764 2016-06-20 09:49:55Z 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, 0, NULL, 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
264/* Prototypes needed for COREAUDIOSTREAMCBCTX. */
265struct COREAUDIOSTREAMIN;
266typedef struct COREAUDIOSTREAMIN *PCOREAUDIOSTREAMIN;
267struct COREAUDIOSTREAMOUT;
268typedef struct COREAUDIOSTREAMOUT *PCOREAUDIOSTREAMOUT;
269
270/**
271 * Simple structure for maintaining a stream's callback context.
272 ** @todo Remove this as soon as we have unified input/output streams in this backend.
273 */
274typedef struct COREAUDIOSTREAMCBCTX
275{
276 /** Pointer to driver instance. */
277 PDRVHOSTCOREAUDIO pThis;
278 /** The stream's direction. */
279 PDMAUDIODIR enmDir;
280 union
281 {
282 /** Pointer to self, if it's an input stream. */
283 PCOREAUDIOSTREAMIN pIn;
284 /** Pointer to self, if it's an output stream. */
285 PCOREAUDIOSTREAMOUT pOut;
286 };
287} COREAUDIOSTREAMCBCTX, *PCOREAUDIOSTREAMCBCTX;
288
289/** @todo Unify COREAUDIOSTREAMOUT / COREAUDIOSTREAMIN. */
290typedef struct COREAUDIOSTREAMOUT
291{
292 /** Host output stream.
293 * Note: Always must come first in this structure! */
294 PDMAUDIOSTREAM Stream;
295 /** Stream description which is default on the device. */
296 AudioStreamBasicDescription deviceFormat;
297 /** Stream description which is selected for using with VBox. */
298 AudioStreamBasicDescription streamFormat;
299 /** The audio device ID of the currently used device. */
300 AudioDeviceID deviceID;
301 /** The AudioUnit being used. */
302 AudioUnit audioUnit;
303 /** A ring buffer for transferring data to the playback thread. */
304 PRTCIRCBUF pBuf;
305 /** Initialization status tracker. Used when some of the device parameters
306 * or the device itself is changed during the runtime. */
307 volatile uint32_t status;
308 /** Flag whether the "default device changed" listener was registered. */
309 bool fDefDevChgListReg;
310 /** Flag whether the "device state changed" listener was registered. */
311 bool fDevStateChgListReg;
312 /** Callback context for this stream for handing this stream in to
313 * a CoreAudio callback.
314 ** @todo Remove this as soon as we have unified input/output streams in this backend. */
315 COREAUDIOSTREAMCBCTX cbCtx;
316} COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT;
317
318typedef struct COREAUDIOSTREAMIN
319{
320 /** Host input stream.
321 * Note: Always must come first in this structure! */
322 PDMAUDIOSTREAM Stream;
323 /** Stream description which is default on the device. */
324 AudioStreamBasicDescription deviceFormat;
325 /** Stream description which is selected for using with VBox. */
326 AudioStreamBasicDescription streamFormat;
327 /** The audio device ID of the currently used device. */
328 AudioDeviceID deviceID;
329 /** The AudioUnit used. */
330 AudioUnit audioUnit;
331 /** The audio converter if necessary. */
332 AudioConverterRef pConverter;
333 /** Native buffer used for render the audio data in the recording thread. */
334 AudioBufferList bufferList;
335 /** Reading offset for the bufferList's buffer. */
336 uint32_t offBufferRead;
337 /** The ratio between the device & the stream sample rate. */
338 Float64 sampleRatio;
339 /** A ring buffer for transferring data from the recording thread. */
340 PRTCIRCBUF pBuf;
341 /** Initialization status tracker. Used when some of the device parameters
342 * or the device itself is changed during the runtime. */
343 volatile uint32_t status;
344 /** Flag whether the "default device changed" listener was registered. */
345 bool fDefDevChgListReg;
346 /** Flag whether the "device state changed" listener was registered. */
347 bool fDevStateChgListReg;
348 /** Callback context for this stream for handing this stream in to
349 * a CoreAudio callback.
350 ** @todo Remove this as soon as we have unified input/output streams in this backend. */
351 COREAUDIOSTREAMCBCTX cbCtx;
352} COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN;
353
354
355static int coreAudioInitIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, uint32_t *pcSamples);
356static int coreAudioInitOut(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, uint32_t *pcSamples);
357static int coreAudioControlStreamIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
358static int coreAudioControlStreamOut(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd);
359static int coreAudioCreateStreamIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples);
360static int coreAudioCreateStreamOut(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples);
361static int coreAudioDestroyStreamIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream);
362static int coreAudioDestroyStreamOut(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream);
363
364static OSStatus coreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void *pvUser);
365static OSStatus coreAudioPlaybackCb(void *pvUser, AudioUnitRenderActionFlags *pActionFlags, const AudioTimeStamp *pAudioTS, UInt32 uBusID, UInt32 cFrames, AudioBufferList* pBufData);
366
367/**
368 * Does a (Re-)enumeration of the host's playback + capturing devices.
369 *
370 * @return IPRT status code.
371 * @param pThis Host audio driver instance.
372 * @param pCfg Where to store the enumeration results.
373 * @param fEnum Enumeration flags.
374 */
375static int coreAudioDevicesEnumerate(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, bool fIn, uint32_t fEnum)
376{
377 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
378 /* pCfg is optional. */
379
380 int rc = VINF_SUCCESS;
381
382 uint8_t cDevs = 0;
383
384 do
385 {
386 AudioObjectPropertyAddress propAdrDevList = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
387 kAudioObjectPropertyElementMaster };
388 UInt32 uSize = 0;
389 OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize);
390 if (err != kAudioHardwareNoError)
391 break;
392
393 AudioDeviceID *pDevIDs = (AudioDeviceID *)alloca(uSize);
394 if (pDevIDs == NULL)
395 break;
396
397 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize, pDevIDs);
398 if (err != kAudioHardwareNoError)
399 break;
400
401 UInt32 cDevices = uSize / sizeof (AudioDeviceID);
402 for (UInt32 i = 0; i < cDevices; i++)
403 {
404 AudioDeviceID curDevID = pDevIDs[i];
405
406 /* Check if the device is valid. */
407 AudioObjectPropertyAddress propAddrCfg = { kAudioDevicePropertyStreamConfiguration,
408 fIn ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
409 kAudioObjectPropertyElementMaster };
410
411 err = AudioObjectGetPropertyDataSize(curDevID, &propAddrCfg, 0, NULL, &uSize);
412 if (err != noErr)
413 continue;
414
415 AudioBufferList *pBufList = (AudioBufferList *)RTMemAlloc(uSize);
416 if (!pBufList)
417 continue;
418
419 bool fIsValid = false;
420
421 err = AudioObjectGetPropertyData(curDevID, &propAddrCfg, 0, NULL, &uSize, pBufList);
422 if (err == noErr)
423 {
424 for (UInt32 a = 0; a < pBufList->mNumberBuffers; a++)
425 {
426 fIsValid = pBufList->mBuffers[a].mNumberChannels > 0;
427 if (fIsValid)
428 break;
429 }
430 }
431
432 if (pBufList)
433 {
434 RTMemFree(pBufList);
435 pBufList = NULL;
436 }
437
438 if (!fIsValid)
439 continue;
440
441 /* Resolve the device's name. */
442 AudioObjectPropertyAddress propAddrName = { kAudioObjectPropertyName,
443 fIn ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
444 kAudioObjectPropertyElementMaster };
445 uSize = sizeof(CFStringRef);
446 CFStringRef pcfstrName = NULL;
447
448 err = AudioObjectGetPropertyData(curDevID, &propAddrName, 0, NULL, &uSize, &pcfstrName);
449 if (err != kAudioHardwareNoError)
450 continue;
451
452 CFIndex uMax = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pcfstrName), kCFStringEncodingUTF8) + 1;
453 if (uMax)
454 {
455 char *pszName = (char *)RTStrAlloc(uMax);
456 if ( pszName
457 && CFStringGetCString(pcfstrName, pszName, uMax, kCFStringEncodingUTF8))
458 {
459 LogRel2(("CoreAudio: Found %s device '%s'\n", fIn ? "recording" : "playback", pszName));
460 cDevs++;
461 }
462
463 if (pszName)
464 {
465 RTStrFree(pszName);
466 pszName = NULL;
467 }
468 }
469
470 CFRelease(pcfstrName);
471 }
472
473 } while (0);
474
475 if (fIn)
476 LogRel2(("CoreAudio: Found %RU8 capturing device(s)\n", cDevs));
477 else
478 LogRel2(("CoreAudio: Found %RU8 playback device(s)\n", cDevs));
479
480 if (pCfg)
481 {
482 if (fIn)
483 pCfg->cSources = cDevs;
484 else
485 pCfg->cSinks = cDevs;
486 }
487
488 LogFlowFuncLeaveRC(rc);
489 return rc;
490}
491
492/**
493 * Updates this host driver's internal status, according to the global, overall input/output
494 * state and all connected (native) audio streams.
495 *
496 * @param pThis Host audio driver instance.
497 * @param pCfg Where to store the backend configuration. Optional.
498 * @param fEnum Enumeration flags.
499 */
500int coreAudioUpdateStatusInternalEx(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
501{
502 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
503 /* pCfg is optional. */
504
505 PDMAUDIOBACKENDCFG Cfg;
506 RT_ZERO(Cfg);
507
508 Cfg.cbStreamOut = sizeof(COREAUDIOSTREAMOUT);
509 Cfg.cbStreamIn = sizeof(COREAUDIOSTREAMIN);
510 Cfg.cMaxStreamsIn = UINT32_MAX;
511 Cfg.cMaxStreamsOut = UINT32_MAX;
512
513 int rc = coreAudioDevicesEnumerate(pThis, &Cfg, false /* fIn */, 0 /* fEnum */);
514 AssertRC(rc);
515 rc = coreAudioDevicesEnumerate(pThis, &Cfg, true /* fIn */, 0 /* fEnum */);
516 AssertRC(rc);
517
518 if (pCfg)
519 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
520
521 LogFlowFuncLeaveRC(rc);
522 return rc;
523}
524
525static DECLCALLBACK(OSStatus) drvHostCoreAudioDeviceStateChanged(AudioObjectID propertyID,
526 UInt32 nAddresses,
527 const AudioObjectPropertyAddress properties[],
528 void *pvUser)
529{
530 LogFlowFunc(("propertyID=%u nAddresses=%u pvUser=%p\n", propertyID, nAddresses, pvUser));
531
532 PCOREAUDIOSTREAMCBCTX pCbCtx = (PCOREAUDIOSTREAMCBCTX)pvUser;
533 AssertPtr(pCbCtx);
534
535 UInt32 uAlive = 1;
536 UInt32 uSize = sizeof(UInt32);
537
538 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
539 kAudioObjectPropertyElementMaster };
540
541 AudioDeviceID deviceID = pCbCtx->enmDir == PDMAUDIODIR_IN
542 ? pCbCtx->pIn->deviceID : pCbCtx->pOut->deviceID;
543
544 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uAlive);
545
546 bool fIsDead = false;
547
548 if (err == kAudioHardwareBadDeviceError)
549 fIsDead = true; /* Unplugged. */
550 else if ((err == kAudioHardwareNoError) && (!RT_BOOL(uAlive)))
551 fIsDead = true; /* Something else happened. */
552
553 if (fIsDead)
554 {
555 switch (pCbCtx->enmDir)
556 {
557 case PDMAUDIODIR_IN:
558 {
559 PCOREAUDIOSTREAMIN pStreamIn = pCbCtx->pIn;
560
561 /* We move the reinitialization to the next output event.
562 * This make sure this thread isn't blocked and the
563 * reinitialization is done when necessary only. */
564 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
565
566 LogRel(("CoreAudio: Capturing device stopped functioning\n"));
567 break;
568 }
569
570 case PDMAUDIODIR_OUT:
571 {
572 PCOREAUDIOSTREAMOUT pStreamOut = pCbCtx->pOut;
573
574 /* We move the reinitialization to the next output event.
575 * This make sure this thread isn't blocked and the
576 * reinitialization is done when necessary only. */
577 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_REINIT);
578
579 LogRel(("CoreAudio: Playback device stopped functioning\n"));
580 break;
581 }
582
583 default:
584 AssertMsgFailed(("Not implemented\n"));
585 break;
586 }
587 }
588
589 int rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, false /* fIn */, 0 /* fEnum */);
590 AssertRC(rc2);
591 rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, true /* fIn */, 0 /* fEnum */);
592 AssertRC(rc2);
593
594 return noErr;
595}
596
597/* Callback for getting notified when the default recording/playback device has been changed. */
598static DECLCALLBACK(OSStatus) coreAudioDefaultDeviceChanged(AudioObjectID propertyID,
599 UInt32 nAddresses,
600 const AudioObjectPropertyAddress properties[],
601 void *pvUser)
602{
603 OSStatus err = noErr;
604
605 LogFlowFunc(("propertyID=%u nAddresses=%u pvUser=%p\n", propertyID, nAddresses, pvUser));
606
607 PCOREAUDIOSTREAMCBCTX pCbCtx = (PCOREAUDIOSTREAMCBCTX)pvUser;
608 AssertPtr(pCbCtx);
609
610 for (UInt32 idxAddress = 0; idxAddress < nAddresses; idxAddress++)
611 {
612 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress];
613
614 switch (pProperty->mSelector)
615 {
616 case kAudioHardwarePropertyDefaultInputDevice:
617 {
618 PCOREAUDIOSTREAMIN pStreamIn = pCbCtx->pIn;
619 AssertPtr(pStreamIn);
620
621 /* This listener is called on every change of the hardware
622 * device. So check if the default device has really changed. */
623 UInt32 uSize = sizeof(pStreamIn->deviceID);
624 UInt32 uResp;
625 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp);
626 if (err == noErr)
627 {
628 if (pStreamIn->deviceID != uResp)
629 {
630 LogRel2(("CoreAudio: Default device for recording has changed\n"));
631
632 /* We move the reinitialization to the next input event.
633 * This make sure this thread isn't blocked and the
634 * reinitialization is done when necessary only. */
635 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
636 }
637 }
638 break;
639 }
640
641 case kAudioHardwarePropertyDefaultOutputDevice:
642 {
643 PCOREAUDIOSTREAMOUT pStreamOut = pCbCtx->pOut;
644 AssertPtr(pStreamOut);
645
646 /* This listener is called on every change of the hardware
647 * device. So check if the default device has really changed. */
648 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
649 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
650
651 UInt32 uSize = sizeof(pStreamOut->deviceID);
652 UInt32 uResp;
653 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &uResp);
654 if (err == noErr)
655 {
656 if (pStreamOut->deviceID != uResp)
657 {
658 LogRel2(("CoreAudio: Default device for playback has changed\n"));
659
660 /* We move the reinitialization to the next input event.
661 * This make sure this thread isn't blocked and the
662 * reinitialization is done when necessary only. */
663 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_REINIT);
664 }
665 }
666 break;
667 }
668
669 default:
670 break;
671 }
672 }
673
674 int rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, false /* fIn */, 0 /* fEnum */);
675 AssertRC(rc2);
676 rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, true /* fIn */, 0 /* fEnum */);
677 AssertRC(rc2);
678
679 /** @todo Implement callback notification here to let the audio connector / device emulation
680 * know that something has changed. */
681
682 return noErr;
683}
684
685/* Callback for getting notified when some of the properties of an audio device has changed. */
686static DECLCALLBACK(OSStatus) coreAudioRecordingAudioDevicePropertyChanged(AudioObjectID propertyID,
687 UInt32 cAdresses,
688 const AudioObjectPropertyAddress aProperties[],
689 void *pvUser)
690{
691 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
692
693 switch (propertyID)
694 {
695#ifdef DEBUG
696 case kAudioDeviceProcessorOverload:
697 {
698 LogFunc(("Processor overload detected!\n"));
699 break;
700 }
701#endif /* DEBUG */
702 case kAudioDevicePropertyNominalSampleRate:
703 {
704 LogRel(("CoreAudio: Recording sample rate changed\n"));
705
706 /* We move the reinitialization to the next input event.
707 * This make sure this thread isn't blocked and the
708 * reinitialization is done when necessary only. */
709 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
710 break;
711 }
712
713 default:
714 break;
715 }
716
717 return noErr;
718}
719
720/* Callback to convert audio input data from one format to another. */
721static DECLCALLBACK(OSStatus) coreAudioConverterCb(AudioConverterRef converterID,
722 UInt32 *pcPackets,
723 AudioBufferList *pBufData,
724 AudioStreamPacketDescription **ppPacketDesc,
725 void *pvUser)
726{
727 /** @todo Check incoming pointers. */
728
729 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
730
731 /** @todo Check converter ID? */
732
733 const AudioBufferList *pBufferList = &pStreamIn->bufferList;
734
735 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
736 return noErr;
737
738 /** @todo In principle we had to check here if the source is non interleaved, and if so,
739 * so go through all buffers not only the first one like now. */
740
741 /* Use the lower one of the packets to process & the available packets in the buffer. */
742 Assert(pBufferList->mBuffers[0].mDataByteSize >= pStreamIn->offBufferRead);
743 UInt32 cSize = RT_MIN(*pcPackets * pStreamIn->deviceFormat.mBytesPerPacket,
744 pBufferList->mBuffers[0].mDataByteSize - pStreamIn->offBufferRead);
745
746 /* Set the new size on output, so the caller know what we have processed. */
747 Assert(pStreamIn->deviceFormat.mBytesPerPacket);
748 *pcPackets = cSize / pStreamIn->deviceFormat.mBytesPerPacket;
749
750 OSStatus err;
751
752 /* If no data is available anymore we return with an error code. This error code will be returned
753 * from AudioConverterFillComplexBuffer. */
754 if (*pcPackets == 0)
755 {
756 pBufData->mBuffers[0].mDataByteSize = 0;
757 pBufData->mBuffers[0].mData = NULL;
758
759 err = caConverterEOFDErr;
760 }
761 else
762 {
763 pBufData->mBuffers[0].mNumberChannels = pBufferList->mBuffers[0].mNumberChannels;
764 pBufData->mBuffers[0].mDataByteSize = cSize;
765 pBufData->mBuffers[0].mData = (uint8_t *)pBufferList->mBuffers[0].mData + pStreamIn->offBufferRead;
766
767 pStreamIn->offBufferRead += cSize;
768
769 err = noErr;
770 }
771
772 return err;
773}
774
775/* Callback to feed audio input buffer. */
776static DECLCALLBACK(OSStatus) coreAudioRecordingCb(void *pvUser,
777 AudioUnitRenderActionFlags *pActionFlags,
778 const AudioTimeStamp *pAudioTS,
779 UInt32 uBusID,
780 UInt32 cFrames,
781 AudioBufferList *pBufData)
782{
783 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
784 PPDMAUDIOSTREAM pStream = &pStreamIn->Stream;
785
786 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
787 return noErr;
788
789 /* If nothing is pending return immediately. */
790 if (cFrames == 0)
791 return noErr;
792
793 OSStatus err = noErr;
794 int rc = VINF_SUCCESS;
795
796 do
797 {
798 /* Are we using a converter? */
799 if (pStreamIn->pConverter)
800 {
801 /* First, render the data as usual. */
802 pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->deviceFormat.mChannelsPerFrame;
803 pStreamIn->bufferList.mBuffers[0].mDataByteSize = pStreamIn->deviceFormat.mBytesPerFrame * cFrames;
804 AssertBreakStmt(pStreamIn->bufferList.mBuffers[0].mDataByteSize, rc = VERR_INVALID_PARAMETER);
805 pStreamIn->bufferList.mBuffers[0].mData = RTMemAlloc(pStreamIn->bufferList.mBuffers[0].mDataByteSize);
806 if (!pStreamIn->bufferList.mBuffers[0].mData)
807 {
808 rc = VERR_NO_MEMORY;
809 break;
810 }
811
812 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pStreamIn->bufferList);
813 if (err != noErr)
814 {
815 LogRel2(("CoreAudio: Failed rendering converted audio input data (%RI32)\n", err));
816 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
817 break;
818 }
819
820 size_t cbAvail = RT_MIN(RTCircBufFree(pStreamIn->pBuf), pStreamIn->bufferList.mBuffers[0].mDataByteSize);
821
822 /* Initialize the temporary output buffer */
823 AudioBufferList tmpList;
824 tmpList.mNumberBuffers = 1;
825 tmpList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
826
827 /* Set the read position to zero. */
828 pStreamIn->offBufferRead = 0;
829
830 /* Iterate as long as data is available. */
831 uint8_t *puDst = NULL;
832 while (cbAvail)
833 {
834 /* Try to acquire the necessary space from the ring buffer. */
835 size_t cbToWrite = 0;
836 RTCircBufAcquireWriteBlock(pStreamIn->pBuf, cbAvail, (void **)&puDst, &cbToWrite);
837 if (!cbToWrite)
838 break;
839
840 /* Now set how much space is available for output. */
841 Assert(pStreamIn->streamFormat.mBytesPerPacket);
842
843 UInt32 ioOutputDataPacketSize = cbToWrite / pStreamIn->streamFormat.mBytesPerPacket;
844
845 /* Set our ring buffer as target. */
846 tmpList.mBuffers[0].mDataByteSize = cbToWrite;
847 tmpList.mBuffers[0].mData = puDst;
848
849 AudioConverterReset(pStreamIn->pConverter);
850
851 err = AudioConverterFillComplexBuffer(pStreamIn->pConverter, coreAudioConverterCb, pStreamIn,
852 &ioOutputDataPacketSize, &tmpList, NULL);
853 if( err != noErr
854 && err != caConverterEOFDErr)
855 {
856 LogFunc(("Failed to convert audio data (%RI32:%c%c%c%c)\n", err,
857 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
858 rc = VERR_IO_GEN_FAILURE;
859 break;
860 }
861
862 /* Check in any case what processed size is returned. It could be less than we expected. */
863 cbToWrite = ioOutputDataPacketSize * pStreamIn->streamFormat.mBytesPerPacket;
864
865 Log3Func(("cbToWrite=%RU32\n", cbToWrite));
866
867 /* Release the ring buffer, so the main thread could start reading this data. */
868 RTCircBufReleaseWriteBlock(pStreamIn->pBuf, cbToWrite);
869
870 /* If the error is "End of Data" it means there is no data anymore
871 * which could be converted. So end here now. */
872 if (err == caConverterEOFDErr)
873 break;
874
875 Assert(cbAvail >= cbToWrite);
876 cbAvail -= cbToWrite;
877 }
878 }
879 else /* No converter being used. */
880 {
881 AssertBreakStmt(pStreamIn->streamFormat.mChannelsPerFrame >= 1, rc = VERR_INVALID_PARAMETER);
882 AssertBreakStmt(pStreamIn->streamFormat.mBytesPerFrame >= 1, rc = VERR_INVALID_PARAMETER);
883
884 AssertBreakStmt(pStreamIn->bufferList.mNumberBuffers >= 1, rc = VERR_INVALID_PARAMETER);
885 AssertBreakStmt(pStreamIn->bufferList.mBuffers[0].mNumberChannels, rc = VERR_INVALID_PARAMETER);
886
887 pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
888 pStreamIn->bufferList.mBuffers[0].mDataByteSize = pStreamIn->streamFormat.mBytesPerFrame * cFrames;
889 pStreamIn->bufferList.mBuffers[0].mData = RTMemAlloc(pStreamIn->bufferList.mBuffers[0].mDataByteSize);
890 if (!pStreamIn->bufferList.mBuffers[0].mData)
891 {
892 rc = VERR_NO_MEMORY;
893 break;
894 }
895
896 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pStreamIn->bufferList);
897 if (err != noErr)
898 {
899 LogRel2(("CoreAudio: Failed rendering non-coverted audio input data (%RI32)\n", err));
900 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
901 break;
902 }
903
904 const uint32_t cbDataSize = pStreamIn->bufferList.mBuffers[0].mDataByteSize;
905 const size_t cbBufFree = RTCircBufFree(pStreamIn->pBuf);
906 size_t cbAvail = RT_MIN(cbDataSize, cbBufFree);
907
908 Log3Func(("cbDataSize=%RU32, cbBufFree=%zu, cbAvail=%zu\n", cbDataSize, cbBufFree, cbAvail));
909
910 /* Iterate as long as data is available. */
911 uint8_t *puDst = NULL;
912 uint32_t cbWrittenTotal = 0;
913 while (cbAvail)
914 {
915 /* Try to acquire the necessary space from the ring buffer. */
916 size_t cbToWrite = 0;
917 RTCircBufAcquireWriteBlock(pStreamIn->pBuf, cbAvail, (void **)&puDst, &cbToWrite);
918 if (!cbToWrite)
919 break;
920
921 /* Copy the data from the Core Audio buffer to the ring buffer. */
922 memcpy(puDst, (uint8_t *)pStreamIn->bufferList.mBuffers[0].mData + cbWrittenTotal, cbToWrite);
923
924 /* Release the ring buffer, so the main thread could start reading this data. */
925 RTCircBufReleaseWriteBlock(pStreamIn->pBuf, cbToWrite);
926
927 cbWrittenTotal += cbToWrite;
928
929 Assert(cbAvail >= cbToWrite);
930 cbAvail -= cbToWrite;
931 }
932
933 Log3Func(("cbWrittenTotal=%RU32, cbLeft=%zu\n", cbWrittenTotal, cbAvail));
934 }
935
936 } while (0);
937
938 if (pStreamIn->bufferList.mBuffers[0].mData)
939 {
940 RTMemFree(pStreamIn->bufferList.mBuffers[0].mData);
941 pStreamIn->bufferList.mBuffers[0].mData = NULL;
942 }
943
944 if (RT_FAILURE(rc))
945 LogFunc(("Failed with rc=%Rrc\n", rc));
946
947 return err;
948}
949
950# define CA_BREAK_STMT(stmt) \
951 stmt; \
952 break;
953
954/** @todo Eventually split up this function, as this already is huge! */
955static int coreAudioInitIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, uint32_t *pcSamples)
956{
957 int rc = VINF_SUCCESS;
958
959 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
960 UInt32 cSamples = 0;
961
962 do
963 {
964 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_INIT);
965
966 OSStatus err;
967
968 UInt32 uSize = 0;
969 if (pStreamIn->deviceID == kAudioDeviceUnknown)
970 {
971 /* Fetch the default audio recording device currently in use. */
972 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice,
973 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
974 uSize = sizeof(pStreamIn->deviceID);
975 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &pStreamIn->deviceID);
976 if (err != noErr)
977 {
978 LogRel2(("CoreAudio: Unable to determine default recording device (%RI32)\n", err));
979 return VERR_AUDIO_NO_FREE_INPUT_STREAMS;
980 }
981 }
982
983 if (pStreamIn->deviceID == kAudioDeviceUnknown)
984 {
985 LogFlowFunc(("No default recording device found\n"));
986 return VERR_AUDIO_NO_FREE_INPUT_STREAMS;
987 }
988
989 /*
990 * Try to get the name of the recording device and log it. It's not fatal if it fails.
991 */
992 CFStringRef strTemp;
993
994 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
995 kAudioObjectPropertyElementMaster };
996 uSize = sizeof(CFStringRef);
997 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
998 if (err == noErr)
999 {
1000 char *pszDevName = NULL;
1001 err = coreAudioCFStringToCString(strTemp, &pszDevName);
1002 if (err == noErr)
1003 {
1004 CFRelease(strTemp);
1005
1006 /* Get the device' UUID. */
1007 propAdr.mSelector = kAudioDevicePropertyDeviceUID;
1008 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1009 if (err == noErr)
1010 {
1011 char *pszUID = NULL;
1012 err = coreAudioCFStringToCString(strTemp, &pszUID);
1013 if (err == noErr)
1014 {
1015 CFRelease(strTemp);
1016 LogRel(("CoreAudio: Using recording device: %s (UID: %s)\n", pszDevName, pszUID));
1017
1018 RTMemFree(pszUID);
1019 }
1020 }
1021
1022 RTMemFree(pszDevName);
1023 }
1024 }
1025 else
1026 {
1027 /* This is not fatal, can happen for some Macs. */
1028 LogRel2(("CoreAudio: Unable to determine recording device name (%RI32)\n", err));
1029 }
1030
1031 /* Get the default frames buffer size, so that we can setup our internal buffers. */
1032 UInt32 cFrames;
1033 uSize = sizeof(cFrames);
1034 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
1035 propAdr.mScope = kAudioDevicePropertyScopeInput;
1036 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
1037 if (err != noErr)
1038 {
1039 /* Can happen if no recording device is available by default. Happens on some Macs,
1040 * so don't log this by default to not scare people. */
1041 LogRel2(("CoreAudio: Failed to determine frame buffer size of the audio recording device (%RI32)\n", err));
1042 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1043 }
1044
1045 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
1046 err = coreAudioSetFrameBufferSize(pStreamIn->deviceID, true /* fInput */, cFrames, &cFrames);
1047 if (err != noErr)
1048 {
1049 LogRel(("CoreAudio: Failed to set frame buffer size for the audio recording device (%RI32)\n", err));
1050 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1051 }
1052
1053 LogFlowFunc(("cFrames=%RU32\n", cFrames));
1054
1055 ComponentDescription cd;
1056 RT_ZERO(cd);
1057 cd.componentType = kAudioUnitType_Output;
1058 cd.componentSubType = kAudioUnitSubType_HALOutput;
1059 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
1060
1061 /* Try to find the default HAL output component. */
1062 Component cp = FindNextComponent(NULL, &cd);
1063 if (cp == 0)
1064 {
1065 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
1066 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1067 }
1068
1069 /* Open the default HAL output component. */
1070 err = OpenAComponent(cp, &pStreamIn->audioUnit);
1071 if (err != noErr)
1072 {
1073 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
1074 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1075 }
1076
1077 /* Switch the I/O mode for input to on. */
1078 UInt32 uFlag = 1;
1079 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
1080 1, &uFlag, sizeof(uFlag));
1081 if (err != noErr)
1082 {
1083 LogRel(("CoreAudio: Failed to disable input I/O mode for input stream (%RI32)\n", err));
1084 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1085 }
1086
1087 /* Switch the I/O mode for output to off. This is important, as this is a pure input stream. */
1088 uFlag = 0;
1089 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
1090 0, &uFlag, sizeof(uFlag));
1091 if (err != noErr)
1092 {
1093 LogRel(("CoreAudio: Failed to disable output I/O mode for input stream (%RI32)\n", err));
1094 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1095 }
1096
1097 /* Set the default audio recording device as the device for the new AudioUnit. */
1098 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
1099 0, &pStreamIn->deviceID, sizeof(pStreamIn->deviceID));
1100 if (err != noErr)
1101 {
1102 LogRel(("CoreAudio: Failed to set current device (%RI32)\n", err));
1103 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1104 }
1105
1106 /*
1107 * CoreAudio will inform us on a second thread for new incoming audio data.
1108 * Therefore register a callback function which will process the new data.
1109 */
1110 AURenderCallbackStruct cb;
1111 RT_ZERO(cb);
1112 cb.inputProc = coreAudioRecordingCb;
1113 cb.inputProcRefCon = pStreamIn;
1114
1115 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,
1116 0, &cb, sizeof(cb));
1117 if (err != noErr)
1118 {
1119 LogRel(("CoreAudio: Failed to register input callback (%RI32)\n", err));
1120 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1121 }
1122
1123 /* Fetch the current stream format of the device. */
1124 uSize = sizeof(pStreamIn->deviceFormat);
1125 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1126 1, &pStreamIn->deviceFormat, &uSize);
1127 if (err != noErr)
1128 {
1129 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
1130 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1131 }
1132
1133 /* Create an AudioStreamBasicDescription based on our required audio settings. */
1134 coreAudioPCMInfoToASBDesc(&pStreamIn->Stream.Props, &pStreamIn->streamFormat);
1135
1136 coreAudioPrintASBDesc("CoreAudio: Recording device", &pStreamIn->deviceFormat);
1137 coreAudioPrintASBDesc("CoreAudio: Recording stream", &pStreamIn->streamFormat);
1138
1139 /* If the frequency of the device is different from the requested one we
1140 * need a converter. The same count if the number of channels is different. */
1141 if ( pStreamIn->deviceFormat.mSampleRate != pStreamIn->streamFormat.mSampleRate
1142 || pStreamIn->deviceFormat.mChannelsPerFrame != pStreamIn->streamFormat.mChannelsPerFrame)
1143 {
1144 LogRel(("CoreAudio: Input converter is active\n"));
1145
1146 err = AudioConverterNew(&pStreamIn->deviceFormat, &pStreamIn->streamFormat, &pStreamIn->pConverter);
1147 if (RT_UNLIKELY(err != noErr))
1148 {
1149 LogRel(("CoreAudio: Failed to create the audio converter (%RI32)\n", err));
1150 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1151 }
1152
1153 if ( pStreamIn->deviceFormat.mChannelsPerFrame == 1 /* Mono */
1154 && pStreamIn->streamFormat.mChannelsPerFrame == 2 /* Stereo */)
1155 {
1156 /*
1157 * If the channel count is different we have to tell this the converter
1158 * and supply a channel mapping. For now we only support mapping
1159 * from mono to stereo. For all other cases the core audio defaults
1160 * are used, which means dropping additional channels in most
1161 * cases.
1162 */
1163 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo, */
1164
1165 err = AudioConverterSetProperty(pStreamIn->pConverter, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
1166 if (err != noErr)
1167 {
1168 LogRel(("CoreAudio: Failed to set channel mapping (mono -> stereo) for the audio input converter (%RI32)\n", err));
1169 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1170 }
1171 }
1172#if 0
1173 /* Set sample rate converter quality to maximum. */
1174 uFlag = kAudioConverterQuality_Max;
1175 err = AudioConverterSetProperty(pStreamIn->converter, kAudioConverterSampleRateConverterQuality,
1176 sizeof(uFlag), &uFlag);
1177 if (err != noErr)
1178 LogRel(("CoreAudio: Failed to set input audio converter quality to the maximum (%RI32)\n", err));
1179#endif
1180 /* Set the new format description for the stream. */
1181 err = AudioUnitSetProperty(pStreamIn->audioUnit,
1182 kAudioUnitProperty_StreamFormat,
1183 kAudioUnitScope_Output,
1184 1,
1185 &pStreamIn->deviceFormat,
1186 sizeof(pStreamIn->deviceFormat));
1187 if (RT_UNLIKELY(err != noErr))
1188 {
1189 LogRel(("CoreAudio: Failed to set input stream output format (%RI32)\n", err));
1190 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1191 }
1192
1193 err = AudioUnitSetProperty(pStreamIn->audioUnit,
1194 kAudioUnitProperty_StreamFormat,
1195 kAudioUnitScope_Input,
1196 1,
1197 &pStreamIn->deviceFormat,
1198 sizeof(pStreamIn->deviceFormat));
1199 if (RT_UNLIKELY(err != noErr))
1200 {
1201 LogRel(("CoreAudio: Failed to set stream input format (%RI32)\n", err));
1202 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1203 }
1204 }
1205 else
1206 {
1207
1208 /* Set the new output format description for the input stream. */
1209 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1210 1, &pStreamIn->streamFormat, sizeof(pStreamIn->streamFormat));
1211 if (err != noErr)
1212 {
1213 LogRel(("CoreAudio: Failed to set output format for input stream (%RI32)\n", err));
1214 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1215 }
1216 }
1217
1218 /*
1219 * Also set the frame buffer size off the device on our AudioUnit. This
1220 * should make sure that the frames count which we receive in the render
1221 * thread is as we like.
1222 */
1223 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1224 1, &cFrames, sizeof(cFrames));
1225 if (err != noErr)
1226 {
1227 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err));
1228 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1229 }
1230
1231 /* Finally initialize the new AudioUnit. */
1232 err = AudioUnitInitialize(pStreamIn->audioUnit);
1233 if (err != noErr)
1234 {
1235 LogRel(("CoreAudio: Failed to initialize audio unit for input stream (%RI32)\n", err));
1236 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1237 }
1238
1239 uSize = sizeof(pStreamIn->deviceFormat);
1240 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1241 1, &pStreamIn->deviceFormat, &uSize);
1242 if (err != noErr)
1243 {
1244 LogRel(("CoreAudio: Failed to get recording device format (%RI32)\n", err));
1245 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1246 }
1247
1248 /*
1249 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
1250 * the frame buffer size set in the previous calls. So finally get the
1251 * frame buffer size after the AudioUnit was initialized.
1252 */
1253 uSize = sizeof(cFrames);
1254 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1255 0, &cFrames, &uSize);
1256 if (err != noErr)
1257 {
1258 LogRel(("CoreAudio: Failed to get maximum frame buffer size from input audio device (%RI32)\n", err));
1259 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1260 }
1261
1262 /* Destroy any former internal ring buffer. */
1263 if (pStreamIn->pBuf)
1264 {
1265 RTCircBufDestroy(pStreamIn->pBuf);
1266 pStreamIn->pBuf = NULL;
1267 }
1268
1269 /* Calculate the ratio between the device and the stream sample rate. */
1270 pStreamIn->sampleRatio = pStreamIn->streamFormat.mSampleRate / pStreamIn->deviceFormat.mSampleRate;
1271
1272 /* Create the AudioBufferList structure with one buffer. */
1273 pStreamIn->bufferList.mNumberBuffers = 1;
1274 /* Initialize the buffer to nothing. */
1275 pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
1276 pStreamIn->bufferList.mBuffers[0].mDataByteSize = 0;
1277 pStreamIn->bufferList.mBuffers[0].mData = NULL;
1278
1279 /*
1280 * Make sure that the ring buffer is big enough to hold the recording
1281 * data. Compare the maximum frames per slice value with the frames
1282 * necessary when using the converter where the sample rate could differ.
1283 * The result is always multiplied by the channels per frame to get the
1284 * samples count.
1285 */
1286 cSamples = RT_MAX(cFrames,
1287 (cFrames * pStreamIn->deviceFormat.mBytesPerFrame * pStreamIn->sampleRatio)
1288 / pStreamIn->streamFormat.mBytesPerFrame)
1289 * pStreamIn->streamFormat.mChannelsPerFrame;
1290 if (!cSamples)
1291 {
1292 LogRel(("CoreAudio: Failed to determine samples buffer count input stream\n"));
1293 CA_BREAK_STMT(rc = VERR_INVALID_PARAMETER);
1294 }
1295
1296 /* Create the internal ring buffer. */
1297 rc = RTCircBufCreate(&pStreamIn->pBuf, cSamples << pStream->Props.cShift);
1298
1299 } while (0);
1300
1301 if (RT_SUCCESS(rc))
1302 {
1303 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_INIT);
1304
1305 LogFunc(("cSamples=%RU32\n", cSamples));
1306
1307 if (pcSamples)
1308 *pcSamples = cSamples;
1309 }
1310 else
1311 {
1312 AudioUnitUninitialize(pStreamIn->audioUnit);
1313
1314 if (pStreamIn->pBuf)
1315 {
1316 RTCircBufDestroy(pStreamIn->pBuf);
1317 pStreamIn->pBuf = NULL;
1318 }
1319
1320 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
1321 }
1322
1323 LogFunc(("rc=%Rrc\n", rc));
1324 return rc;
1325}
1326
1327/** @todo Eventually split up this function, as this already is huge! */
1328static int coreAudioInitOut(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, uint32_t *pcSamples)
1329{
1330 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1331
1332 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_INIT);
1333
1334 OSStatus err = noErr;
1335
1336 UInt32 uSize = 0;
1337 if (pStreamOut->deviceID == kAudioDeviceUnknown)
1338 {
1339 /* Fetch the default audio recording device currently in use. */
1340 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
1341 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1342 uSize = sizeof(pStreamOut->deviceID);
1343 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &pStreamOut->deviceID);
1344 if (err != noErr)
1345 {
1346 LogRel(("CoreAudio: Unable to determine default playback device (%RI32)\n", err));
1347 return VERR_NOT_FOUND;
1348 }
1349 }
1350
1351 /*
1352 * Try to get the name of the playback device and log it. It's not fatal if it fails.
1353 */
1354 CFStringRef strTemp;
1355
1356 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
1357 kAudioObjectPropertyElementMaster };
1358 uSize = sizeof(CFStringRef);
1359 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1360 if (err == noErr)
1361 {
1362 char *pszDevName = NULL;
1363 err = coreAudioCFStringToCString(strTemp, &pszDevName);
1364 if (err == noErr)
1365 {
1366 CFRelease(strTemp);
1367
1368 /* Get the device' UUID. */
1369 propAdr.mSelector = kAudioDevicePropertyDeviceUID;
1370 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1371 if (err == noErr)
1372 {
1373 char *pszUID = NULL;
1374 err = coreAudioCFStringToCString(strTemp, &pszUID);
1375 if (err == noErr)
1376 {
1377 CFRelease(strTemp);
1378 LogRel(("CoreAudio: Using playback device: %s (UID: %s)\n", pszDevName, pszUID));
1379
1380 RTMemFree(pszUID);
1381 }
1382 }
1383
1384 RTMemFree(pszDevName);
1385 }
1386 }
1387 else
1388 LogRel(("CoreAudio: Unable to determine playback device name (%RI32)\n", err));
1389
1390 /* Get the default frames buffer size, so that we can setup our internal buffers. */
1391 UInt32 cFrames;
1392 uSize = sizeof(cFrames);
1393 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
1394 propAdr.mScope = kAudioDevicePropertyScopeInput;
1395 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
1396 if (err != noErr)
1397 {
1398 LogRel(("CoreAudio: Failed to determine frame buffer size of the audio playback device (%RI32)\n", err));
1399 return VERR_AUDIO_BACKEND_INIT_FAILED;
1400 }
1401
1402 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
1403 err = coreAudioSetFrameBufferSize(pStreamOut->deviceID, false /* fInput */, cFrames, &cFrames);
1404 if (err != noErr)
1405 {
1406 LogRel(("CoreAudio: Failed to set frame buffer size for the audio playback device (%RI32)\n", err));
1407 return VERR_AUDIO_BACKEND_INIT_FAILED;
1408 }
1409
1410 ComponentDescription cd;
1411 RT_ZERO(cd);
1412 cd.componentType = kAudioUnitType_Output;
1413 cd.componentSubType = kAudioUnitSubType_HALOutput;
1414 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
1415
1416 /* Try to find the default HAL output component. */
1417 Component cp = FindNextComponent(NULL, &cd);
1418 if (cp == 0)
1419 {
1420 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
1421 return VERR_AUDIO_BACKEND_INIT_FAILED;
1422 }
1423
1424 /* Open the default HAL output component. */
1425 err = OpenAComponent(cp, &pStreamOut->audioUnit);
1426 if (err != noErr)
1427 {
1428 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
1429 return VERR_AUDIO_BACKEND_INIT_FAILED;
1430 }
1431
1432 /* Switch the I/O mode for output to on. */
1433 UInt32 uFlag = 1;
1434 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
1435 0, &uFlag, sizeof(uFlag));
1436 if (err != noErr)
1437 {
1438 LogRel(("CoreAudio: Failed to disable I/O mode for output stream (%RI32)\n", err));
1439 return VERR_AUDIO_BACKEND_INIT_FAILED;
1440 }
1441
1442 /* Set the default audio playback device as the device for the new AudioUnit. */
1443 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
1444 0, &pStreamOut->deviceID, sizeof(pStreamOut->deviceID));
1445 if (err != noErr)
1446 {
1447 LogRel(("CoreAudio: Failed to set current device for output stream (%RI32)\n", err));
1448 return VERR_AUDIO_BACKEND_INIT_FAILED;
1449 }
1450
1451 /*
1452 * CoreAudio will inform us on a second thread for new incoming audio data.
1453 * Therefor register a callback function which will process the new data.
1454 */
1455 AURenderCallbackStruct cb;
1456 RT_ZERO(cb);
1457 cb.inputProc = coreAudioPlaybackCb; /* pvUser */
1458 cb.inputProcRefCon = pStreamOut;
1459
1460 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1461 0, &cb, sizeof(cb));
1462 if (err != noErr)
1463 {
1464 LogRel(("CoreAudio: Failed to register output callback (%RI32)\n", err));
1465 return VERR_AUDIO_BACKEND_INIT_FAILED;
1466 }
1467
1468 /* Fetch the current stream format of the device. */
1469 uSize = sizeof(pStreamOut->deviceFormat);
1470 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1471 0, &pStreamOut->deviceFormat, &uSize);
1472 if (err != noErr)
1473 {
1474 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
1475 return VERR_AUDIO_BACKEND_INIT_FAILED;
1476 }
1477
1478 /* Create an AudioStreamBasicDescription based on our required audio settings. */
1479 coreAudioPCMInfoToASBDesc(&pStreamOut->Stream.Props, &pStreamOut->streamFormat);
1480
1481 coreAudioPrintASBDesc("CoreAudio: playback device", &pStreamOut->deviceFormat);
1482 coreAudioPrintASBDesc("CoreAudio: Output format", &pStreamOut->streamFormat);
1483
1484 /* Set the new output format description for the stream. */
1485 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1486 0, &pStreamOut->streamFormat, sizeof(pStreamOut->streamFormat));
1487 if (err != noErr)
1488 {
1489 LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err));
1490 return VERR_AUDIO_BACKEND_INIT_FAILED;
1491 }
1492
1493 uSize = sizeof(pStreamOut->deviceFormat);
1494 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1495 0, &pStreamOut->deviceFormat, &uSize);
1496 if (err != noErr)
1497 {
1498 LogRel(("CoreAudio: Failed to retrieve device format for output stream (%RI32)\n", err));
1499 return VERR_AUDIO_BACKEND_INIT_FAILED;
1500 }
1501
1502 /*
1503 * Also set the frame buffer size off the device on our AudioUnit. This
1504 * should make sure that the frames count which we receive in the render
1505 * thread is as we like.
1506 */
1507 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1508 0, &cFrames, sizeof(cFrames));
1509 if (err != noErr)
1510 {
1511 LogRel(("CoreAudio: Failed to set maximum frame buffer size for output AudioUnit (%RI32)\n", err));
1512 return VERR_AUDIO_BACKEND_INIT_FAILED;
1513 }
1514
1515 /* Finally initialize the new AudioUnit. */
1516 err = AudioUnitInitialize(pStreamOut->audioUnit);
1517 if (err != noErr)
1518 {
1519 LogRel(("CoreAudio: Failed to initialize the output audio device (%RI32)\n", err));
1520 return VERR_AUDIO_BACKEND_INIT_FAILED;
1521 }
1522
1523 /*
1524 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
1525 * the frame buffer size set in the previous calls. So finally get the
1526 * frame buffer size after the AudioUnit was initialized.
1527 */
1528 uSize = sizeof(cFrames);
1529 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1530 0, &cFrames, &uSize);
1531 if (err != noErr)
1532 {
1533 LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err));
1534
1535 AudioUnitUninitialize(pStreamOut->audioUnit);
1536 return VERR_AUDIO_BACKEND_INIT_FAILED;
1537 }
1538
1539 /*
1540 * Make sure that the ring buffer is big enough to hold the recording
1541 * data. Compare the maximum frames per slice value with the frames
1542 * necessary when using the converter where the sample rate could differ.
1543 * The result is always multiplied by the channels per frame to get the
1544 * samples count.
1545 */
1546 int rc = VINF_SUCCESS;
1547
1548 UInt32 cSamples = cFrames * pStreamOut->streamFormat.mChannelsPerFrame;
1549 if (!cSamples)
1550 {
1551 LogRel(("CoreAudio: Failed to determine samples buffer count output stream\n"));
1552 rc = VERR_INVALID_PARAMETER;
1553 }
1554
1555 /* Destroy any former internal ring buffer. */
1556 if (pStreamOut->pBuf)
1557 {
1558 RTCircBufDestroy(pStreamOut->pBuf);
1559 pStreamOut->pBuf = NULL;
1560 }
1561
1562 /* Create the internal ring buffer. */
1563 rc = RTCircBufCreate(&pStreamOut->pBuf, cSamples << pStream->Props.cShift);
1564 if (RT_SUCCESS(rc))
1565 {
1566 /*
1567 * Register callbacks.
1568 */
1569#ifdef DEBUG
1570 propAdr.mSelector = kAudioDeviceProcessorOverload;
1571 propAdr.mScope = kAudioUnitScope_Global;
1572 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1573 coreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1574 if (err != noErr)
1575 LogRel(("CoreAudio: Failed to register processor overload listener for output stream (%RI32)\n", err));
1576#endif /* DEBUG */
1577
1578 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1579 propAdr.mScope = kAudioUnitScope_Global;
1580 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1581 coreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1582 /* Not fatal. */
1583 if (err != noErr)
1584 LogRel(("CoreAudio: Failed to register sample rate changed listener for output stream (%RI32)\n", err));
1585 }
1586
1587 if (RT_SUCCESS(rc))
1588 {
1589 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_INIT);
1590
1591 if (pcSamples)
1592 *pcSamples = cSamples;
1593 }
1594 else
1595 {
1596 AudioUnitUninitialize(pStreamOut->audioUnit);
1597
1598 if (pStreamOut->pBuf)
1599 {
1600 RTCircBufDestroy(pStreamOut->pBuf);
1601 pStreamOut->pBuf = NULL;
1602 }
1603 }
1604
1605 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
1606 return rc;
1607}
1608
1609/* Callback for getting notified when some of the properties of an audio device has changed. */
1610static DECLCALLBACK(OSStatus) coreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID,
1611 UInt32 nAddresses,
1612 const AudioObjectPropertyAddress properties[],
1613 void *pvUser)
1614{
1615 switch (propertyID)
1616 {
1617#ifdef DEBUG
1618#endif /* DEBUG */
1619 default:
1620 break;
1621 }
1622
1623 return noErr;
1624}
1625
1626/* Callback to feed audio output buffer. */
1627static DECLCALLBACK(OSStatus) coreAudioPlaybackCb(void *pvUser,
1628 AudioUnitRenderActionFlags *pActionFlags,
1629 const AudioTimeStamp *pAudioTS,
1630 UInt32 uBusID,
1631 UInt32 cFrames,
1632 AudioBufferList *pBufData)
1633{
1634 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pvUser;
1635 PPDMAUDIOSTREAM pStream = &pStreamOut->Stream;
1636
1637 if (ASMAtomicReadU32(&pStreamOut->status) != CA_STATUS_INIT)
1638 {
1639 pBufData->mBuffers[0].mDataByteSize = 0;
1640 return noErr;
1641 }
1642
1643 /* How much space is used in the ring buffer? */
1644 size_t cbToRead = RT_MIN(RTCircBufUsed(pStreamOut->pBuf), pBufData->mBuffers[0].mDataByteSize);
1645 if (!cbToRead)
1646 {
1647 pBufData->mBuffers[0].mDataByteSize = 0;
1648 return noErr;
1649 }
1650
1651 uint8_t *pbSrc = NULL;
1652 size_t cbRead = 0;
1653
1654 size_t cbLeft = cbToRead;
1655 while (cbLeft)
1656 {
1657 /* Try to acquire the necessary block from the ring buffer. */
1658 RTCircBufAcquireReadBlock(pStreamOut->pBuf, cbLeft, (void **)&pbSrc, &cbToRead);
1659
1660 /* Break if nothing is used anymore. */
1661 if (!cbToRead)
1662 break;
1663
1664 /* Copy the data from our ring buffer to the core audio buffer. */
1665 memcpy((uint8_t *)pBufData->mBuffers[0].mData + cbRead, pbSrc, cbToRead);
1666
1667 /* Release the read buffer, so it could be used for new data. */
1668 RTCircBufReleaseReadBlock(pStreamOut->pBuf, cbToRead);
1669
1670 /* Move offset. */
1671 cbRead += cbToRead;
1672 Assert(pBufData->mBuffers[0].mDataByteSize >= cbRead);
1673
1674 Assert(cbToRead <= cbLeft);
1675 cbLeft -= cbToRead;
1676 }
1677
1678 /* Write the bytes to the core audio buffer which where really written. */
1679 pBufData->mBuffers[0].mDataByteSize = cbRead;
1680
1681 LogFlowFunc(("Read %zu / %zu bytes\n", cbRead, cbToRead));
1682
1683 return noErr;
1684}
1685
1686static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)
1687{
1688 NOREF(pInterface);
1689
1690 LogFlowFuncEnter();
1691
1692 return VINF_SUCCESS;
1693}
1694
1695static DECLCALLBACK(int) drvHostCoreAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1696 uint32_t *pcSamplesCaptured)
1697{
1698 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1699 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1700
1701 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1702
1703 size_t csReads = 0;
1704 char *pcSrc;
1705 PPDMAUDIOSAMPLE psDst;
1706
1707 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
1708 {
1709 if (pcSamplesCaptured)
1710 *pcSamplesCaptured = 0;
1711 return VINF_SUCCESS;
1712 }
1713
1714 int rc = VINF_SUCCESS;
1715 uint32_t cbWrittenTotal = 0;
1716
1717 do
1718 {
1719 size_t cbBuf = AudioMixBufSizeBytes(&pStream->MixBuf);
1720 size_t cbToWrite = RT_MIN(cbBuf, RTCircBufUsed(pStreamIn->pBuf));
1721
1722 uint32_t cWritten, cbWritten;
1723 uint8_t *puBuf;
1724 size_t cbToRead;
1725
1726 Log3Func(("cbBuf=%zu, cbToWrite=%zu\n", cbBuf, cbToWrite));
1727
1728 while (cbToWrite)
1729 {
1730 /* Try to acquire the necessary block from the ring buffer. */
1731 RTCircBufAcquireReadBlock(pStreamIn->pBuf, cbToWrite, (void **)&puBuf, &cbToRead);
1732 if (!cbToRead)
1733 {
1734 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbToRead);
1735 break;
1736 }
1737
1738 rc = AudioMixBufWriteCirc(&pStream->MixBuf, puBuf, cbToRead, &cWritten);
1739 if ( RT_FAILURE(rc)
1740 || !cWritten)
1741 {
1742 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbToRead);
1743 break;
1744 }
1745
1746 cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
1747
1748 /* Release the read buffer, so it could be used for new data. */
1749 RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbWritten);
1750
1751 Assert(cbToWrite >= cbWritten);
1752 cbToWrite -= cbWritten;
1753 cbWrittenTotal += cbWritten;
1754 }
1755
1756 Log3Func(("cbToWrite=%zu, cbToRead=%zu, cbWrittenTotal=%RU32, rc=%Rrc\n", cbToWrite, cbToRead, cbWrittenTotal, rc));
1757 }
1758 while (0);
1759
1760 if (RT_SUCCESS(rc))
1761 {
1762 uint32_t cCaptured = 0;
1763 uint32_t cWrittenTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbWrittenTotal);
1764 if (cWrittenTotal)
1765 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal, &cCaptured);
1766
1767 Log3Func(("cWrittenTotal=%RU32 (%RU32 bytes), cCaptured=%RU32, rc=%Rrc\n", cWrittenTotal, cbWrittenTotal, cCaptured, rc));
1768
1769 if (cCaptured)
1770 LogFlowFunc(("%RU32 samples captured\n", cCaptured));
1771
1772 if (pcSamplesCaptured)
1773 *pcSamplesCaptured = cCaptured;
1774 }
1775
1776 if (RT_FAILURE(rc))
1777 LogFunc(("Failed with rc=%Rrc\n", rc));
1778
1779 return rc;
1780}
1781
1782static DECLCALLBACK(int) drvHostCoreAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1783 uint32_t *pcSamplesPlayed)
1784{
1785 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1786 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
1787
1788 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1789
1790 int rc = VINF_SUCCESS;
1791
1792 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
1793 if (!cLive) /* Not live samples to play? Bail out. */
1794 {
1795 if (pcSamplesPlayed)
1796 *pcSamplesPlayed = 0;
1797 return VINF_SUCCESS;
1798 }
1799
1800 size_t cbLive = AUDIOMIXBUF_S2B(&pStream->MixBuf, cLive);
1801
1802 uint32_t cbReadTotal = 0;
1803
1804 size_t cbToRead = RT_MIN(cbLive, RTCircBufFree(pStreamOut->pBuf));
1805 LogFlowFunc(("cbToRead=%zu\n", cbToRead));
1806
1807 while (cbToRead)
1808 {
1809 uint32_t cRead, cbRead;
1810 uint8_t *puBuf;
1811 size_t cbCopy;
1812
1813 /* Try to acquire the necessary space from the ring buffer. */
1814 RTCircBufAcquireWriteBlock(pStreamOut->pBuf, cbToRead, (void **)&puBuf, &cbCopy);
1815 if (!cbCopy)
1816 {
1817 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbCopy);
1818 break;
1819 }
1820
1821 Assert(cbCopy <= cbToRead);
1822
1823 rc = AudioMixBufReadCirc(&pStream->MixBuf,
1824 puBuf, cbCopy, &cRead);
1825
1826 if ( RT_FAILURE(rc)
1827 || !cRead)
1828 {
1829 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, 0);
1830 break;
1831 }
1832
1833 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
1834
1835 /* Release the ring buffer, so the read thread could start reading this data. */
1836 RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbRead);
1837
1838 Assert(cbToRead >= cbRead);
1839 cbToRead -= cbRead;
1840 cbReadTotal += cbRead;
1841 }
1842
1843 if (RT_SUCCESS(rc))
1844 {
1845 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
1846 if (cReadTotal)
1847 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
1848
1849 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes)\n", cReadTotal, cbReadTotal));
1850
1851 if (pcSamplesPlayed)
1852 *pcSamplesPlayed = cReadTotal;
1853 }
1854
1855 return rc;
1856}
1857
1858static DECLCALLBACK(int) coreAudioControlStreamOut(PDRVHOSTCOREAUDIO pThis,
1859 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1860{
1861 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1862
1863 LogFlowFunc(("enmStreamCmd=%RU32\n", enmStreamCmd));
1864
1865 uint32_t uStatus = ASMAtomicReadU32(&pStreamOut->status);
1866 if (!( uStatus == CA_STATUS_INIT
1867 || uStatus == CA_STATUS_REINIT))
1868 {
1869 return VINF_SUCCESS;
1870 }
1871
1872 int rc = VINF_SUCCESS;
1873 OSStatus err;
1874
1875 switch (enmStreamCmd)
1876 {
1877 case PDMAUDIOSTREAMCMD_ENABLE:
1878 case PDMAUDIOSTREAMCMD_RESUME:
1879 {
1880 /* Only start the device if it is actually stopped */
1881 if (!coreAudioIsRunning(pStreamOut->deviceID))
1882 {
1883 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1884 if (err != noErr)
1885 {
1886 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1887 /* Keep going. */
1888 }
1889 RTCircBufReset(pStreamOut->pBuf);
1890
1891 err = AudioOutputUnitStart(pStreamOut->audioUnit);
1892 if (RT_UNLIKELY(err != noErr))
1893 {
1894 LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));
1895 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1896 }
1897 }
1898 break;
1899 }
1900
1901 case PDMAUDIOSTREAMCMD_DISABLE:
1902 case PDMAUDIOSTREAMCMD_PAUSE:
1903 {
1904 /* Only stop the device if it is actually running */
1905 if (coreAudioIsRunning(pStreamOut->deviceID))
1906 {
1907 err = AudioOutputUnitStop(pStreamOut->audioUnit);
1908 if (err != noErr)
1909 {
1910 LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
1911 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1912 break;
1913 }
1914
1915 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
1916 if (err != noErr)
1917 {
1918 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1919 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1920 }
1921 }
1922 break;
1923 }
1924
1925 default:
1926 rc = VERR_NOT_SUPPORTED;
1927 break;
1928 }
1929
1930 LogFlowFuncLeaveRC(rc);
1931 return rc;
1932}
1933
1934static int coreAudioControlStreamIn(PDRVHOSTCOREAUDIO pThis,
1935 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1936{
1937 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1938
1939 LogFlowFunc(("enmStreamCmd=%RU32\n", enmStreamCmd));
1940
1941 uint32_t uStatus = ASMAtomicReadU32(&pStreamIn->status);
1942 if (!( uStatus == CA_STATUS_INIT
1943 || uStatus == CA_STATUS_REINIT))
1944 {
1945 return VINF_SUCCESS;
1946 }
1947
1948 int rc = VINF_SUCCESS;
1949 OSStatus err = noErr;
1950
1951 switch (enmStreamCmd)
1952 {
1953 case PDMAUDIOSTREAMCMD_ENABLE:
1954 case PDMAUDIOSTREAMCMD_RESUME:
1955 {
1956 /* Only start the device if it is actually stopped */
1957 if (!coreAudioIsRunning(pStreamIn->deviceID))
1958 {
1959 RTCircBufReset(pStreamIn->pBuf);
1960 err = AudioOutputUnitStart(pStreamIn->audioUnit);
1961 if (err != noErr)
1962 {
1963 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1964 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1965 break;
1966 }
1967 }
1968
1969 if (err != noErr)
1970 {
1971 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
1972 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1973 }
1974 break;
1975 }
1976
1977 case PDMAUDIOSTREAMCMD_DISABLE:
1978 case PDMAUDIOSTREAMCMD_PAUSE:
1979 {
1980 /* Only stop the device if it is actually running */
1981 if (coreAudioIsRunning(pStreamIn->deviceID))
1982 {
1983 err = AudioOutputUnitStop(pStreamIn->audioUnit);
1984 if (err != noErr)
1985 {
1986 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
1987 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1988 break;
1989 }
1990
1991 err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);
1992 if (err != noErr)
1993 {
1994 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
1995 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1996 break;
1997 }
1998 }
1999 break;
2000 }
2001
2002 default:
2003 rc = VERR_NOT_SUPPORTED;
2004 break;
2005 }
2006
2007 LogFlowFuncLeaveRC(rc);
2008 return rc;
2009}
2010
2011static int coreAudioDestroyStreamIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream)
2012{
2013 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2014
2015 LogFlowFuncEnter();
2016
2017 uint32_t status = ASMAtomicReadU32(&pStreamIn->status);
2018 if (!( status == CA_STATUS_INIT
2019 || status == CA_STATUS_REINIT))
2020 {
2021 return VINF_SUCCESS;
2022 }
2023
2024 OSStatus err = noErr;
2025
2026 int rc = coreAudioControlStreamIn(pThis, &pStreamIn->Stream, PDMAUDIOSTREAMCMD_DISABLE);
2027 if (RT_SUCCESS(rc))
2028 {
2029 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);
2030
2031 /*
2032 * Unregister recording device callbacks.
2033 */
2034 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
2035 kAudioObjectPropertyElementMaster };
2036#ifdef DEBUG
2037 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
2038 coreAudioRecordingAudioDevicePropertyChanged, &pStreamIn->cbCtx);
2039 if ( err != noErr
2040 && err != kAudioHardwareBadObjectError)
2041 {
2042 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err));
2043 }
2044#endif /* DEBUG */
2045
2046 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
2047 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
2048 coreAudioRecordingAudioDevicePropertyChanged, &pStreamIn->cbCtx);
2049 if ( err != noErr
2050 && err != kAudioHardwareBadObjectError)
2051 {
2052 LogRel(("CoreAudio: Failed to remove the recording sample rate changed listener (%RI32)\n", err));
2053 }
2054
2055 if (pStreamIn->fDefDevChgListReg)
2056 {
2057 propAdr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
2058 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
2059 coreAudioDefaultDeviceChanged, &pStreamIn->cbCtx);
2060 if ( err != noErr
2061 && err != kAudioHardwareBadObjectError)
2062 {
2063 LogRel(("CoreAudio: Failed to remove the default recording device changed listener (%RI32)\n", err));
2064 }
2065
2066 pStreamIn->fDefDevChgListReg = false;
2067 }
2068
2069 if (pStreamIn->fDevStateChgListReg)
2070 {
2071 Assert(pStreamIn->deviceID != kAudioDeviceUnknown);
2072
2073 AudioObjectPropertyAddress propAdr2 = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2074 kAudioObjectPropertyElementMaster };
2075 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr2,
2076 drvHostCoreAudioDeviceStateChanged, &pStreamIn->cbCtx);
2077 if ( err != noErr
2078 && err != kAudioHardwareBadObjectError)
2079 {
2080 LogRel(("CoreAudio: Failed to remove the recording device state changed listener (%RI32)\n", err));
2081 }
2082
2083 pStreamIn->fDevStateChgListReg = false;
2084 }
2085
2086 if (pStreamIn->pConverter)
2087 {
2088 AudioConverterDispose(pStreamIn->pConverter);
2089 pStreamIn->pConverter = NULL;
2090 }
2091
2092 err = AudioUnitUninitialize(pStreamIn->audioUnit);
2093 if (err == noErr)
2094 err = CloseComponent(pStreamIn->audioUnit);
2095
2096 if ( err != noErr
2097 && err != kAudioHardwareBadObjectError)
2098 {
2099 LogRel(("CoreAudio: Failed to uninit the recording device (%RI32)\n", err));
2100 }
2101
2102 pStreamIn->deviceID = kAudioDeviceUnknown;
2103 pStreamIn->audioUnit = NULL;
2104 pStreamIn->offBufferRead = 0;
2105 pStreamIn->sampleRatio = 1;
2106 if (pStreamIn->pBuf)
2107 {
2108 RTCircBufDestroy(pStreamIn->pBuf);
2109 pStreamIn->pBuf = NULL;
2110 }
2111
2112 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
2113 }
2114 else
2115 LogRel(("CoreAudio: Failed to stop recording on uninit, rc=%Rrc\n", rc));
2116
2117 LogFlowFuncLeaveRC(rc);
2118 return rc;
2119}
2120
2121static int coreAudioDestroyStreamOut(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream)
2122{
2123 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2124
2125 LogFlowFuncEnter();
2126
2127 uint32_t status = ASMAtomicReadU32(&pStreamOut->status);
2128 if (!( status == CA_STATUS_INIT
2129 || status == CA_STATUS_REINIT))
2130 {
2131 return VINF_SUCCESS;
2132 }
2133
2134 int rc = coreAudioControlStreamOut(pThis, &pStreamOut->Stream, PDMAUDIOSTREAMCMD_DISABLE);
2135 if (RT_SUCCESS(rc))
2136 {
2137 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_UNINIT);
2138
2139 OSStatus err;
2140
2141 /*
2142 * Unregister playback device callbacks.
2143 */
2144 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
2145 kAudioObjectPropertyElementMaster };
2146#ifdef DEBUG
2147 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
2148 coreAudioPlaybackAudioDevicePropertyChanged, &pStreamOut->cbCtx);
2149 if ( err != noErr
2150 && err != kAudioHardwareBadObjectError)
2151 {
2152 LogRel(("CoreAudio: Failed to remove the playback processor overload listener (%RI32)\n", err));
2153 }
2154#endif /* DEBUG */
2155
2156 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
2157 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
2158 coreAudioPlaybackAudioDevicePropertyChanged, &pStreamOut->cbCtx);
2159 if ( err != noErr
2160 && err != kAudioHardwareBadObjectError)
2161 {
2162 LogRel(("CoreAudio: Failed to remove the playback sample rate changed listener (%RI32)\n", err));
2163 }
2164
2165 if (pStreamOut->fDefDevChgListReg)
2166 {
2167 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
2168 propAdr.mScope = kAudioObjectPropertyScopeGlobal;
2169 propAdr.mElement = kAudioObjectPropertyElementMaster;
2170 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
2171 coreAudioDefaultDeviceChanged, &pStreamOut->cbCtx);
2172 if ( err != noErr
2173 && err != kAudioHardwareBadObjectError)
2174 {
2175 LogRel(("CoreAudio: Failed to remove the default playback device changed listener (%RI32)\n", err));
2176 }
2177
2178 pStreamOut->fDefDevChgListReg = false;
2179 }
2180
2181 if (pStreamOut->fDevStateChgListReg)
2182 {
2183 Assert(pStreamOut->deviceID != kAudioDeviceUnknown);
2184
2185 AudioObjectPropertyAddress propAdr2 = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2186 kAudioObjectPropertyElementMaster };
2187 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr2,
2188 drvHostCoreAudioDeviceStateChanged, &pStreamOut->cbCtx);
2189 if ( err != noErr
2190 && err != kAudioHardwareBadObjectError)
2191 {
2192 LogRel(("CoreAudio: Failed to remove the playback device state changed listener (%RI32)\n", err));
2193 }
2194
2195 pStreamOut->fDevStateChgListReg = false;
2196 }
2197
2198 err = AudioUnitUninitialize(pStreamOut->audioUnit);
2199 if (err == noErr)
2200 err = CloseComponent(pStreamOut->audioUnit);
2201
2202 if ( err != noErr
2203 && err != kAudioHardwareBadObjectError)
2204 {
2205 LogRel(("CoreAudio: Failed to uninit the playback device (%RI32)\n", err));
2206 }
2207
2208 pStreamOut->deviceID = kAudioDeviceUnknown;
2209 pStreamOut->audioUnit = NULL;
2210 if (pStreamOut->pBuf)
2211 {
2212 RTCircBufDestroy(pStreamOut->pBuf);
2213 pStreamOut->pBuf = NULL;
2214 }
2215
2216 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
2217 }
2218 else
2219 LogRel(("CoreAudio: Failed to stop playback on uninit, rc=%Rrc\n", rc));
2220
2221 LogFlowFuncLeaveRC(rc);
2222 return rc;
2223}
2224
2225static int coreAudioCreateStreamIn(PDRVHOSTCOREAUDIO pThis,
2226 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
2227{
2228 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2229
2230 pStreamIn->deviceID = kAudioDeviceUnknown;
2231 pStreamIn->audioUnit = NULL;
2232 pStreamIn->pConverter = NULL;
2233 pStreamIn->bufferList.mNumberBuffers = 0;
2234 pStreamIn->offBufferRead = 0;
2235 pStreamIn->sampleRatio = 1;
2236 pStreamIn->pBuf = NULL;
2237 pStreamIn->status = CA_STATUS_UNINIT;
2238 pStreamIn->fDefDevChgListReg = false;
2239 pStreamIn->fDevStateChgListReg = false;
2240
2241 /* Set callback context. */
2242 pStreamIn->cbCtx.pThis = pThis;
2243 pStreamIn->cbCtx.enmDir = PDMAUDIODIR_IN;
2244 pStreamIn->cbCtx.pIn = pStreamIn;
2245
2246 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
2247
2248 LogFlowFunc(("enmRecSource=%RU32\n", pCfg->DestSource.Source));
2249
2250 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStreamIn->Stream.Props);
2251 if (RT_SUCCESS(rc))
2252 {
2253#if 0
2254 /* Try to find the audio device set by the user */
2255 if (DeviceUID.pszInputDeviceUID)
2256 {
2257 pStreamIn->deviceID = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszInputDeviceUID);
2258 /* Not fatal */
2259 if (pStreamIn->deviceID == kAudioDeviceUnknown)
2260 LogRel(("CoreAudio: Unable to find recording device %s. Falling back to the default audio device. \n", DeviceUID.pszInputDeviceUID));
2261 else
2262 fDeviceByUser = true;
2263 }
2264#endif
2265 rc = coreAudioInitIn(pThis, &pStreamIn->Stream, pcSamples);
2266 }
2267
2268 OSStatus err;
2269
2270 /* When the devices isn't forced by the user, we want default device change notifications. */
2271 if (!fDeviceByUser)
2272 {
2273 if (!pStreamIn->fDefDevChgListReg)
2274 {
2275 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
2276 kAudioObjectPropertyElementMaster };
2277 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2278 coreAudioDefaultDeviceChanged, &pStreamIn->cbCtx);
2279 if ( err == noErr
2280 || err == kAudioHardwareIllegalOperationError)
2281 {
2282 pStreamIn->fDefDevChgListReg = true;
2283 }
2284 else
2285 LogRel(("CoreAudio: Failed to add the default recording device changed listener (%RI32)\n", err));
2286 }
2287 }
2288
2289 if ( !pStreamIn->fDevStateChgListReg
2290 && (pStreamIn->deviceID != kAudioDeviceUnknown))
2291 {
2292 /* Register callback for being notified if the device stops being alive. */
2293 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2294 kAudioObjectPropertyElementMaster };
2295 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr, drvHostCoreAudioDeviceStateChanged,
2296 &pStreamIn->cbCtx);
2297 if (err == noErr)
2298 {
2299 pStreamIn->fDevStateChgListReg = true;
2300 }
2301 else
2302 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err));
2303 }
2304
2305 LogFlowFuncLeaveRC(rc);
2306 return rc;
2307}
2308
2309static int coreAudioCreateStreamOut(PDRVHOSTCOREAUDIO pThis,
2310 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg,
2311 uint32_t *pcSamples)
2312{
2313 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2314
2315 LogFlowFuncEnter();
2316
2317 pStreamOut->deviceID = kAudioDeviceUnknown;
2318 pStreamOut->audioUnit = NULL;
2319 pStreamOut->pBuf = NULL;
2320 pStreamOut->status = CA_STATUS_UNINIT;
2321 pStreamOut->fDefDevChgListReg = false;
2322 pStreamOut->fDevStateChgListReg = false;
2323
2324 /* Set callback context. */
2325 pStreamOut->cbCtx.pThis = pThis;
2326 pStreamOut->cbCtx.enmDir = PDMAUDIODIR_OUT;
2327 pStreamOut->cbCtx.pOut = pStreamOut;
2328
2329 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
2330
2331 /* If a stream configuration is given, apply that to the stream. */
2332 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStreamOut->Stream.Props);
2333 if (RT_SUCCESS(rc))
2334 {
2335#if 0
2336 /* Try to find the audio device set by the user. Use
2337 * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0
2338 * to set it. */
2339 if (DeviceUID.pszOutputDeviceUID)
2340 {
2341 pStreamOut->audioDeviceId = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszOutputDeviceUID);
2342 /* Not fatal */
2343 if (pStreamOut->audioDeviceId == kAudioDeviceUnknown)
2344 LogRel(("CoreAudio: Unable to find playback device %s. Falling back to the default audio device. \n", DeviceUID.pszOutputDeviceUID));
2345 else
2346 fDeviceByUser = true;
2347 }
2348#endif
2349 rc = coreAudioInitOut(pThis, pStream, pcSamples);
2350 }
2351
2352 OSStatus err;
2353
2354 /* When the devices isn't forced by the user, we want default device change notifications. */
2355 if (!fDeviceByUser)
2356 {
2357 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
2358 kAudioObjectPropertyElementMaster };
2359 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2360 coreAudioDefaultDeviceChanged, &pStreamOut->cbCtx);
2361 if (err == noErr)
2362 {
2363 pStreamOut->fDefDevChgListReg = true;
2364 }
2365 else
2366 LogRel(("CoreAudio: Failed to add the default playback device changed listener (%RI32)\n", err));
2367 }
2368
2369 if ( !pStreamOut->fDevStateChgListReg
2370 && (pStreamOut->deviceID != kAudioDeviceUnknown))
2371 {
2372 /* Register callback for being notified if the device stops being alive. */
2373 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2374 kAudioObjectPropertyElementMaster };
2375 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr, drvHostCoreAudioDeviceStateChanged,
2376 &pStreamOut->cbCtx);
2377 if (err == noErr)
2378 {
2379 pStreamOut->fDevStateChgListReg = true;
2380 }
2381 else
2382 LogRel(("CoreAudio: Failed to add the playback device state changed listener (%RI32)\n", err));
2383 }
2384
2385 LogFlowFuncLeaveRC(rc);
2386 return rc;
2387}
2388
2389static DECLCALLBACK(int) drvHostCoreAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
2390{
2391 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2392 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2393
2394 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2395 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2396
2397 return coreAudioUpdateStatusInternalEx(pThis, pCfg, 0 /* fEnum */);
2398}
2399
2400static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2401{
2402 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2403
2404 return PDMAUDIOBACKENDSTS_RUNNING;
2405}
2406
2407static DECLCALLBACK(int) drvHostCoreAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
2408 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
2409{
2410 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2411 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2412 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2413
2414 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2415 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2416
2417 int rc;
2418 if (pCfg->enmDir == PDMAUDIODIR_IN)
2419 rc = coreAudioCreateStreamIn(pThis, pStream, pCfg, pcSamples);
2420 else
2421 rc = coreAudioCreateStreamOut(pThis, pStream, pCfg, pcSamples);
2422
2423 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
2424 return rc;
2425}
2426
2427static DECLCALLBACK(int) drvHostCoreAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2428{
2429 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2430 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2431
2432 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2433 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2434
2435 int rc;
2436 if (pStream->enmDir == PDMAUDIODIR_IN)
2437 rc = coreAudioDestroyStreamIn(pThis, pStream);
2438 else
2439 rc = coreAudioDestroyStreamOut(pThis, pStream);
2440
2441 return rc;
2442}
2443
2444static DECLCALLBACK(int) drvHostCoreAudioStreamControl(PPDMIHOSTAUDIO pInterface,
2445 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2446{
2447 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2448 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2449
2450 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
2451
2452 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2453 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2454
2455 int rc;
2456 if (pStream->enmDir == PDMAUDIODIR_IN)
2457 rc = coreAudioControlStreamIn(pThis, pStream, enmStreamCmd);
2458 else
2459 rc = coreAudioControlStreamOut(pThis, pStream, enmStreamCmd);
2460
2461 return rc;
2462}
2463
2464static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostCoreAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2465{
2466 NOREF(pInterface);
2467 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2468
2469 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
2470
2471 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE;
2472
2473 if (pStream->enmDir == PDMAUDIODIR_IN)
2474 {
2475 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2476
2477 if (ASMAtomicReadU32(&pStreamIn->status) == CA_STATUS_INIT)
2478 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED;
2479
2480 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED)
2481 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_READABLE;
2482 }
2483 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2484 {
2485 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2486
2487 if (ASMAtomicReadU32(&pStreamOut->status) == CA_STATUS_INIT)
2488 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED;
2489
2490 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED)
2491 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
2492 }
2493 else
2494 AssertFailed();
2495
2496 return strmSts;
2497}
2498
2499static DECLCALLBACK(int) drvHostCoreAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2500{
2501 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2502 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2503
2504 /* Nothing to do here for Core Audio. */
2505 return VINF_SUCCESS;
2506}
2507
2508static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
2509{
2510 NOREF(pInterface);
2511}
2512
2513static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2514{
2515 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2516 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2517
2518 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2519 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2520
2521 return NULL;
2522}
2523
2524 /* Construct a DirectSound Audio driver instance.
2525 *
2526 * @copydoc FNPDMDRVCONSTRUCT
2527 */
2528static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2529{
2530 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2531 LogRel(("Audio: Initializing Core Audio driver\n"));
2532
2533 /*
2534 * Init the static parts.
2535 */
2536 pThis->pDrvIns = pDrvIns;
2537 /* IBase */
2538 pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
2539 /* IHostAudio */
2540 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
2541
2542 return VINF_SUCCESS;
2543}
2544
2545/**
2546 * Char driver registration record.
2547 */
2548const PDMDRVREG g_DrvHostCoreAudio =
2549{
2550 /* u32Version */
2551 PDM_DRVREG_VERSION,
2552 /* szName */
2553 "CoreAudio",
2554 /* szRCMod */
2555 "",
2556 /* szR0Mod */
2557 "",
2558 /* pszDescription */
2559 "Core Audio host driver",
2560 /* fFlags */
2561 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2562 /* fClass. */
2563 PDM_DRVREG_CLASS_AUDIO,
2564 /* cMaxInstances */
2565 ~0U,
2566 /* cbInstance */
2567 sizeof(DRVHOSTCOREAUDIO),
2568 /* pfnConstruct */
2569 drvHostCoreAudioConstruct,
2570 /* pfnDestruct */
2571 NULL,
2572 /* pfnRelocate */
2573 NULL,
2574 /* pfnIOCtl */
2575 NULL,
2576 /* pfnPowerOn */
2577 NULL,
2578 /* pfnReset */
2579 NULL,
2580 /* pfnSuspend */
2581 NULL,
2582 /* pfnResume */
2583 NULL,
2584 /* pfnAttach */
2585 NULL,
2586 /* pfnDetach */
2587 NULL,
2588 /* pfnPowerOff */
2589 NULL,
2590 /* pfnSoftReset */
2591 NULL,
2592 /* u32EndVersion */
2593 PDM_DRVREG_VERSION
2594};
2595
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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