VirtualBox

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

Last change on this file since 69304 was 69118, checked in by vboxsync, 7 years ago

Audio: scm

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