VirtualBox

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

Last change on this file since 73370 was 73370, checked in by vboxsync, 6 years ago

Audio: Implemented backend-independent (pre-)buffering support. Work in progress.

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