VirtualBox

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

Last change on this file since 64685 was 64612, checked in by vboxsync, 8 years ago

build fix

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