VirtualBox

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

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

Audio/DrvHostCoreAudio.cpp: Fixed a crash in default device changed notifications.

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

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