VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioCoreAudio.cpp@ 88375

Last change on this file since 88375 was 88362, checked in by vboxsync, 4 years ago

Audio: Removed PDMIHOSTAUDIO::pfnSetCallback (replaced by PDMIAUDIONOTIFYFROMHOST). bugref:9890

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette