VirtualBox

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

Last change on this file since 85561 was 85199, checked in by vboxsync, 5 years ago

DrvHostCoreAudio: Missing DECLCALLBACKs on prototypes. coreAudioStreamControl does not need DECLCALLBACK. bugref:9794

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

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