VirtualBox

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

Last change on this file since 71136 was 70318, checked in by vboxsync, 7 years ago

Audio/DrvHostCoreAudio.cpp: Forward ported having the default audio queue configuration split up in more but smaller buffers (32).

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