VirtualBox

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

Last change on this file since 88985 was 88959, checked in by vboxsync, 4 years ago

Audio/VaKit: Added driver backends for MacOS and Windows. bugref:10008

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