VirtualBox

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

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

vmm/pdmaudioifs.h: The prefix 'cf' reads 'count-of-flags', so if you want 'count-of-frames' in a non-confusing way you have to spell it out. The prefix 'cx' means 'count-of-x-coordinates', i.e. width in pixels, and is not in any way suited to combine 'cb' and 'cFrames' in this context (PDM). bugref:9218

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 91.6 KB
 
1/* $Id: DrvHostCoreAudio.cpp 82254 2019-11-27 22:09:17Z vboxsync $ */
2/** @file
3 * VBox audio devices - Mac OS X CoreAudio audio driver.
4 */
5
6/*
7 * Copyright (C) 2010-2019 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <VBox/log.h>
24
25#include "DrvAudio.h"
26#include "VBoxDD.h"
27
28#include <iprt/asm.h>
29#include <iprt/cdefs.h>
30#include <iprt/circbuf.h>
31#include <iprt/mem.h>
32
33#include <iprt/uuid.h>
34
35#include <CoreAudio/CoreAudio.h>
36#include <CoreServices/CoreServices.h>
37#include <AudioUnit/AudioUnit.h>
38#include <AudioToolbox/AudioConverter.h>
39#include <AudioToolbox/AudioToolbox.h>
40
41
42
43/* Enables utilizing the Core Audio converter unit for converting
44 * input / output from/to our requested formats. That might be more
45 * performant than using our own routines later down the road. */
46/** @todo Needs more investigation and testing first before enabling. */
47//# define VBOX_WITH_AUDIO_CA_CONVERTER
48
49/** @todo
50 * - Maybe make sure the threads are immediately stopped if playing/recording stops.
51 */
52
53/*
54 * Most of this is based on:
55 * http://developer.apple.com/mac/library/technotes/tn2004/tn2097.html
56 * http://developer.apple.com/mac/library/technotes/tn2002/tn2091.html
57 * http://developer.apple.com/mac/library/qa/qa2007/qa1533.html
58 * http://developer.apple.com/mac/library/qa/qa2001/qa1317.html
59 * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
60 */
61
62/* Prototypes needed for COREAUDIODEVICE. */
63struct DRVHOSTCOREAUDIO;
64typedef struct DRVHOSTCOREAUDIO *PDRVHOSTCOREAUDIO;
65
66/**
67 * Structure for holding Core Audio-specific device data.
68 * This data then lives in the pvData part of the PDMAUDIODEVICE struct.
69 */
70typedef struct COREAUDIODEVICEDATA
71{
72 /** Pointer to driver instance this device is bound to. */
73 PDRVHOSTCOREAUDIO pDrv;
74 /** The audio device ID of the currently used device (UInt32 typedef). */
75 AudioDeviceID deviceID;
76 /** The device' UUID. */
77 CFStringRef UUID;
78 /** List of attached (native) Core Audio streams attached to this device. */
79 RTLISTANCHOR lstStreams;
80} COREAUDIODEVICEDATA, *PCOREAUDIODEVICEDATA;
81
82/**
83 * Host Coreaudio driver instance data.
84 * @implements PDMIAUDIOCONNECTOR
85 */
86typedef struct DRVHOSTCOREAUDIO
87{
88 /** Pointer to the driver instance structure. */
89 PPDMDRVINS pDrvIns;
90 /** Pointer to host audio interface. */
91 PDMIHOSTAUDIO IHostAudio;
92 /** Critical section to serialize access. */
93 RTCRITSECT CritSect;
94 /** Current (last reported) device enumeration. */
95 PDMAUDIODEVICEENUM Devices;
96 /** Pointer to the currently used input device in the device enumeration.
97 * Can be NULL if none assigned. */
98 PPDMAUDIODEVICE pDefaultDevIn;
99 /** Pointer to the currently used output device in the device enumeration.
100 * Can be NULL if none assigned. */
101 PPDMAUDIODEVICE pDefaultDevOut;
102#ifdef VBOX_WITH_AUDIO_CALLBACKS
103 /** Callback function to the upper driver.
104 * Can be NULL if not being used / registered. */
105 PFNPDMHOSTAUDIOCALLBACK pfnCallback;
106#endif
107} DRVHOSTCOREAUDIO, *PDRVHOSTCOREAUDIO;
108
109/** Converts a pointer to DRVHOSTCOREAUDIO::IHostAudio to a PDRVHOSTCOREAUDIO. */
110#define PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTCOREAUDIO, IHostAudio)
111
112/**
113 * Structure for holding a Core Audio unit
114 * and its data.
115 */
116typedef struct COREAUDIOUNIT
117{
118 /** Pointer to the device this audio unit is bound to.
119 * Can be NULL if not bound to a device (anymore). */
120 PPDMAUDIODEVICE pDevice;
121 /** The actual audio unit object. */
122 AudioUnit audioUnit;
123 /** Stream description for using with VBox:
124 * - When using this audio unit for input (capturing), this format states
125 * the unit's output format.
126 * - When using this audio unit for output (playback), this format states
127 * the unit's input format. */
128 AudioStreamBasicDescription streamFmt;
129} COREAUDIOUNIT, *PCOREAUDIOUNIT;
130
131
132/*******************************************************************************
133 *
134 * Helper function section
135 *
136 ******************************************************************************/
137
138/* Move these down below the internal function prototypes... */
139
140static void coreAudioPrintASBD(const char *pszDesc, const AudioStreamBasicDescription *pASBD)
141{
142 char pszSampleRate[32];
143 LogRel2(("CoreAudio: %s description:\n", pszDesc));
144 LogRel2(("CoreAudio:\tFormat ID: %RU32 (%c%c%c%c)\n", pASBD->mFormatID,
145 RT_BYTE4(pASBD->mFormatID), RT_BYTE3(pASBD->mFormatID),
146 RT_BYTE2(pASBD->mFormatID), RT_BYTE1(pASBD->mFormatID)));
147 LogRel2(("CoreAudio:\tFlags: %RU32", pASBD->mFormatFlags));
148 if (pASBD->mFormatFlags & kAudioFormatFlagIsFloat)
149 LogRel2((" Float"));
150 if (pASBD->mFormatFlags & kAudioFormatFlagIsBigEndian)
151 LogRel2((" BigEndian"));
152 if (pASBD->mFormatFlags & kAudioFormatFlagIsSignedInteger)
153 LogRel2((" SignedInteger"));
154 if (pASBD->mFormatFlags & kAudioFormatFlagIsPacked)
155 LogRel2((" Packed"));
156 if (pASBD->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
157 LogRel2((" AlignedHigh"));
158 if (pASBD->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
159 LogRel2((" NonInterleaved"));
160 if (pASBD->mFormatFlags & kAudioFormatFlagIsNonMixable)
161 LogRel2((" NonMixable"));
162 if (pASBD->mFormatFlags & kAudioFormatFlagsAreAllClear)
163 LogRel2((" AllClear"));
164 LogRel2(("\n"));
165 snprintf(pszSampleRate, 32, "%.2f", (float)pASBD->mSampleRate); /** @todo r=andy Use RTStrPrint*. */
166 LogRel2(("CoreAudio:\tSampleRate : %s\n", pszSampleRate));
167 LogRel2(("CoreAudio:\tChannelsPerFrame: %RU32\n", pASBD->mChannelsPerFrame));
168 LogRel2(("CoreAudio:\tFramesPerPacket : %RU32\n", pASBD->mFramesPerPacket));
169 LogRel2(("CoreAudio:\tBitsPerChannel : %RU32\n", pASBD->mBitsPerChannel));
170 LogRel2(("CoreAudio:\tBytesPerFrame : %RU32\n", pASBD->mBytesPerFrame));
171 LogRel2(("CoreAudio:\tBytesPerPacket : %RU32\n", pASBD->mBytesPerPacket));
172}
173
174static void coreAudioPCMPropsToASBD(PDMAUDIOPCMPROPS *pPCMProps, AudioStreamBasicDescription *pASBD)
175{
176 AssertPtrReturnVoid(pPCMProps);
177 AssertPtrReturnVoid(pASBD);
178
179 RT_BZERO(pASBD, sizeof(AudioStreamBasicDescription));
180
181 pASBD->mFormatID = kAudioFormatLinearPCM;
182 pASBD->mFormatFlags = kAudioFormatFlagIsPacked;
183 pASBD->mFramesPerPacket = 1; /* For uncompressed audio, set this to 1. */
184 pASBD->mSampleRate = (Float64)pPCMProps->uHz;
185 pASBD->mChannelsPerFrame = pPCMProps->cChannels;
186 pASBD->mBitsPerChannel = pPCMProps->cbSample * 8;
187 if (pPCMProps->fSigned)
188 pASBD->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
189 pASBD->mBytesPerFrame = pASBD->mChannelsPerFrame * (pASBD->mBitsPerChannel / 8);
190 pASBD->mBytesPerPacket = pASBD->mFramesPerPacket * pASBD->mBytesPerFrame;
191}
192
193#ifndef VBOX_WITH_AUDIO_CALLBACKS
194static int coreAudioASBDToStreamCfg(AudioStreamBasicDescription *pASBD, PPDMAUDIOSTREAMCFG pCfg)
195{
196 AssertPtrReturn(pASBD, VERR_INVALID_PARAMETER);
197 AssertPtrReturn(pCfg, VERR_INVALID_PARAMETER);
198
199 pCfg->Props.cChannels = pASBD->mChannelsPerFrame;
200 pCfg->Props.uHz = (uint32_t)pASBD->mSampleRate;
201 AssertMsg(!(pASBD->mBitsPerChannel & 7), ("%u\n", pASBD->mBitsPerChannel));
202 pCfg->Props.cbSample = pASBD->mBitsPerChannel / 8;
203 pCfg->Props.fSigned = RT_BOOL(pASBD->mFormatFlags & kAudioFormatFlagIsSignedInteger);
204 pCfg->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfg->Props.cbSample, pCfg->Props.cChannels);
205 /** @todo r=bird: pCfg->Props.fSwapEndian is not initialized here! */
206
207 return VINF_SUCCESS;
208}
209#endif /* !VBOX_WITH_AUDIO_CALLBACKS */
210
211#if 0 /* unused */
212static int coreAudioCFStringToCString(const CFStringRef pCFString, char **ppszString)
213{
214 CFIndex cLen = CFStringGetLength(pCFString) + 1;
215 char *pszResult = (char *)RTMemAllocZ(cLen * sizeof(char));
216 if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
217 {
218 RTMemFree(pszResult);
219 return VERR_NOT_FOUND;
220 }
221
222 *ppszString = pszResult;
223 return VINF_SUCCESS;
224}
225
226static AudioDeviceID coreAudioDeviceUIDtoID(const char* pszUID)
227{
228 /* Create a CFString out of our CString. */
229 CFStringRef strUID = CFStringCreateWithCString(NULL, pszUID, kCFStringEncodingMacRoman);
230
231 /* Fill the translation structure. */
232 AudioDeviceID deviceID;
233
234 AudioValueTranslation translation;
235 translation.mInputData = &strUID;
236 translation.mInputDataSize = sizeof(CFStringRef);
237 translation.mOutputData = &deviceID;
238 translation.mOutputDataSize = sizeof(AudioDeviceID);
239
240 /* Fetch the translation from the UID to the device ID. */
241 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDeviceForUID, kAudioObjectPropertyScopeGlobal,
242 kAudioObjectPropertyElementMaster };
243
244 UInt32 uSize = sizeof(AudioValueTranslation);
245 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &translation);
246
247 /* Release the temporary CFString */
248 CFRelease(strUID);
249
250 if (RT_LIKELY(err == noErr))
251 return deviceID;
252
253 /* Return the unknown device on error. */
254 return kAudioDeviceUnknown;
255}
256#endif /* unused */
257
258
259/*********************************************************************************************************************************
260* Defined Constants And Macros *
261*********************************************************************************************************************************/
262
263/** @name Initialization status indicator used for the recreation of the AudioUnits.
264 *
265 * Global structures section
266 *
267 ******************************************************************************/
268
269/**
270 * Enumeration for a Core Audio stream status.
271 */
272typedef enum COREAUDIOSTATUS
273{
274 /** The device is uninitialized. */
275 COREAUDIOSTATUS_UNINIT = 0,
276 /** The device is currently initializing. */
277 COREAUDIOSTATUS_IN_INIT,
278 /** The device is initialized. */
279 COREAUDIOSTATUS_INIT,
280 /** The device is currently uninitializing. */
281 COREAUDIOSTATUS_IN_UNINIT,
282#ifndef VBOX_WITH_AUDIO_CALLBACKS
283 /** The device has to be reinitialized.
284 * Note: Only needed if VBOX_WITH_AUDIO_CALLBACKS is not defined, as otherwise
285 * the Audio Connector will take care of this as soon as this backend
286 * tells it to do so via the provided audio callback. */
287 COREAUDIOSTATUS_REINIT,
288#endif
289 /** The usual 32-bit hack. */
290 COREAUDIOSTATUS_32BIT_HACK = 0x7fffffff
291} COREAUDIOSTATUS, *PCOREAUDIOSTATUS;
292
293#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
294 /* Error code which indicates "End of data" */
295 static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */
296#endif
297
298/* Prototypes needed for COREAUDIOSTREAMCBCTX. */
299struct COREAUDIOSTREAM;
300typedef struct COREAUDIOSTREAM *PCOREAUDIOSTREAM;
301
302/**
303 * Structure for keeping a conversion callback context.
304 * This is needed when using an audio converter during input/output processing.
305 */
306typedef struct COREAUDIOCONVCBCTX
307{
308 /** Pointer to the stream this context is bound to. */
309 PCOREAUDIOSTREAM pStream;
310 /** Source stream description. */
311 AudioStreamBasicDescription asbdSrc;
312 /** Destination stream description. */
313 AudioStreamBasicDescription asbdDst;
314 /** Pointer to native buffer list used for rendering the source audio data into. */
315 AudioBufferList *pBufLstSrc;
316 /** Total packet conversion count. */
317 UInt32 uPacketCnt;
318 /** Current packet conversion index. */
319 UInt32 uPacketIdx;
320 /** Error count, for limiting the logging. */
321 UInt32 cErrors;
322} COREAUDIOCONVCBCTX, *PCOREAUDIOCONVCBCTX;
323
324/**
325 * Structure for keeping the input stream specifics.
326 */
327typedef struct COREAUDIOSTREAMIN
328{
329#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
330 /** The audio converter if necessary. NULL if no converter is being used. */
331 AudioConverterRef ConverterRef;
332 /** Callback context for the audio converter. */
333 COREAUDIOCONVCBCTX convCbCtx;
334#endif
335 /** The ratio between the device & the stream sample rate. */
336 Float64 sampleRatio;
337} COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN;
338
339/**
340 * Structure for keeping the output stream specifics.
341 */
342typedef struct COREAUDIOSTREAMOUT
343{
344 /** Nothing here yet. */
345} COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT;
346
347/**
348 * Structure for maintaining a Core Audio stream.
349 */
350typedef struct COREAUDIOSTREAM
351{
352 /** The stream's acquired configuration. */
353 PPDMAUDIOSTREAMCFG pCfg;
354 /** Stream-specific data, depending on the stream type. */
355 union
356 {
357 COREAUDIOSTREAMIN In;
358 COREAUDIOSTREAMOUT Out;
359 };
360 /** List node for the device's stream list. */
361 RTLISTNODE Node;
362 /** Pointer to driver instance this stream is bound to. */
363 PDRVHOSTCOREAUDIO pDrv;
364 /** The stream's thread handle for maintaining the audio queue. */
365 RTTHREAD hThread;
366 /** Flag indicating to start a stream's data processing. */
367 bool fRun;
368 /** Whether the stream is in a running (active) state or not.
369 * For playback streams this means that audio data can be (or is being) played,
370 * for capturing streams this means that audio data is being captured (if available). */
371 bool fIsRunning;
372 /** Thread shutdown indicator. */
373 bool fShutdown;
374 /** Critical section for serializing access between thread + callbacks. */
375 RTCRITSECT CritSect;
376 /** The actual audio queue being used. */
377 AudioQueueRef audioQueue;
378 /** The audio buffers which are used with the above audio queue. */
379 AudioQueueBufferRef audioBuffer[2];
380 /** The acquired (final) audio format for this stream. */
381 AudioStreamBasicDescription asbdStream;
382 /** The audio unit for this stream. */
383 COREAUDIOUNIT Unit;
384 /** Initialization status tracker. Used when some of the device parameters
385 * or the device itself is changed during the runtime. */
386 volatile uint32_t enmStatus;
387 /** An internal ring buffer for transferring data from/to the rendering callbacks. */
388 PRTCIRCBUF pCircBuf;
389} COREAUDIOSTREAM, *PCOREAUDIOSTREAM;
390
391static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev);
392#ifndef VBOX_WITH_AUDIO_CALLBACKS
393static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev);
394#endif
395static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream);
396
397static int coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd);
398
399static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev);
400static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev);
401static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, bool fIsInput, PDRVHOSTCOREAUDIO pDrv);
402
403static OSStatus coreAudioDevPropChgCb(AudioObjectID propertyID, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void *pvUser);
404
405static int coreAudioStreamInitQueue(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq);
406static void coreAudioInputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer, const AudioTimeStamp *pAudioTS, UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc);
407static void coreAudioOutputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer);
408
409#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
410/**
411 * Initializes a conversion callback context.
412 *
413 * @return IPRT status code.
414 * @param pConvCbCtx Conversion callback context to initialize.
415 * @param pStream Pointer to stream to use.
416 * @param pASBDSrc Input (source) stream description to use.
417 * @param pASBDDst Output (destination) stream description to use.
418 */
419static int coreAudioInitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx, PCOREAUDIOSTREAM pStream,
420 AudioStreamBasicDescription *pASBDSrc, AudioStreamBasicDescription *pASBDDst)
421{
422 AssertPtrReturn(pConvCbCtx, VERR_INVALID_POINTER);
423 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
424 AssertPtrReturn(pASBDSrc, VERR_INVALID_POINTER);
425 AssertPtrReturn(pASBDDst, VERR_INVALID_POINTER);
426
427#ifdef DEBUG
428 coreAudioPrintASBD("CbCtx: Src", pASBDSrc);
429 coreAudioPrintASBD("CbCtx: Dst", pASBDDst);
430#endif
431
432 pConvCbCtx->pStream = pStream;
433
434 memcpy(&pConvCbCtx->asbdSrc, pASBDSrc, sizeof(AudioStreamBasicDescription));
435 memcpy(&pConvCbCtx->asbdDst, pASBDDst, sizeof(AudioStreamBasicDescription));
436
437 pConvCbCtx->pBufLstSrc = NULL;
438 pConvCbCtx->cErrors = 0;
439
440 return VINF_SUCCESS;
441}
442
443
444/**
445 * Uninitializes a conversion callback context.
446 *
447 * @return IPRT status code.
448 * @param pConvCbCtx Conversion callback context to uninitialize.
449 */
450static void coreAudioUninitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx)
451{
452 AssertPtrReturnVoid(pConvCbCtx);
453
454 pConvCbCtx->pStream = NULL;
455
456 RT_ZERO(pConvCbCtx->asbdSrc);
457 RT_ZERO(pConvCbCtx->asbdDst);
458
459 pConvCbCtx->pBufLstSrc = NULL;
460 pConvCbCtx->cErrors = 0;
461}
462#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
463
464
465/**
466 * Does a (re-)enumeration of the host's playback + recording devices.
467 *
468 * @return IPRT status code.
469 * @param pThis Host audio driver instance.
470 * @param enmUsage Which devices to enumerate.
471 * @param pDevEnm Where to store the enumerated devices.
472 */
473static int coreAudioDevicesEnumerate(PDRVHOSTCOREAUDIO pThis, PDMAUDIODIR enmUsage, PPDMAUDIODEVICEENUM pDevEnm)
474{
475 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
476 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
477
478 int rc = VINF_SUCCESS;
479
480 do
481 {
482 AudioDeviceID defaultDeviceID = kAudioDeviceUnknown;
483
484 /* Fetch the default audio device currently in use. */
485 AudioObjectPropertyAddress propAdrDefaultDev = { enmUsage == PDMAUDIODIR_IN
486 ? kAudioHardwarePropertyDefaultInputDevice
487 : kAudioHardwarePropertyDefaultOutputDevice,
488 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
489 UInt32 uSize = sizeof(defaultDeviceID);
490 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdrDefaultDev, 0, NULL, &uSize, &defaultDeviceID);
491 if (err != noErr)
492 {
493 LogRel(("CoreAudio: Unable to determine default %s device (%RI32)\n",
494 enmUsage == PDMAUDIODIR_IN ? "capturing" : "playback", err));
495 return VERR_NOT_FOUND;
496 }
497
498 if (defaultDeviceID == kAudioDeviceUnknown)
499 {
500 LogFunc(("No default %s device found\n", enmUsage == PDMAUDIODIR_IN ? "capturing" : "playback"));
501 /* Keep going. */
502 }
503
504 AudioObjectPropertyAddress propAdrDevList = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
505 kAudioObjectPropertyElementMaster };
506
507 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize);
508 if (err != kAudioHardwareNoError)
509 break;
510
511 AudioDeviceID *pDevIDs = (AudioDeviceID *)alloca(uSize);
512 if (pDevIDs == NULL)
513 break;
514
515 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize, pDevIDs);
516 if (err != kAudioHardwareNoError)
517 break;
518
519 rc = DrvAudioHlpDeviceEnumInit(pDevEnm);
520 if (RT_FAILURE(rc))
521 break;
522
523 UInt16 cDevices = uSize / sizeof(AudioDeviceID);
524
525 PPDMAUDIODEVICE pDev = NULL;
526 for (UInt16 i = 0; i < cDevices; i++)
527 {
528 if (pDev) /* Some (skipped) device to clean up first? */
529 DrvAudioHlpDeviceFree(pDev);
530
531 pDev = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA));
532 if (!pDev)
533 {
534 rc = VERR_NO_MEMORY;
535 break;
536 }
537
538 /* Set usage. */
539 pDev->enmUsage = enmUsage;
540
541 /* Init backend-specific device data. */
542 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pDev->pvData;
543 AssertPtr(pDevData);
544 coreAudioDeviceDataInit(pDevData, pDevIDs[i], enmUsage == PDMAUDIODIR_IN, pThis);
545
546 /* Check if the device is valid. */
547 AudioDeviceID curDevID = pDevData->deviceID;
548
549 /* Is the device the default device? */
550 if (curDevID == defaultDeviceID)
551 pDev->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT;
552
553 AudioObjectPropertyAddress propAddrCfg = { kAudioDevicePropertyStreamConfiguration,
554 enmUsage == PDMAUDIODIR_IN
555 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
556 kAudioObjectPropertyElementMaster };
557
558 err = AudioObjectGetPropertyDataSize(curDevID, &propAddrCfg, 0, NULL, &uSize);
559 if (err != noErr)
560 continue;
561
562 AudioBufferList *pBufList = (AudioBufferList *)RTMemAlloc(uSize);
563 if (!pBufList)
564 continue;
565
566 err = AudioObjectGetPropertyData(curDevID, &propAddrCfg, 0, NULL, &uSize, pBufList);
567 if (err == noErr)
568 {
569 for (UInt32 a = 0; a < pBufList->mNumberBuffers; a++)
570 {
571 if (enmUsage == PDMAUDIODIR_IN)
572 pDev->cMaxInputChannels += pBufList->mBuffers[a].mNumberChannels;
573 else if (enmUsage == PDMAUDIODIR_OUT)
574 pDev->cMaxOutputChannels += pBufList->mBuffers[a].mNumberChannels;
575 }
576 }
577
578 if (pBufList)
579 {
580 RTMemFree(pBufList);
581 pBufList = NULL;
582 }
583
584 /* Check if the device is valid, e.g. has any input/output channels according to its usage. */
585 if ( enmUsage == PDMAUDIODIR_IN
586 && !pDev->cMaxInputChannels)
587 continue;
588 if ( enmUsage == PDMAUDIODIR_OUT
589 && !pDev->cMaxOutputChannels)
590 continue;
591
592 /* Resolve the device's name. */
593 AudioObjectPropertyAddress propAddrName = { kAudioObjectPropertyName,
594 enmUsage == PDMAUDIODIR_IN
595 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
596 kAudioObjectPropertyElementMaster };
597 uSize = sizeof(CFStringRef);
598 CFStringRef pcfstrName = NULL;
599
600 err = AudioObjectGetPropertyData(curDevID, &propAddrName, 0, NULL, &uSize, &pcfstrName);
601 if (err != kAudioHardwareNoError)
602 continue;
603
604 CFIndex cbName = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pcfstrName), kCFStringEncodingUTF8) + 1;
605 if (cbName)
606 {
607 char *pszName = (char *)RTStrAlloc(cbName);
608 if ( pszName
609 && CFStringGetCString(pcfstrName, pszName, cbName, kCFStringEncodingUTF8))
610 RTStrCopy(pDev->szName, sizeof(pDev->szName), pszName);
611
612 LogFunc(("Device '%s': %RU32\n", pszName, curDevID));
613
614 if (pszName)
615 {
616 RTStrFree(pszName);
617 pszName = NULL;
618 }
619 }
620
621 CFRelease(pcfstrName);
622
623 /* Check if the device is alive for the intended usage. */
624 AudioObjectPropertyAddress propAddrAlive = { kAudioDevicePropertyDeviceIsAlive,
625 enmUsage == PDMAUDIODIR_IN
626 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
627 kAudioObjectPropertyElementMaster };
628
629 UInt32 uAlive = 0;
630 uSize = sizeof(uAlive);
631
632 err = AudioObjectGetPropertyData(curDevID, &propAddrAlive, 0, NULL, &uSize, &uAlive);
633 if ( (err == noErr)
634 && !uAlive)
635 {
636 pDev->fFlags |= PDMAUDIODEV_FLAGS_DEAD;
637 }
638
639 /* Check if the device is being hogged by someone else. */
640 AudioObjectPropertyAddress propAddrHogged = { kAudioDevicePropertyHogMode,
641 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
642
643 pid_t pid = 0;
644 uSize = sizeof(pid);
645
646 err = AudioObjectGetPropertyData(curDevID, &propAddrHogged, 0, NULL, &uSize, &pid);
647 if ( (err == noErr)
648 && (pid != -1))
649 {
650 pDev->fFlags |= PDMAUDIODEV_FLAGS_LOCKED;
651 }
652
653 /* Add the device to the enumeration. */
654 rc = DrvAudioHlpDeviceEnumAdd(pDevEnm, pDev);
655 if (RT_FAILURE(rc))
656 break;
657
658 /* NULL device pointer because it's now part of the device enumeration. */
659 pDev = NULL;
660 }
661
662 if (RT_FAILURE(rc))
663 {
664 DrvAudioHlpDeviceFree(pDev);
665 pDev = NULL;
666 }
667
668 } while (0);
669
670 if (RT_SUCCESS(rc))
671 {
672#ifdef DEBUG
673 LogFunc(("Devices for pDevEnm=%p, enmUsage=%RU32:\n", pDevEnm, enmUsage));
674 DrvAudioHlpDeviceEnumPrint("Core Audio", pDevEnm);
675#endif
676 }
677 else
678 DrvAudioHlpDeviceEnumFree(pDevEnm);
679
680 LogFlowFuncLeaveRC(rc);
681 return rc;
682}
683
684
685/**
686 * Checks if an audio device with a specific device ID is in the given device
687 * enumeration or not.
688 *
689 * @retval true if the node is the last element in the list.
690 * @retval false otherwise.
691 *
692 * @param pEnmSrc Device enumeration to search device ID in.
693 * @param deviceID Device ID to search.
694 */
695bool coreAudioDevicesHasDevice(PPDMAUDIODEVICEENUM pEnmSrc, AudioDeviceID deviceID)
696{
697 PPDMAUDIODEVICE pDevSrc;
698 RTListForEach(&pEnmSrc->lstDevices, pDevSrc, PDMAUDIODEVICE, Node)
699 {
700 PCOREAUDIODEVICEDATA pDevSrcData = (PCOREAUDIODEVICEDATA)pDevSrc->pvData;
701 AssertPtr(pDevSrcData);
702
703 if (pDevSrcData->deviceID == deviceID)
704 return true;
705 }
706
707 return false;
708}
709
710
711/**
712 * Enumerates all host devices and builds a final device enumeration list, consisting
713 * of (duplex) input and output devices.
714 *
715 * @return IPRT status code.
716 * @param pThis Host audio driver instance.
717 * @param pEnmDst Where to store the device enumeration list.
718 */
719int coreAudioDevicesEnumerateAll(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICEENUM pEnmDst)
720{
721 PDMAUDIODEVICEENUM devEnmIn;
722 int rc = coreAudioDevicesEnumerate(pThis, PDMAUDIODIR_IN, &devEnmIn);
723 if (RT_SUCCESS(rc))
724 {
725 PDMAUDIODEVICEENUM devEnmOut;
726 rc = coreAudioDevicesEnumerate(pThis, PDMAUDIODIR_OUT, &devEnmOut);
727 if (RT_SUCCESS(rc))
728 {
729 /*
730 * Build up the final device enumeration, based on the input and output device lists
731 * just enumerated.
732 *
733 * Also make sure to handle duplex devices, that is, devices which act as input and output
734 * at the same time.
735 */
736
737 rc = DrvAudioHlpDeviceEnumInit(pEnmDst);
738 if (RT_SUCCESS(rc))
739 {
740 PPDMAUDIODEVICE pDevSrcIn;
741 RTListForEach(&devEnmIn.lstDevices, pDevSrcIn, PDMAUDIODEVICE, Node)
742 {
743 PCOREAUDIODEVICEDATA pDevSrcInData = (PCOREAUDIODEVICEDATA)pDevSrcIn->pvData;
744 AssertPtr(pDevSrcInData);
745
746 PPDMAUDIODEVICE pDevDst = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA));
747 if (!pDevDst)
748 {
749 rc = VERR_NO_MEMORY;
750 break;
751 }
752
753 PCOREAUDIODEVICEDATA pDevDstData = (PCOREAUDIODEVICEDATA)pDevDst->pvData;
754 AssertPtr(pDevDstData);
755 coreAudioDeviceDataInit(pDevDstData, pDevSrcInData->deviceID, true /* fIsInput */, pThis);
756
757 RTStrCopy(pDevDst->szName, sizeof(pDevDst->szName), pDevSrcIn->szName);
758
759 pDevDst->enmUsage = PDMAUDIODIR_IN; /* Input device by default (simplex). */
760 pDevDst->cMaxInputChannels = pDevSrcIn->cMaxInputChannels;
761
762 /* Handle flags. */
763 if (pDevSrcIn->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
764 pDevDst->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT;
765 /** @todo Handle hot plugging? */
766
767 /*
768 * Now search through the list of all found output devices and check if we found
769 * an output device with the same device ID as the currently handled input device.
770 *
771 * If found, this means we have to treat that device as a duplex device then.
772 */
773 PPDMAUDIODEVICE pDevSrcOut;
774 RTListForEach(&devEnmOut.lstDevices, pDevSrcOut, PDMAUDIODEVICE, Node)
775 {
776 PCOREAUDIODEVICEDATA pDevSrcOutData = (PCOREAUDIODEVICEDATA)pDevSrcOut->pvData;
777 AssertPtr(pDevSrcOutData);
778
779 if (pDevSrcInData->deviceID == pDevSrcOutData->deviceID)
780 {
781 pDevDst->enmUsage = PDMAUDIODIR_ANY;
782 pDevDst->cMaxOutputChannels = pDevSrcOut->cMaxOutputChannels;
783 break;
784 }
785 }
786
787 if (RT_SUCCESS(rc))
788 {
789 rc = DrvAudioHlpDeviceEnumAdd(pEnmDst, pDevDst);
790 }
791 else
792 {
793 DrvAudioHlpDeviceFree(pDevDst);
794 pDevDst = NULL;
795 }
796 }
797
798 if (RT_SUCCESS(rc))
799 {
800 /*
801 * As a last step, add all remaining output devices which have not been handled in the loop above,
802 * that is, all output devices which operate in simplex mode.
803 */
804 PPDMAUDIODEVICE pDevSrcOut;
805 RTListForEach(&devEnmOut.lstDevices, pDevSrcOut, PDMAUDIODEVICE, Node)
806 {
807 PCOREAUDIODEVICEDATA pDevSrcOutData = (PCOREAUDIODEVICEDATA)pDevSrcOut->pvData;
808 AssertPtr(pDevSrcOutData);
809
810 if (coreAudioDevicesHasDevice(pEnmDst, pDevSrcOutData->deviceID))
811 continue; /* Already in our list, skip. */
812
813 PPDMAUDIODEVICE pDevDst = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA));
814 if (!pDevDst)
815 {
816 rc = VERR_NO_MEMORY;
817 break;
818 }
819
820 PCOREAUDIODEVICEDATA pDevDstData = (PCOREAUDIODEVICEDATA)pDevDst->pvData;
821 AssertPtr(pDevDstData);
822 coreAudioDeviceDataInit(pDevDstData, pDevSrcOutData->deviceID, false /* fIsInput */, pThis);
823
824 RTStrCopy(pDevDst->szName, sizeof(pDevDst->szName), pDevSrcOut->szName);
825
826 pDevDst->enmUsage = PDMAUDIODIR_OUT;
827 pDevDst->cMaxOutputChannels = pDevSrcOut->cMaxOutputChannels;
828
829 pDevDstData->deviceID = pDevSrcOutData->deviceID;
830
831 /* Handle flags. */
832 if (pDevSrcOut->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
833 pDevDst->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT;
834 /** @todo Handle hot plugging? */
835
836 rc = DrvAudioHlpDeviceEnumAdd(pEnmDst, pDevDst);
837 if (RT_FAILURE(rc))
838 {
839 DrvAudioHlpDeviceFree(pDevDst);
840 break;
841 }
842 }
843 }
844
845 if (RT_FAILURE(rc))
846 DrvAudioHlpDeviceEnumFree(pEnmDst);
847 }
848
849 DrvAudioHlpDeviceEnumFree(&devEnmOut);
850 }
851
852 DrvAudioHlpDeviceEnumFree(&devEnmIn);
853 }
854
855#ifdef DEBUG
856 if (RT_SUCCESS(rc))
857 DrvAudioHlpDeviceEnumPrint("Core Audio (Final)", pEnmDst);
858#endif
859
860 LogFlowFuncLeaveRC(rc);
861 return rc;
862}
863
864
865/**
866 * Initializes a Core Audio-specific device data structure.
867 *
868 * @returns IPRT status code.
869 * @param pDevData Device data structure to initialize.
870 * @param deviceID Core Audio device ID to assign this structure to.
871 * @param fIsInput Whether this is an input device or not.
872 * @param pDrv Driver instance to use.
873 */
874static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, bool fIsInput, PDRVHOSTCOREAUDIO pDrv)
875{
876 AssertPtrReturnVoid(pDevData);
877 AssertPtrReturnVoid(pDrv);
878
879 pDevData->deviceID = deviceID;
880 pDevData->pDrv = pDrv;
881
882 /* Get the device UUID. */
883 AudioObjectPropertyAddress propAdrDevUUID = { kAudioDevicePropertyDeviceUID,
884 fIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
885 kAudioObjectPropertyElementMaster };
886 UInt32 uSize = sizeof(pDevData->UUID);
887 OSStatus err = AudioObjectGetPropertyData(pDevData->deviceID, &propAdrDevUUID, 0, NULL, &uSize, &pDevData->UUID);
888 if (err != noErr)
889 LogRel(("CoreAudio: Failed to retrieve device UUID for device %RU32 (%RI32)\n", deviceID, err));
890
891 RTListInit(&pDevData->lstStreams);
892}
893
894
895/**
896 * Propagates an audio device status to all its connected Core Audio streams.
897 *
898 * @return IPRT status code.
899 * @param pDev Audio device to propagate status for.
900 * @param enmSts Status to propagate.
901 */
902static int coreAudioDevicePropagateStatus(PPDMAUDIODEVICE pDev, COREAUDIOSTATUS enmSts)
903{
904 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
905
906 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pDev->pvData;
907 AssertPtrReturn(pDevData, VERR_INVALID_POINTER);
908
909 /* Sanity. */
910 AssertPtr(pDevData->pDrv);
911
912 LogFlowFunc(("pDev=%p, pDevData=%p, enmSts=%RU32\n", pDev, pDevData, enmSts));
913
914 PCOREAUDIOSTREAM pCAStream;
915 RTListForEach(&pDevData->lstStreams, pCAStream, COREAUDIOSTREAM, Node)
916 {
917 LogFlowFunc(("pCAStream=%p\n", pCAStream));
918
919 /* We move the reinitialization to the next output event.
920 * This make sure this thread isn't blocked and the
921 * reinitialization is done when necessary only. */
922 ASMAtomicXchgU32(&pCAStream->enmStatus, enmSts);
923 }
924
925 return VINF_SUCCESS;
926}
927
928
929static DECLCALLBACK(OSStatus) coreAudioDeviceStateChangedCb(AudioObjectID propertyID,
930 UInt32 nAddresses,
931 const AudioObjectPropertyAddress properties[],
932 void *pvUser)
933{
934 RT_NOREF(propertyID, nAddresses, properties);
935
936 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser));
937
938 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)pvUser;
939 AssertPtr(pDev);
940
941 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
942 AssertPtrReturn(pData, VERR_INVALID_POINTER);
943
944 PDRVHOSTCOREAUDIO pThis = pData->pDrv;
945 AssertPtr(pThis);
946
947 int rc2 = RTCritSectEnter(&pThis->CritSect);
948 AssertRC(rc2);
949
950 UInt32 uAlive = 1;
951 UInt32 uSize = sizeof(UInt32);
952
953 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
954 kAudioObjectPropertyElementMaster };
955
956 AudioDeviceID deviceID = pData->deviceID;
957
958 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uAlive);
959
960 bool fIsDead = false;
961
962 if (err == kAudioHardwareBadDeviceError)
963 fIsDead = true; /* Unplugged. */
964 else if ((err == kAudioHardwareNoError) && (!RT_BOOL(uAlive)))
965 fIsDead = true; /* Something else happened. */
966
967 if (fIsDead)
968 {
969 LogRel2(("CoreAudio: Device '%s' stopped functioning\n", pDev->szName));
970
971 /* Mark device as dead. */
972 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_UNINIT);
973 AssertRC(rc2);
974 }
975
976 rc2 = RTCritSectLeave(&pThis->CritSect);
977 AssertRC(rc2);
978
979 return noErr;
980}
981
982/* Callback for getting notified when the default recording/playback device has been changed. */
983static DECLCALLBACK(OSStatus) coreAudioDefaultDeviceChangedCb(AudioObjectID propertyID,
984 UInt32 nAddresses,
985 const AudioObjectPropertyAddress properties[],
986 void *pvUser)
987{
988 RT_NOREF(propertyID, nAddresses);
989
990 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser));
991
992 PDRVHOSTCOREAUDIO pThis = (PDRVHOSTCOREAUDIO)pvUser;
993 AssertPtr(pThis);
994
995 int rc2 = RTCritSectEnter(&pThis->CritSect);
996 AssertRC(rc2);
997
998 for (UInt32 idxAddress = 0; idxAddress < nAddresses; idxAddress++)
999 {
1000 PPDMAUDIODEVICE pDev = NULL;
1001
1002 /*
1003 * Check if the default input / output device has been changed.
1004 */
1005 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress];
1006 switch (pProperty->mSelector)
1007 {
1008 case kAudioHardwarePropertyDefaultInputDevice:
1009 LogFlowFunc(("kAudioHardwarePropertyDefaultInputDevice\n"));
1010 pDev = pThis->pDefaultDevIn;
1011 break;
1012
1013 case kAudioHardwarePropertyDefaultOutputDevice:
1014 LogFlowFunc(("kAudioHardwarePropertyDefaultOutputDevice\n"));
1015 pDev = pThis->pDefaultDevOut;
1016 break;
1017
1018 default:
1019 /* Skip others. */
1020 break;
1021 }
1022
1023 LogFlowFunc(("pDev=%p\n", pDev));
1024
1025#ifndef VBOX_WITH_AUDIO_CALLBACKS
1026 if (pDev)
1027 {
1028 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1029 AssertPtr(pData);
1030
1031 /* This listener is called on every change of the hardware
1032 * device. So check if the default device has really changed. */
1033 UInt32 uSize = sizeof(AudioDeviceID);
1034 UInt32 uResp = 0;
1035
1036 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp);
1037 if (err == noErr)
1038 {
1039 if (pData->deviceID != uResp) /* Has the device ID changed? */
1040 {
1041 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT);
1042 AssertRC(rc2);
1043 }
1044 }
1045 }
1046#endif /* VBOX_WITH_AUDIO_CALLBACKS */
1047 }
1048
1049#ifdef VBOX_WITH_AUDIO_CALLBACKS
1050 PFNPDMHOSTAUDIOCALLBACK pfnCallback = pThis->pfnCallback;
1051#endif
1052
1053 /* Make sure to leave the critical section before calling the callback. */
1054 rc2 = RTCritSectLeave(&pThis->CritSect);
1055 AssertRC(rc2);
1056
1057#ifdef VBOX_WITH_AUDIO_CALLBACKS
1058 if (pfnCallback)
1059 /* Ignore rc */ pfnCallback(pThis->pDrvIns, PDMAUDIOBACKENDCBTYPE_DEVICES_CHANGED, NULL, 0);
1060#endif
1061
1062 return noErr;
1063}
1064
1065#ifndef VBOX_WITH_AUDIO_CALLBACKS
1066/**
1067 * Re-initializes a Core Audio stream with a specific audio device and stream configuration.
1068 *
1069 * @return IPRT status code.
1070 * @param pThis Driver instance.
1071 * @param pCAStream Audio stream to re-initialize.
1072 * @param pDev Audio device to use for re-initialization.
1073 * @param pCfg Stream configuration to use for re-initialization.
1074 */
1075static int coreAudioStreamReinitEx(PDRVHOSTCOREAUDIO pThis,
1076 PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev, PPDMAUDIOSTREAMCFG pCfg)
1077{
1078 LogFunc(("pCAStream=%p\n", pCAStream));
1079
1080 int rc = coreAudioStreamUninit(pCAStream);
1081 if (RT_SUCCESS(rc))
1082 {
1083 rc = coreAudioStreamInit(pCAStream, pThis, pDev);
1084 if (RT_SUCCESS(rc))
1085 {
1086 rc = coreAudioStreamInitQueue(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */);
1087 if (RT_SUCCESS(rc))
1088 rc = coreAudioStreamControl(pCAStream->pDrv, pCAStream, PDMAUDIOSTREAMCMD_ENABLE);
1089
1090 if (RT_FAILURE(rc))
1091 {
1092 int rc2 = coreAudioStreamUninit(pCAStream);
1093 AssertRC(rc2);
1094 }
1095 }
1096 }
1097
1098 if (RT_FAILURE(rc))
1099 LogRel(("CoreAudio: Unable to re-init stream: %Rrc\n", rc));
1100
1101 return rc;
1102}
1103
1104/**
1105 * Re-initializes a Core Audio stream with a specific audio device.
1106 *
1107 * @return IPRT status code.
1108 * @param pThis Driver instance.
1109 * @param pCAStream Audio stream to re-initialize.
1110 * @param pDev Audio device to use for re-initialization.
1111 */
1112static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev)
1113{
1114 int rc = coreAudioStreamUninit(pCAStream);
1115 if (RT_SUCCESS(rc))
1116 {
1117 /* Use the acquired stream configuration from the former initialization to
1118 * re-initialize the stream. */
1119 PDMAUDIOSTREAMCFG CfgAcq;
1120 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, &CfgAcq);
1121 if (RT_SUCCESS(rc))
1122 rc = coreAudioStreamReinitEx(pThis, pCAStream, pDev, &CfgAcq);
1123 }
1124
1125 return rc;
1126}
1127#endif /* !VBOX_WITH_AUDIO_CALLBACKS */
1128
1129#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
1130/* Callback to convert audio input data from one format to another. */
1131static DECLCALLBACK(OSStatus) coreAudioConverterCb(AudioConverterRef inAudioConverter,
1132 UInt32 *ioNumberDataPackets,
1133 AudioBufferList *ioData,
1134 AudioStreamPacketDescription **ppASPD,
1135 void *pvUser)
1136{
1137 RT_NOREF(inAudioConverter);
1138
1139 AssertPtrReturn(ioNumberDataPackets, caConverterEOFDErr);
1140 AssertPtrReturn(ioData, caConverterEOFDErr);
1141
1142 PCOREAUDIOCONVCBCTX pConvCbCtx = (PCOREAUDIOCONVCBCTX)pvUser;
1143 AssertPtr(pConvCbCtx);
1144
1145 /* Initialize values. */
1146 ioData->mBuffers[0].mNumberChannels = 0;
1147 ioData->mBuffers[0].mDataByteSize = 0;
1148 ioData->mBuffers[0].mData = NULL;
1149
1150 if (ppASPD)
1151 {
1152 Log3Func(("Handling packet description not implemented\n"));
1153 }
1154 else
1155 {
1156 /** @todo Check converter ID? */
1157
1158 /** @todo Handled non-interleaved data by going through the full buffer list,
1159 * not only through the first buffer like we do now. */
1160 Log3Func(("ioNumberDataPackets=%RU32\n", *ioNumberDataPackets));
1161
1162 UInt32 cNumberDataPackets = *ioNumberDataPackets;
1163 Assert(pConvCbCtx->uPacketIdx + cNumberDataPackets <= pConvCbCtx->uPacketCnt);
1164
1165 if (cNumberDataPackets)
1166 {
1167 AssertPtr(pConvCbCtx->pBufLstSrc);
1168 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers == 1); /* Only one buffer for the source supported atm. */
1169
1170 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc;
1171 AudioBuffer *pSrcBuf = &pConvCbCtx->pBufLstSrc->mBuffers[0];
1172
1173 size_t cbOff = pConvCbCtx->uPacketIdx * pSrcASBD->mBytesPerPacket;
1174
1175 cNumberDataPackets = RT_MIN((pSrcBuf->mDataByteSize - cbOff) / pSrcASBD->mBytesPerPacket,
1176 cNumberDataPackets);
1177
1178 void *pvAvail = (uint8_t *)pSrcBuf->mData + cbOff;
1179 size_t cbAvail = RT_MIN(pSrcBuf->mDataByteSize - cbOff, cNumberDataPackets * pSrcASBD->mBytesPerPacket);
1180
1181 Log3Func(("cNumberDataPackets=%RU32, cbOff=%zu, cbAvail=%zu\n", cNumberDataPackets, cbOff, cbAvail));
1182
1183 /* Set input data for the converter to use.
1184 * Note: For VBR (Variable Bit Rates) or interleaved data handling we need multiple buffers here. */
1185 ioData->mNumberBuffers = 1;
1186
1187 ioData->mBuffers[0].mNumberChannels = pSrcBuf->mNumberChannels;
1188 ioData->mBuffers[0].mDataByteSize = cbAvail;
1189 ioData->mBuffers[0].mData = pvAvail;
1190
1191#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1192 RTFILE fh;
1193 int rc = RTFileOpen(&fh,VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caConverterCbInput.pcm",
1194 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1195 if (RT_SUCCESS(rc))
1196 {
1197 RTFileWrite(fh, pvAvail, cbAvail, NULL);
1198 RTFileClose(fh);
1199 }
1200 else
1201 AssertFailed();
1202#endif
1203 pConvCbCtx->uPacketIdx += cNumberDataPackets;
1204 Assert(pConvCbCtx->uPacketIdx <= pConvCbCtx->uPacketCnt);
1205
1206 *ioNumberDataPackets = cNumberDataPackets;
1207 }
1208 }
1209
1210 Log3Func(("%RU32 / %RU32 -> ioNumberDataPackets=%RU32\n",
1211 pConvCbCtx->uPacketIdx, pConvCbCtx->uPacketCnt, *ioNumberDataPackets));
1212
1213 return noErr;
1214}
1215#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
1216
1217
1218/**
1219 * Initializes a Core Audio stream.
1220 *
1221 * @return IPRT status code.
1222 * @param pThis Driver instance.
1223 * @param pCAStream Stream to initialize.
1224 * @param pDev Audio device to use for this stream.
1225 */
1226static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev)
1227{
1228 AssertPtrReturn(pCAStream, VERR_INVALID_POINTER);
1229 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1230 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
1231
1232 Assert(pCAStream->Unit.pDevice == NULL); /* Make sure no device is assigned yet. */
1233 AssertPtr(pDev->pvData);
1234 Assert(pDev->cbData == sizeof(COREAUDIODEVICEDATA));
1235
1236#ifdef DEBUG
1237 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1238 LogFunc(("pCAStream=%p, pDev=%p ('%s', ID=%RU32)\n", pCAStream, pDev, pDev->szName, pData->deviceID));
1239#endif
1240
1241 pCAStream->Unit.pDevice = pDev;
1242 pCAStream->pDrv = pThis;
1243
1244 return VINF_SUCCESS;
1245}
1246
1247# define CA_BREAK_STMT(stmt) \
1248 stmt; \
1249 break;
1250
1251/**
1252 * Thread for a Core Audio stream's audio queue handling.
1253 *
1254 * This thread is required per audio queue to pump data to/from the Core Audio
1255 * stream and handling its callbacks.
1256 *
1257 * @returns IPRT status code.
1258 * @param hThreadSelf Thread handle.
1259 * @param pvUser User argument.
1260 */
1261static DECLCALLBACK(int) coreAudioQueueThread(RTTHREAD hThreadSelf, void *pvUser)
1262{
1263 RT_NOREF(hThreadSelf);
1264
1265 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser;
1266 AssertPtr(pCAStream);
1267 AssertPtr(pCAStream->pCfg);
1268
1269 const bool fIn = pCAStream->pCfg->enmDir == PDMAUDIODIR_IN;
1270
1271 LogFunc(("Thread started for pCAStream=%p, fIn=%RTbool\n", pCAStream, fIn));
1272
1273 /*
1274 * Create audio queue.
1275 */
1276 OSStatus err;
1277 if (fIn)
1278 err = AudioQueueNewInput(&pCAStream->asbdStream, coreAudioInputQueueCb, pCAStream /* pvData */,
1279 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue);
1280 else
1281 err = AudioQueueNewOutput(&pCAStream->asbdStream, coreAudioOutputQueueCb, pCAStream /* pvData */,
1282 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue);
1283
1284 if (err != noErr)
1285 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1286
1287 /*
1288 * Assign device to queue.
1289 */
1290 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pCAStream->Unit.pDevice->pvData;
1291 AssertPtr(pData);
1292
1293 UInt32 uSize = sizeof(pData->UUID);
1294 err = AudioQueueSetProperty(pCAStream->audioQueue, kAudioQueueProperty_CurrentDevice, &pData->UUID, uSize);
1295 if (err != noErr)
1296 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1297
1298 const size_t cbBufSize = DrvAudioHlpFramesToBytes(pCAStream->pCfg->Backend.cFramesPeriod, &pCAStream->pCfg->Props);
1299
1300 /*
1301 * Allocate audio buffers.
1302 */
1303 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++)
1304 {
1305 err = AudioQueueAllocateBuffer(pCAStream->audioQueue, cbBufSize, &pCAStream->audioBuffer[i]);
1306 if (err != noErr)
1307 break;
1308 }
1309
1310 if (err != noErr)
1311 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1312
1313 /* Signal the main thread before entering the main loop. */
1314 RTThreadUserSignal(RTThreadSelf());
1315
1316 /*
1317 * Enter the main loop.
1318 */
1319 while (!ASMAtomicReadBool(&pCAStream->fShutdown))
1320 {
1321 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
1322 }
1323
1324 /*
1325 * Cleanup.
1326 */
1327 if (fIn)
1328 {
1329 AudioQueueStop(pCAStream->audioQueue, 1);
1330 }
1331 else
1332 {
1333 AudioQueueStop(pCAStream->audioQueue, 0);
1334 }
1335
1336 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++)
1337 {
1338 if (pCAStream->audioBuffer[i])
1339 AudioQueueFreeBuffer(pCAStream->audioQueue, pCAStream->audioBuffer[i]);
1340 }
1341
1342 AudioQueueDispose(pCAStream->audioQueue, 1);
1343
1344 LogFunc(("Thread ended for pCAStream=%p, fIn=%RTbool\n", pCAStream, fIn));
1345 return VINF_SUCCESS;
1346}
1347
1348/**
1349 * Processes input data of an audio queue buffer and stores it into a Core Audio stream.
1350 *
1351 * @returns IPRT status code.
1352 * @param pCAStream Core Audio stream to store input data into.
1353 * @param audioBuffer Audio buffer to process input data from.
1354 */
1355int coreAudioInputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer)
1356{
1357 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf;
1358 AssertPtr(pCircBuf);
1359
1360 UInt8 *pvSrc = (UInt8 *)audioBuffer->mAudioData;
1361 UInt8 *pvDst = NULL;
1362
1363 size_t cbWritten = 0;
1364
1365 size_t cbToWrite = audioBuffer->mAudioDataByteSize;
1366 size_t cbLeft = RT_MIN(cbToWrite, RTCircBufFree(pCircBuf));
1367
1368 while (cbLeft)
1369 {
1370 /* Try to acquire the necessary block from the ring buffer. */
1371 RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, (void **)&pvDst, &cbToWrite);
1372
1373 if (!cbToWrite)
1374 break;
1375
1376 /* Copy the data from our ring buffer to the core audio buffer. */
1377 memcpy((UInt8 *)pvDst, pvSrc + cbWritten, cbToWrite);
1378
1379 /* Release the read buffer, so it could be used for new data. */
1380 RTCircBufReleaseWriteBlock(pCircBuf, cbToWrite);
1381
1382 cbWritten += cbToWrite;
1383
1384 Assert(cbLeft >= cbToWrite);
1385 cbLeft -= cbToWrite;
1386 }
1387
1388 Log3Func(("pCAStream=%p, cbBuffer=%RU32/%zu, cbWritten=%zu\n",
1389 pCAStream, audioBuffer->mAudioDataByteSize, audioBuffer->mAudioDataBytesCapacity, cbWritten));
1390
1391 return VINF_SUCCESS;
1392}
1393
1394/**
1395 * Input audio queue callback. Called whenever input data from the audio queue becomes available.
1396 *
1397 * @param pvUser User argument.
1398 * @param audioQueue Audio queue to process input data from.
1399 * @param audioBuffer Audio buffer to process input data from. Must be part of audio queue.
1400 * @param pAudioTS Audio timestamp.
1401 * @param cPacketDesc Number of packet descriptors.
1402 * @param paPacketDesc Array of packet descriptors.
1403 */
1404static DECLCALLBACK(void) coreAudioInputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer,
1405 const AudioTimeStamp *pAudioTS,
1406 UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc)
1407{
1408 RT_NOREF(audioQueue, pAudioTS, cPacketDesc, paPacketDesc);
1409
1410 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser;
1411 AssertPtr(pCAStream);
1412
1413 int rc = RTCritSectEnter(&pCAStream->CritSect);
1414 AssertRC(rc);
1415
1416 rc = coreAudioInputQueueProcBuffer(pCAStream, audioBuffer);
1417 if (RT_SUCCESS(rc))
1418 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL);
1419
1420 rc = RTCritSectLeave(&pCAStream->CritSect);
1421 AssertRC(rc);
1422}
1423
1424/**
1425 * Processes output data of a Core Audio stream into an audio queue buffer.
1426 *
1427 * @returns IPRT status code.
1428 * @param pCAStream Core Audio stream to process output data for.
1429 * @param audioBuffer Audio buffer to store data into.
1430 */
1431int coreAudioOutputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer)
1432{
1433 AssertPtr(pCAStream);
1434
1435 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf;
1436 AssertPtr(pCircBuf);
1437
1438 size_t cbRead = 0;
1439
1440 UInt8 *pvSrc = NULL;
1441 UInt8 *pvDst = (UInt8 *)audioBuffer->mAudioData;
1442
1443 size_t cbToRead = RT_MIN(RTCircBufUsed(pCircBuf), audioBuffer->mAudioDataBytesCapacity);
1444 size_t cbLeft = cbToRead;
1445
1446 while (cbLeft)
1447 {
1448 /* Try to acquire the necessary block from the ring buffer. */
1449 RTCircBufAcquireReadBlock(pCircBuf, cbLeft, (void **)&pvSrc, &cbToRead);
1450
1451 if (cbToRead)
1452 {
1453 /* Copy the data from our ring buffer to the core audio buffer. */
1454 memcpy((UInt8 *)pvDst + cbRead, pvSrc, cbToRead);
1455 }
1456
1457 /* Release the read buffer, so it could be used for new data. */
1458 RTCircBufReleaseReadBlock(pCircBuf, cbToRead);
1459
1460 if (!cbToRead)
1461 break;
1462
1463 /* Move offset. */
1464 cbRead += cbToRead;
1465 Assert(cbRead <= audioBuffer->mAudioDataBytesCapacity);
1466
1467 Assert(cbToRead <= cbLeft);
1468 cbLeft -= cbToRead;
1469 }
1470
1471 audioBuffer->mAudioDataByteSize = cbRead;
1472
1473 if (audioBuffer->mAudioDataByteSize < audioBuffer->mAudioDataBytesCapacity)
1474 {
1475 RT_BZERO((UInt8 *)audioBuffer->mAudioData + audioBuffer->mAudioDataByteSize,
1476 audioBuffer->mAudioDataBytesCapacity - audioBuffer->mAudioDataByteSize);
1477
1478 audioBuffer->mAudioDataByteSize = audioBuffer->mAudioDataBytesCapacity;
1479 }
1480
1481 Log3Func(("pCAStream=%p, cbCapacity=%RU32, cbRead=%zu\n",
1482 pCAStream, audioBuffer->mAudioDataBytesCapacity, cbRead));
1483
1484 return VINF_SUCCESS;
1485}
1486
1487/**
1488 * Output audio queue callback. Called whenever an audio queue is ready to process more output data.
1489 *
1490 * @param pvUser User argument.
1491 * @param audioQueue Audio queue to process output data for.
1492 * @param audioBuffer Audio buffer to store output data in. Must be part of audio queue.
1493 */
1494static DECLCALLBACK(void) coreAudioOutputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer)
1495{
1496 RT_NOREF(audioQueue);
1497
1498 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser;
1499 AssertPtr(pCAStream);
1500
1501 int rc = RTCritSectEnter(&pCAStream->CritSect);
1502 AssertRC(rc);
1503
1504 rc = coreAudioOutputQueueProcBuffer(pCAStream, audioBuffer);
1505 if (RT_SUCCESS(rc))
1506 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL);
1507
1508 rc = RTCritSectLeave(&pCAStream->CritSect);
1509 AssertRC(rc);
1510}
1511
1512/**
1513 * Invalidates a Core Audio stream's audio queue.
1514 *
1515 * @returns IPRT status code.
1516 * @param pCAStream Core Audio stream to invalidate its queue for.
1517 */
1518static int coreAudioStreamInvalidateQueue(PCOREAUDIOSTREAM pCAStream)
1519{
1520 int rc = VINF_SUCCESS;
1521
1522 Log3Func(("pCAStream=%p\n", pCAStream));
1523
1524 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++)
1525 {
1526 AudioQueueBufferRef pBuf = pCAStream->audioBuffer[i];
1527
1528 if (pCAStream->pCfg->enmDir == PDMAUDIODIR_IN)
1529 {
1530 int rc2 = coreAudioInputQueueProcBuffer(pCAStream, pBuf);
1531 if (RT_SUCCESS(rc2))
1532 {
1533 AudioQueueEnqueueBuffer(pCAStream->audioQueue, pBuf, 0, NULL);
1534 }
1535 }
1536 else if (pCAStream->pCfg->enmDir == PDMAUDIODIR_OUT)
1537 {
1538 int rc2 = coreAudioOutputQueueProcBuffer(pCAStream, pBuf);
1539 if ( RT_SUCCESS(rc2)
1540 && pBuf->mAudioDataByteSize)
1541 {
1542 AudioQueueEnqueueBuffer(pCAStream->audioQueue, pBuf, 0, NULL);
1543 }
1544
1545 if (RT_SUCCESS(rc))
1546 rc = rc2;
1547 }
1548 else
1549 AssertFailed();
1550 }
1551
1552 return rc;
1553}
1554
1555/**
1556 * Initializes a Core Audio stream's audio queue.
1557 *
1558 * @returns IPRT status code.
1559 * @param pCAStream Core Audio stream to initialize audio queue for.
1560 * @param pCfgReq Requested stream configuration.
1561 * @param pCfgAcq Acquired stream configuration on success.
1562 */
1563static int coreAudioStreamInitQueue(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1564{
1565 RT_NOREF(pCfgAcq);
1566
1567 LogFunc(("pCAStream=%p, pCfgReq=%p, pCfgAcq=%p\n", pCAStream, pCfgReq, pCfgAcq));
1568
1569 /* No device assigned? Bail out early. */
1570 if (pCAStream->Unit.pDevice == NULL)
1571 return VERR_NOT_AVAILABLE;
1572
1573 const bool fIn = pCfgReq->enmDir == PDMAUDIODIR_IN;
1574
1575 int rc = VINF_SUCCESS;
1576
1577 /* Create the recording device's out format based on our required audio settings. */
1578 Assert(pCAStream->pCfg == NULL);
1579 pCAStream->pCfg = DrvAudioHlpStreamCfgDup(pCfgReq);
1580 if (!pCAStream->pCfg)
1581 rc = VERR_NO_MEMORY;
1582
1583 coreAudioPCMPropsToASBD(&pCfgReq->Props, &pCAStream->asbdStream);
1584 /** @todo Do some validation? */
1585
1586 coreAudioPrintASBD( fIn
1587 ? "Capturing queue format"
1588 : "Playback queue format", &pCAStream->asbdStream);
1589
1590 if (RT_FAILURE(rc))
1591 {
1592 LogRel(("CoreAudio: Failed to convert requested %s format to native format (%Rrc)\n", fIn ? "input" : "output", rc));
1593 return rc;
1594 }
1595
1596 rc = RTCircBufCreate(&pCAStream->pCircBuf, PDMAUDIOSTREAMCFG_F2B(pCfgReq, pCfgReq->Backend.cFramesBufferSize));
1597 if (RT_FAILURE(rc))
1598 return rc;
1599
1600 /*
1601 * Start the thread.
1602 */
1603 rc = RTThreadCreate(&pCAStream->hThread, coreAudioQueueThread,
1604 pCAStream /* pvUser */, 0 /* Default stack size */,
1605 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CAQUEUE");
1606 if (RT_SUCCESS(rc))
1607 rc = RTThreadUserWait(pCAStream->hThread, 10 * 1000 /* 10s timeout */);
1608
1609 LogFunc(("Returning %Rrc\n", rc));
1610 return rc;
1611}
1612
1613/**
1614 * Unitializes a Core Audio stream's audio queue.
1615 *
1616 * @returns IPRT status code.
1617 * @param pCAStream Core Audio stream to unitialize audio queue for.
1618 */
1619static int coreAudioStreamUninitQueue(PCOREAUDIOSTREAM pCAStream)
1620{
1621 LogFunc(("pCAStream=%p\n", pCAStream));
1622
1623 if (pCAStream->hThread != NIL_RTTHREAD)
1624 {
1625 LogFunc(("Waiting for thread ...\n"));
1626
1627 ASMAtomicXchgBool(&pCAStream->fShutdown, true);
1628
1629 int rcThread;
1630 int rc = RTThreadWait(pCAStream->hThread, 30 * 1000, &rcThread);
1631 if (RT_FAILURE(rc))
1632 return rc;
1633
1634 RT_NOREF(rcThread);
1635 LogFunc(("Thread stopped with %Rrc\n", rcThread));
1636
1637 pCAStream->hThread = NIL_RTTHREAD;
1638 }
1639
1640 if (pCAStream->pCfg)
1641 {
1642 DrvAudioHlpStreamCfgFree(pCAStream->pCfg);
1643 pCAStream->pCfg = NULL;
1644 }
1645
1646 if (pCAStream->pCircBuf)
1647 {
1648 RTCircBufDestroy(pCAStream->pCircBuf);
1649 pCAStream->pCircBuf = NULL;
1650 }
1651
1652 LogFunc(("Returning\n"));
1653 return VINF_SUCCESS;
1654}
1655
1656/**
1657 * Unitializes a Core Audio stream.
1658 *
1659 * @returns IPRT status code.
1660 * @param pCAStream Core Audio stream to uninitialize.
1661 */
1662static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream)
1663{
1664 LogFunc(("pCAStream=%p\n", pCAStream));
1665
1666 int rc = coreAudioStreamUninitQueue(pCAStream);
1667 if (RT_SUCCESS(rc))
1668 {
1669 pCAStream->Unit.pDevice = NULL;
1670 pCAStream->pDrv = NULL;
1671 }
1672
1673 return rc;
1674}
1675
1676/**
1677 * Registers callbacks for a specific Core Audio device.
1678 *
1679 * @return IPRT status code.
1680 * @param pThis Host audio driver instance.
1681 * @param pDev Audio device to use for the registered callbacks.
1682 */
1683static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev)
1684{
1685 RT_NOREF(pThis);
1686
1687 AudioDeviceID deviceID = kAudioDeviceUnknown;
1688
1689 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1690 if (pData)
1691 deviceID = pData->deviceID;
1692
1693 if (deviceID != kAudioDeviceUnknown)
1694 {
1695 LogFunc(("deviceID=%RU32\n", deviceID));
1696
1697 /*
1698 * Register device callbacks.
1699 */
1700 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
1701 kAudioObjectPropertyElementMaster };
1702 OSStatus err = AudioObjectAddPropertyListener(deviceID, &propAdr,
1703 coreAudioDeviceStateChangedCb, pDev /* pvUser */);
1704 if ( err != noErr
1705 && err != kAudioHardwareIllegalOperationError)
1706 {
1707 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err));
1708 }
1709
1710 propAdr.mSelector = kAudioDeviceProcessorOverload;
1711 propAdr.mScope = kAudioUnitScope_Global;
1712 err = AudioObjectAddPropertyListener(deviceID, &propAdr,
1713 coreAudioDevPropChgCb, pDev /* pvUser */);
1714 if (err != noErr)
1715 LogRel(("CoreAudio: Failed to register processor overload listener (%RI32)\n", err));
1716
1717 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1718 propAdr.mScope = kAudioUnitScope_Global;
1719 err = AudioObjectAddPropertyListener(deviceID, &propAdr,
1720 coreAudioDevPropChgCb, pDev /* pvUser */);
1721 if (err != noErr)
1722 LogRel(("CoreAudio: Failed to register sample rate changed listener (%RI32)\n", err));
1723 }
1724
1725 return VINF_SUCCESS;
1726}
1727
1728/**
1729 * Unregisters all formerly registered callbacks of a Core Audio device again.
1730 *
1731 * @return IPRT status code.
1732 * @param pThis Host audio driver instance.
1733 * @param pDev Audio device to use for the registered callbacks.
1734 */
1735static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev)
1736{
1737 RT_NOREF(pThis);
1738
1739 AudioDeviceID deviceID = kAudioDeviceUnknown;
1740
1741 if (pDev)
1742 {
1743 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1744 if (pData)
1745 deviceID = pData->deviceID;
1746 }
1747
1748 if (deviceID != kAudioDeviceUnknown)
1749 {
1750 LogFunc(("deviceID=%RU32\n", deviceID));
1751
1752 /*
1753 * Unregister per-device callbacks.
1754 */
1755 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
1756 kAudioObjectPropertyElementMaster };
1757 OSStatus err = AudioObjectRemovePropertyListener(deviceID, &propAdr,
1758 coreAudioDevPropChgCb, pDev /* pvUser */);
1759 if ( err != noErr
1760 && err != kAudioHardwareBadObjectError)
1761 {
1762 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err));
1763 }
1764
1765 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1766 err = AudioObjectRemovePropertyListener(deviceID, &propAdr,
1767 coreAudioDevPropChgCb, pDev /* pvUser */);
1768 if ( err != noErr
1769 && err != kAudioHardwareBadObjectError)
1770 {
1771 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
1772 }
1773
1774 propAdr.mSelector = kAudioDevicePropertyDeviceIsAlive;
1775 err = AudioObjectRemovePropertyListener(deviceID, &propAdr,
1776 coreAudioDeviceStateChangedCb, pDev /* pvUser */);
1777 if ( err != noErr
1778 && err != kAudioHardwareBadObjectError)
1779 {
1780 LogRel(("CoreAudio: Failed to remove the device alive listener (%RI32)\n", err));
1781 }
1782 }
1783
1784 return VINF_SUCCESS;
1785}
1786
1787/* Callback for getting notified when some of the properties of an audio device have changed. */
1788static DECLCALLBACK(OSStatus) coreAudioDevPropChgCb(AudioObjectID propertyID,
1789 UInt32 cAddresses,
1790 const AudioObjectPropertyAddress properties[],
1791 void *pvUser)
1792{
1793 RT_NOREF(cAddresses, properties, pvUser);
1794
1795 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)pvUser;
1796 AssertPtr(pDev);
1797
1798 LogFlowFunc(("propertyID=%u, nAddresses=%u, pDev=%p\n", propertyID, cAddresses, pDev));
1799
1800 switch (propertyID)
1801 {
1802#ifdef DEBUG
1803 case kAudioDeviceProcessorOverload:
1804 {
1805 LogFunc(("Processor overload detected!\n"));
1806 break;
1807 }
1808#endif /* DEBUG */
1809 case kAudioDevicePropertyNominalSampleRate:
1810 {
1811#ifndef VBOX_WITH_AUDIO_CALLBACKS
1812 int rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT);
1813 AssertRC(rc2);
1814#else
1815 RT_NOREF(pDev);
1816#endif
1817 break;
1818 }
1819
1820 default:
1821 /* Just skip. */
1822 break;
1823 }
1824
1825 return noErr;
1826}
1827
1828/**
1829 * Enumerates all available host audio devices internally.
1830 *
1831 * @returns IPRT status code.
1832 * @param pThis Host audio driver instance.
1833 */
1834static int coreAudioEnumerateDevices(PDRVHOSTCOREAUDIO pThis)
1835{
1836 LogFlowFuncEnter();
1837
1838 /*
1839 * Unregister old default devices, if any.
1840 */
1841 if (pThis->pDefaultDevIn)
1842 {
1843 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevIn);
1844 pThis->pDefaultDevIn = NULL;
1845 }
1846
1847 if (pThis->pDefaultDevOut)
1848 {
1849 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevOut);
1850 pThis->pDefaultDevOut = NULL;
1851 }
1852
1853 /* Remove old / stale device entries. */
1854 DrvAudioHlpDeviceEnumFree(&pThis->Devices);
1855
1856 /* Enumerate all devices internally. */
1857 int rc = coreAudioDevicesEnumerateAll(pThis, &pThis->Devices);
1858 if (RT_SUCCESS(rc))
1859 {
1860 /*
1861 * Default input device.
1862 */
1863 pThis->pDefaultDevIn = DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->Devices, PDMAUDIODIR_IN);
1864 if (pThis->pDefaultDevIn)
1865 {
1866 LogRel2(("CoreAudio: Default capturing device is '%s'\n", pThis->pDefaultDevIn->szName));
1867
1868#ifdef DEBUG
1869 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pThis->pDefaultDevIn->pvData;
1870 AssertPtr(pDevData);
1871 LogFunc(("pDefaultDevIn=%p, ID=%RU32\n", pThis->pDefaultDevIn, pDevData->deviceID));
1872#endif
1873 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevIn);
1874 }
1875 else
1876 LogRel2(("CoreAudio: No default capturing device found\n"));
1877
1878 /*
1879 * Default output device.
1880 */
1881 pThis->pDefaultDevOut = DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->Devices, PDMAUDIODIR_OUT);
1882 if (pThis->pDefaultDevOut)
1883 {
1884 LogRel2(("CoreAudio: Default playback device is '%s'\n", pThis->pDefaultDevOut->szName));
1885
1886#ifdef DEBUG
1887 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pThis->pDefaultDevOut->pvData;
1888 AssertPtr(pDevData);
1889 LogFunc(("pDefaultDevOut=%p, ID=%RU32\n", pThis->pDefaultDevOut, pDevData->deviceID));
1890#endif
1891 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevOut);
1892 }
1893 else
1894 LogRel2(("CoreAudio: No default playback device found\n"));
1895 }
1896
1897 LogFunc(("Returning %Rrc\n", rc));
1898 return rc;
1899}
1900
1901/**
1902 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1903 */
1904static DECLCALLBACK(int) drvHostCoreAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1905 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
1906{
1907 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1908 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1909 /* puRead is optional. */
1910
1911 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
1912 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
1913
1914#ifndef VBOX_WITH_AUDIO_CALLBACKS
1915 /* Check if the audio device should be reinitialized. If so do it. */
1916 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT)
1917 {
1918 /* For now re just re-initialize with the current input device. */
1919 if (pThis->pDefaultDevIn)
1920 {
1921 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevIn);
1922 if (RT_FAILURE(rc2))
1923 return VERR_NOT_AVAILABLE;
1924 }
1925 else
1926 return VERR_NOT_AVAILABLE;
1927 }
1928#else
1929 RT_NOREF(pThis);
1930#endif
1931
1932 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT)
1933 {
1934 if (puRead)
1935 *puRead = 0;
1936 return VINF_SUCCESS;
1937 }
1938
1939 int rc = VINF_SUCCESS;
1940
1941 uint32_t cbReadTotal = 0;
1942
1943 rc = RTCritSectEnter(&pCAStream->CritSect);
1944 AssertRC(rc);
1945
1946 do
1947 {
1948 size_t cbToWrite = RT_MIN(uBufSize, RTCircBufUsed(pCAStream->pCircBuf));
1949
1950 uint8_t *pvChunk;
1951 size_t cbChunk;
1952
1953 Log3Func(("cbToWrite=%zu/%zu\n", cbToWrite, RTCircBufSize(pCAStream->pCircBuf)));
1954
1955 while (cbToWrite)
1956 {
1957 /* Try to acquire the necessary block from the ring buffer. */
1958 RTCircBufAcquireReadBlock(pCAStream->pCircBuf, cbToWrite, (void **)&pvChunk, &cbChunk);
1959 if (cbChunk)
1960 memcpy((uint8_t *)pvBuf + cbReadTotal, pvChunk, cbChunk);
1961
1962 /* Release the read buffer, so it could be used for new data. */
1963 RTCircBufReleaseReadBlock(pCAStream->pCircBuf, cbChunk);
1964
1965 if (RT_FAILURE(rc))
1966 break;
1967
1968 Assert(cbToWrite >= cbChunk);
1969 cbToWrite -= cbChunk;
1970
1971 cbReadTotal += cbChunk;
1972 }
1973 }
1974 while (0);
1975
1976 int rc2 = RTCritSectLeave(&pCAStream->CritSect);
1977 AssertRC(rc2);
1978
1979 if (RT_SUCCESS(rc))
1980 {
1981 if (puRead)
1982 *puRead = cbReadTotal;
1983 }
1984
1985 return rc;
1986}
1987
1988/**
1989 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1990 */
1991static DECLCALLBACK(int) drvHostCoreAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1992 const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
1993{
1994 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
1995 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
1996
1997#ifndef VBOX_WITH_AUDIO_CALLBACKS
1998 /* Check if the audio device should be reinitialized. If so do it. */
1999 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT)
2000 {
2001 if (pThis->pDefaultDevOut)
2002 {
2003 /* For now re just re-initialize with the current output device. */
2004 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevOut);
2005 if (RT_FAILURE(rc2))
2006 return VERR_NOT_AVAILABLE;
2007 }
2008 else
2009 return VERR_NOT_AVAILABLE;
2010 }
2011#else
2012 RT_NOREF(pThis);
2013#endif
2014
2015 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT)
2016 {
2017 if (puWritten)
2018 *puWritten = 0;
2019 return VINF_SUCCESS;
2020 }
2021
2022 uint32_t cbWrittenTotal = 0;
2023
2024 int rc = VINF_SUCCESS;
2025
2026 rc = RTCritSectEnter(&pCAStream->CritSect);
2027 AssertRC(rc);
2028
2029 size_t cbToWrite = RT_MIN(uBufSize, RTCircBufFree(pCAStream->pCircBuf));
2030 Log3Func(("cbToWrite=%zu\n", cbToWrite));
2031
2032 uint8_t *pvChunk;
2033 size_t cbChunk;
2034
2035 while (cbToWrite)
2036 {
2037 /* Try to acquire the necessary space from the ring buffer. */
2038 RTCircBufAcquireWriteBlock(pCAStream->pCircBuf, cbToWrite, (void **)&pvChunk, &cbChunk);
2039 if (!cbChunk)
2040 {
2041 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbChunk);
2042 break;
2043 }
2044
2045 Assert(cbChunk <= cbToWrite);
2046 Assert(cbWrittenTotal + cbChunk <= uBufSize);
2047
2048 memcpy(pvChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
2049
2050#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2051 RTFILE fh;
2052 rc = RTFileOpen(&fh,VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caPlayback.pcm",
2053 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
2054 if (RT_SUCCESS(rc))
2055 {
2056 RTFileWrite(fh, pvChunk, cbChunk, NULL);
2057 RTFileClose(fh);
2058 }
2059 else
2060 AssertFailed();
2061#endif
2062
2063 /* Release the ring buffer, so the read thread could start reading this data. */
2064 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbChunk);
2065
2066 if (RT_FAILURE(rc))
2067 break;
2068
2069 Assert(cbToWrite >= cbChunk);
2070 cbToWrite -= cbChunk;
2071
2072 cbWrittenTotal += cbChunk;
2073 }
2074
2075 if ( RT_SUCCESS(rc)
2076 && pCAStream->fRun
2077 && !pCAStream->fIsRunning)
2078 {
2079 rc = coreAudioStreamInvalidateQueue(pCAStream);
2080 if (RT_SUCCESS(rc))
2081 {
2082 AudioQueueStart(pCAStream->audioQueue, NULL);
2083 pCAStream->fRun = false;
2084 pCAStream->fIsRunning = true;
2085 }
2086 }
2087
2088 int rc2 = RTCritSectLeave(&pCAStream->CritSect);
2089 AssertRC(rc2);
2090
2091 if (RT_SUCCESS(rc))
2092 {
2093 if (puWritten)
2094 *puWritten = cbWrittenTotal;
2095 }
2096
2097 return rc;
2098}
2099
2100static DECLCALLBACK(int) coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis,
2101 PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2102{
2103 RT_NOREF(pThis);
2104
2105 uint32_t enmStatus = ASMAtomicReadU32(&pCAStream->enmStatus);
2106
2107 LogFlowFunc(("enmStreamCmd=%RU32, enmStatus=%RU32\n", enmStreamCmd, enmStatus));
2108
2109 if (!( enmStatus == COREAUDIOSTATUS_INIT
2110#ifndef VBOX_WITH_AUDIO_CALLBACKS
2111 || enmStatus == COREAUDIOSTATUS_REINIT
2112#endif
2113 ))
2114 {
2115 return VINF_SUCCESS;
2116 }
2117
2118 if (!pCAStream->pCfg) /* Not (yet) configured? Skip. */
2119 return VINF_SUCCESS;
2120
2121 int rc = VINF_SUCCESS;
2122
2123 switch (enmStreamCmd)
2124 {
2125 case PDMAUDIOSTREAMCMD_ENABLE:
2126 case PDMAUDIOSTREAMCMD_RESUME:
2127 {
2128 LogFunc(("Queue enable\n"));
2129 if (pCAStream->pCfg->enmDir == PDMAUDIODIR_IN)
2130 {
2131 rc = coreAudioStreamInvalidateQueue(pCAStream);
2132 if (RT_SUCCESS(rc))
2133 {
2134 /* Start the audio queue immediately. */
2135 AudioQueueStart(pCAStream->audioQueue, NULL);
2136 }
2137 }
2138 else if (pCAStream->pCfg->enmDir == PDMAUDIODIR_OUT)
2139 {
2140 /* Touch the run flag to start the audio queue as soon as
2141 * we have anough data to actually play something. */
2142 ASMAtomicXchgBool(&pCAStream->fRun, true);
2143 }
2144 break;
2145 }
2146
2147 case PDMAUDIOSTREAMCMD_DISABLE:
2148 {
2149 LogFunc(("Queue disable\n"));
2150 AudioQueueStop(pCAStream->audioQueue, 1 /* Immediately */);
2151 ASMAtomicXchgBool(&pCAStream->fRun, false);
2152 ASMAtomicXchgBool(&pCAStream->fIsRunning, false);
2153 break;
2154 }
2155 case PDMAUDIOSTREAMCMD_PAUSE:
2156 {
2157 LogFunc(("Queue pause\n"));
2158 AudioQueuePause(pCAStream->audioQueue);
2159 ASMAtomicXchgBool(&pCAStream->fIsRunning, false);
2160 break;
2161 }
2162
2163 default:
2164 rc = VERR_NOT_SUPPORTED;
2165 break;
2166 }
2167
2168 LogFlowFuncLeaveRC(rc);
2169 return rc;
2170}
2171
2172
2173/**
2174 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
2175 */
2176static DECLCALLBACK(int) drvHostCoreAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
2177{
2178 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2179 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
2180
2181 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2182
2183 RT_BZERO(pBackendCfg, sizeof(PDMAUDIOBACKENDCFG));
2184
2185 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Core Audio driver");
2186
2187 pBackendCfg->cbStreamIn = sizeof(COREAUDIOSTREAM);
2188 pBackendCfg->cbStreamOut = sizeof(COREAUDIOSTREAM);
2189
2190 /* For Core Audio we provide one stream per device for now. */
2191 pBackendCfg->cMaxStreamsIn = DrvAudioHlpDeviceEnumGetDeviceCount(&pThis->Devices, PDMAUDIODIR_IN);
2192 pBackendCfg->cMaxStreamsOut = DrvAudioHlpDeviceEnumGetDeviceCount(&pThis->Devices, PDMAUDIODIR_OUT);
2193
2194 LogFlowFunc(("Returning %Rrc\n", VINF_SUCCESS));
2195 return VINF_SUCCESS;
2196}
2197
2198
2199/**
2200 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices}
2201 */
2202static DECLCALLBACK(int) drvHostCoreAudioGetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIODEVICEENUM pDeviceEnum)
2203{
2204 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2205 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER);
2206
2207 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2208
2209 int rc = RTCritSectEnter(&pThis->CritSect);
2210 if (RT_SUCCESS(rc))
2211 {
2212 rc = coreAudioEnumerateDevices(pThis);
2213 if (RT_SUCCESS(rc))
2214 {
2215 if (pDeviceEnum)
2216 {
2217 rc = DrvAudioHlpDeviceEnumInit(pDeviceEnum);
2218 if (RT_SUCCESS(rc))
2219 rc = DrvAudioHlpDeviceEnumCopy(pDeviceEnum, &pThis->Devices);
2220
2221 if (RT_FAILURE(rc))
2222 DrvAudioHlpDeviceEnumFree(pDeviceEnum);
2223 }
2224 }
2225
2226 int rc2 = RTCritSectLeave(&pThis->CritSect);
2227 AssertRC(rc2);
2228 }
2229
2230 LogFlowFunc(("Returning %Rrc\n", rc));
2231 return rc;
2232}
2233
2234
2235#ifdef VBOX_WITH_AUDIO_CALLBACKS
2236/**
2237 * @interface_method_impl{PDMIHOSTAUDIO,pfnSetCallback}
2238 */
2239static DECLCALLBACK(int) drvHostCoreAudioSetCallback(PPDMIHOSTAUDIO pInterface, PFNPDMHOSTAUDIOCALLBACK pfnCallback)
2240{
2241 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2242 /* pfnCallback will be handled below. */
2243
2244 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2245
2246 int rc = RTCritSectEnter(&pThis->CritSect);
2247 if (RT_SUCCESS(rc))
2248 {
2249 LogFunc(("pfnCallback=%p\n", pfnCallback));
2250
2251 if (pfnCallback) /* Register. */
2252 {
2253 Assert(pThis->pfnCallback == NULL);
2254 pThis->pfnCallback = pfnCallback;
2255 }
2256 else /* Unregister. */
2257 {
2258 if (pThis->pfnCallback)
2259 pThis->pfnCallback = NULL;
2260 }
2261
2262 int rc2 = RTCritSectLeave(&pThis->CritSect);
2263 AssertRC(rc2);
2264 }
2265
2266 return rc;
2267}
2268#endif
2269
2270
2271/**
2272 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
2273 */
2274static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2275{
2276 RT_NOREF(pInterface, enmDir);
2277 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2278
2279 return PDMAUDIOBACKENDSTS_RUNNING;
2280}
2281
2282
2283/**
2284 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
2285 */
2286static DECLCALLBACK(int) drvHostCoreAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
2287 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2288{
2289 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2290 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2291 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2292 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2293
2294 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2295 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
2296
2297 int rc = RTCritSectInit(&pCAStream->CritSect);
2298 if (RT_FAILURE(rc))
2299 return rc;
2300
2301 pCAStream->hThread = NIL_RTTHREAD;
2302 pCAStream->fRun = false;
2303 pCAStream->fIsRunning = false;
2304 pCAStream->fShutdown = false;
2305
2306 /* Input or output device? */
2307 bool fIn = pCfgReq->enmDir == PDMAUDIODIR_IN;
2308
2309 /* For now, just use the default device available. */
2310 PPDMAUDIODEVICE pDev = fIn ? pThis->pDefaultDevIn : pThis->pDefaultDevOut;
2311
2312 LogFunc(("pStream=%p, pCfgReq=%p, pCfgAcq=%p, fIn=%RTbool, pDev=%p\n", pStream, pCfgReq, pCfgAcq, fIn, pDev));
2313
2314 if (pDev) /* (Default) device available? */
2315 {
2316 /* Sanity. */
2317 AssertPtr(pDev->pvData);
2318 Assert(pDev->cbData);
2319
2320 /* Init the Core Audio stream. */
2321 rc = coreAudioStreamInit(pCAStream, pThis, pDev);
2322 if (RT_SUCCESS(rc))
2323 {
2324 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_INIT);
2325
2326 rc = coreAudioStreamInitQueue(pCAStream, pCfgReq, pCfgAcq);
2327 if (RT_SUCCESS(rc))
2328 {
2329 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT);
2330 }
2331 else
2332 {
2333 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_UNINIT);
2334
2335 int rc2 = coreAudioStreamUninit(pCAStream);
2336 AssertRC(rc2);
2337
2338 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_UNINIT);
2339 }
2340 }
2341 }
2342 else
2343 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
2344
2345 LogFunc(("Returning %Rrc\n", rc));
2346 return rc;
2347}
2348
2349
2350/**
2351 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
2352 */
2353static DECLCALLBACK(int) drvHostCoreAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2354{
2355 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2356 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2357
2358 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2359
2360 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
2361
2362 uint32_t status = ASMAtomicReadU32(&pCAStream->enmStatus);
2363 if (!( status == COREAUDIOSTATUS_INIT
2364#ifndef VBOX_WITH_AUDIO_CALLBACKS
2365 || status == COREAUDIOSTATUS_REINIT
2366#endif
2367 ))
2368 {
2369 return VINF_SUCCESS;
2370 }
2371
2372 if (!pCAStream->pCfg) /* Not (yet) configured? Skip. */
2373 return VINF_SUCCESS;
2374
2375 int rc = coreAudioStreamControl(pThis, pCAStream, PDMAUDIOSTREAMCMD_DISABLE);
2376 if (RT_SUCCESS(rc))
2377 {
2378 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_UNINIT);
2379
2380 rc = coreAudioStreamUninit(pCAStream);
2381
2382 if (RT_SUCCESS(rc))
2383 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_UNINIT);
2384 }
2385
2386 if (RT_SUCCESS(rc))
2387 {
2388 if (RTCritSectIsInitialized(&pCAStream->CritSect))
2389 RTCritSectDelete(&pCAStream->CritSect);
2390 }
2391
2392 LogFunc(("rc=%Rrc\n", rc));
2393 return rc;
2394}
2395
2396
2397/**
2398 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
2399 */
2400static DECLCALLBACK(int) drvHostCoreAudioStreamControl(PPDMIHOSTAUDIO pInterface,
2401 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2402{
2403 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2404 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2405
2406 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2407 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
2408
2409 return coreAudioStreamControl(pThis, pCAStream, enmStreamCmd);
2410}
2411
2412
2413/**
2414 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
2415 */
2416static DECLCALLBACK(uint32_t) drvHostCoreAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2417{
2418 RT_NOREF(pInterface);
2419 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2420
2421 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
2422
2423 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT)
2424 return 0;
2425
2426 AssertPtr(pCAStream->pCfg);
2427 AssertPtr(pCAStream->pCircBuf);
2428
2429 switch (pCAStream->pCfg->enmDir)
2430 {
2431 case PDMAUDIODIR_IN:
2432 return (uint32_t)RTCircBufUsed(pCAStream->pCircBuf);
2433
2434 case PDMAUDIODIR_OUT:
2435 default:
2436 AssertFailed();
2437 break;
2438 }
2439
2440 return 0;
2441}
2442
2443
2444/**
2445 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
2446 */
2447static DECLCALLBACK(uint32_t) drvHostCoreAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2448{
2449 RT_NOREF(pInterface);
2450 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2451
2452 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
2453
2454 uint32_t cbWritable = 0;
2455
2456 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_INIT)
2457 {
2458 AssertPtr(pCAStream->pCfg);
2459 AssertPtr(pCAStream->pCircBuf);
2460
2461 switch (pCAStream->pCfg->enmDir)
2462 {
2463 case PDMAUDIODIR_OUT:
2464 cbWritable = (uint32_t)RTCircBufFree(pCAStream->pCircBuf);
2465 break;
2466
2467 default:
2468 break;
2469 }
2470 }
2471
2472 LogFlowFunc(("cbWritable=%RU32\n", cbWritable));
2473 return cbWritable;
2474}
2475
2476
2477/**
2478 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
2479 */
2480static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostCoreAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2481{
2482 RT_NOREF(pInterface);
2483 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2484
2485 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
2486
2487 PDMAUDIOSTREAMSTS strmSts = PDMAUDIOSTREAMSTS_FLAG_NONE;
2488
2489 if (!pCAStream->pCfg) /* Not (yet) configured? Skip. */
2490 return strmSts;
2491
2492 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_INIT)
2493 strmSts |= PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED;
2494
2495 return strmSts;
2496}
2497
2498
2499/**
2500 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
2501 */
2502static DECLCALLBACK(int) drvHostCoreAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
2503{
2504 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2505 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2506
2507 RT_NOREF(pInterface, pStream);
2508
2509 /* Nothing to do here for Core Audio. */
2510 return VINF_SUCCESS;
2511}
2512
2513
2514/**
2515 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
2516 */
2517static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)
2518{
2519 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2520
2521 int rc = DrvAudioHlpDeviceEnumInit(&pThis->Devices);
2522 if (RT_SUCCESS(rc))
2523 {
2524 /* Do the first (initial) internal device enumeration. */
2525 rc = coreAudioEnumerateDevices(pThis);
2526 }
2527
2528 if (RT_SUCCESS(rc))
2529 {
2530 /* Register system callbacks. */
2531 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
2532 kAudioObjectPropertyElementMaster };
2533
2534 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2535 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
2536 if ( err != noErr
2537 && err != kAudioHardwareIllegalOperationError)
2538 {
2539 LogRel(("CoreAudio: Failed to add the input default device changed listener (%RI32)\n", err));
2540 }
2541
2542 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
2543 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2544 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
2545 if ( err != noErr
2546 && err != kAudioHardwareIllegalOperationError)
2547 {
2548 LogRel(("CoreAudio: Failed to add the output default device changed listener (%RI32)\n", err));
2549 }
2550 }
2551
2552 LogFlowFunc(("Returning %Rrc\n", rc));
2553 return rc;
2554}
2555
2556
2557/**
2558 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
2559 */
2560static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
2561{
2562 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2563
2564 /*
2565 * Unregister system callbacks.
2566 */
2567 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
2568 kAudioObjectPropertyElementMaster };
2569
2570 OSStatus err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
2571 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
2572 if ( err != noErr
2573 && err != kAudioHardwareBadObjectError)
2574 {
2575 LogRel(("CoreAudio: Failed to remove the default input device changed listener (%RI32)\n", err));
2576 }
2577
2578 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
2579 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
2580 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
2581 if ( err != noErr
2582 && err != kAudioHardwareBadObjectError)
2583 {
2584 LogRel(("CoreAudio: Failed to remove the default output device changed listener (%RI32)\n", err));
2585 }
2586
2587 LogFlowFuncEnter();
2588}
2589
2590
2591/**
2592 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2593 */
2594static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2595{
2596 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2597 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2598
2599 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2600 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2601
2602 return NULL;
2603}
2604
2605
2606/**
2607 * @callback_method_impl{FNPDMDRVCONSTRUCT,
2608 * Construct a Core Audio driver instance.}
2609 */
2610static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2611{
2612 RT_NOREF(pCfg, fFlags);
2613 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2614 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2615 LogRel(("Audio: Initializing Core Audio driver\n"));
2616
2617 /*
2618 * Init the static parts.
2619 */
2620 pThis->pDrvIns = pDrvIns;
2621 /* IBase */
2622 pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
2623 /* IHostAudio */
2624 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
2625
2626 /* This backend supports device enumeration. */
2627 pThis->IHostAudio.pfnGetDevices = drvHostCoreAudioGetDevices;
2628
2629#ifdef VBOX_WITH_AUDIO_CALLBACKS
2630 /* This backend supports host audio callbacks. */
2631 pThis->IHostAudio.pfnSetCallback = drvHostCoreAudioSetCallback;
2632 pThis->pfnCallback = NULL;
2633#endif
2634
2635 int rc = RTCritSectInit(&pThis->CritSect);
2636
2637#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
2638 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caConverterCbInput.pcm");
2639 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caPlayback.pcm");
2640#endif
2641
2642 LogFlowFuncLeaveRC(rc);
2643 return rc;
2644}
2645
2646
2647/**
2648 * @callback_method_impl{FNPDMDRVDESTRUCT}
2649 */
2650static DECLCALLBACK(void) drvHostCoreAudioDestruct(PPDMDRVINS pDrvIns)
2651{
2652 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2653 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2654
2655 int rc2 = RTCritSectDelete(&pThis->CritSect);
2656 AssertRC(rc2);
2657
2658 LogFlowFuncLeaveRC(rc2);
2659}
2660
2661
2662/**
2663 * Char driver registration record.
2664 */
2665const PDMDRVREG g_DrvHostCoreAudio =
2666{
2667 /* u32Version */
2668 PDM_DRVREG_VERSION,
2669 /* szName */
2670 "CoreAudio",
2671 /* szRCMod */
2672 "",
2673 /* szR0Mod */
2674 "",
2675 /* pszDescription */
2676 "Core Audio host driver",
2677 /* fFlags */
2678 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2679 /* fClass. */
2680 PDM_DRVREG_CLASS_AUDIO,
2681 /* cMaxInstances */
2682 ~0U,
2683 /* cbInstance */
2684 sizeof(DRVHOSTCOREAUDIO),
2685 /* pfnConstruct */
2686 drvHostCoreAudioConstruct,
2687 /* pfnDestruct */
2688 drvHostCoreAudioDestruct,
2689 /* pfnRelocate */
2690 NULL,
2691 /* pfnIOCtl */
2692 NULL,
2693 /* pfnPowerOn */
2694 NULL,
2695 /* pfnReset */
2696 NULL,
2697 /* pfnSuspend */
2698 NULL,
2699 /* pfnResume */
2700 NULL,
2701 /* pfnAttach */
2702 NULL,
2703 /* pfnDetach */
2704 NULL,
2705 /* pfnPowerOff */
2706 NULL,
2707 /* pfnSoftReset */
2708 NULL,
2709 /* u32EndVersion */
2710 PDM_DRVREG_VERSION
2711};
2712
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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