VirtualBox

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

Last change on this file since 88158 was 88061, checked in by vboxsync, 4 years ago

Audio: Should use versioned magics like for other PDM structures, sorry guys. Added magic to PDMAUDIOMIXBUF. bugref:9890

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