VirtualBox

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

Last change on this file since 68253 was 68238, checked in by vboxsync, 7 years ago

Audio/DrvHostCoreAudio.cpp: More cleanup code for coreAudioStreamUninit() / coreAudioStreamUninitQueue().

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