VirtualBox

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

Last change on this file since 63989 was 63958, checked in by vboxsync, 8 years ago

Audio/DrvHostCoreAudio.cpp: Update on Audio Queues (work in progress, not yet enabled by default).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 142.9 KB
Line 
1/* $Id: DrvHostCoreAudio.cpp 63958 2016-09-22 15:20:29Z 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#ifndef VBOX_WITH_AUDIO_CA_QUEUES
41# include <AudioToolbox/AudioToolbox.h>
42#endif
43
44#if 0
45# include <iprt/file.h>
46# define DEBUG_DUMP_PCM_DATA
47# ifdef RT_OS_WINDOWS
48# define DEBUG_DUMP_PCM_DATA_PATH "c:\\temp\\"
49# else
50# define DEBUG_DUMP_PCM_DATA_PATH "/tmp/"
51# endif
52#endif
53
54/* Enables utilizing the Core Audio converter unit for converting
55 * input / output from/to our requested formats. That might be more
56 * performant than using our own routines later down the road. */
57/** @todo Needs more investigation and testing first before enabling. */
58//# define VBOX_WITH_AUDIO_CA_CONVERTER
59
60#ifdef DEBUG_andy
61/** Enables support for Audio Queues. */
62# define VBOX_WITH_AUDIO_CA_QUEUES
63#endif
64
65#ifdef DEBUG_andy
66# undef DEBUG_DUMP_PCM_DATA_PATH
67# define DEBUG_DUMP_PCM_DATA_PATH "/Users/anloeffl/Documents/"
68# undef VBOX_WITH_AUDIO_CA_CONVERTER
69#endif
70
71/** @todo
72 * - Maybe make sure the threads are immediately stopped if playing/recording stops.
73 */
74
75/*
76 * Most of this is based on:
77 * http://developer.apple.com/mac/library/technotes/tn2004/tn2097.html
78 * http://developer.apple.com/mac/library/technotes/tn2002/tn2091.html
79 * http://developer.apple.com/mac/library/qa/qa2007/qa1533.html
80 * http://developer.apple.com/mac/library/qa/qa2001/qa1317.html
81 * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
82 */
83
84/* Prototypes needed for COREAUDIODEVICE. */
85struct DRVHOSTCOREAUDIO;
86typedef struct DRVHOSTCOREAUDIO *PDRVHOSTCOREAUDIO;
87
88/**
89 * Structure for holding Core Audio-specific device data.
90 * This data then lives in the pvData part of the PDMAUDIODEVICE struct.
91 */
92typedef struct COREAUDIODEVICEDATA
93{
94 /** Pointer to driver instance this device is bound to. */
95 PDRVHOSTCOREAUDIO pDrv;
96 /** The audio device ID of the currently used device (UInt32 typedef). */
97 AudioDeviceID deviceID;
98 /** The device' UUID. */
99 CFStringRef UUID;
100 /** List of attached (native) Core Audio streams attached to this device. */
101 RTLISTANCHOR lstStreams;
102} COREAUDIODEVICEDATA, *PCOREAUDIODEVICEDATA;
103
104/**
105 * Host Coreaudio driver instance data.
106 * @implements PDMIAUDIOCONNECTOR
107 */
108typedef struct DRVHOSTCOREAUDIO
109{
110 /** Pointer to the driver instance structure. */
111 PPDMDRVINS pDrvIns;
112 /** Pointer to host audio interface. */
113 PDMIHOSTAUDIO IHostAudio;
114 /** Critical section to serialize access. */
115 RTCRITSECT CritSect;
116 /** Current (last reported) device enumeration. */
117 PDMAUDIODEVICEENUM Devices;
118 /** Pointer to the currently used input device in the device enumeration.
119 * Can be NULL if none assigned. */
120 PPDMAUDIODEVICE pDefaultDevIn;
121 /** Pointer to the currently used output device in the device enumeration.
122 * Can be NULL if none assigned. */
123 PPDMAUDIODEVICE pDefaultDevOut;
124#ifdef VBOX_WITH_AUDIO_CALLBACKS
125 /** Callback function to the upper driver.
126 * Can be NULL if not being used / registered. */
127 PFNPDMHOSTAUDIOCALLBACK pfnCallback;
128#endif
129} DRVHOSTCOREAUDIO, *PDRVHOSTCOREAUDIO;
130
131/** Converts a pointer to DRVHOSTCOREAUDIO::IHostAudio to a PDRVHOSTCOREAUDIO. */
132#define PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTCOREAUDIO, IHostAudio)
133
134/**
135 * Structure for holding a Core Audio unit
136 * and its data.
137 */
138typedef struct COREAUDIOUNIT
139{
140 /** Pointer to the device this audio unit is bound to.
141 * Can be NULL if not bound to a device (anymore). */
142 PPDMAUDIODEVICE pDevice;
143 /** The actual audio unit object. */
144 AudioUnit audioUnit;
145 /** Stream description for using with VBox:
146 * - When using this audio unit for input (capturing), this format states
147 * the unit's output format.
148 * - When using this audio unit for output (playback), this format states
149 * the unit's input format. */
150 AudioStreamBasicDescription streamFmt;
151} COREAUDIOUNIT, *PCOREAUDIOUNIT;
152
153/*******************************************************************************
154 *
155 * Helper function section
156 *
157 ******************************************************************************/
158
159/* Move these down below the internal function prototypes... */
160
161static void coreAudioPrintASBD(const char *pszDesc, const AudioStreamBasicDescription *pASBD)
162{
163 char pszSampleRate[32];
164 LogRel2(("CoreAudio: %s description:\n", pszDesc));
165 LogRel2(("CoreAudio:\tFormat ID: %RU32 (%c%c%c%c)\n", pASBD->mFormatID,
166 RT_BYTE4(pASBD->mFormatID), RT_BYTE3(pASBD->mFormatID),
167 RT_BYTE2(pASBD->mFormatID), RT_BYTE1(pASBD->mFormatID)));
168 LogRel2(("CoreAudio:\tFlags: %RU32", pASBD->mFormatFlags));
169 if (pASBD->mFormatFlags & kAudioFormatFlagIsFloat)
170 LogRel2((" Float"));
171 if (pASBD->mFormatFlags & kAudioFormatFlagIsBigEndian)
172 LogRel2((" BigEndian"));
173 if (pASBD->mFormatFlags & kAudioFormatFlagIsSignedInteger)
174 LogRel2((" SignedInteger"));
175 if (pASBD->mFormatFlags & kAudioFormatFlagIsPacked)
176 LogRel2((" Packed"));
177 if (pASBD->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
178 LogRel2((" AlignedHigh"));
179 if (pASBD->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
180 LogRel2((" NonInterleaved"));
181 if (pASBD->mFormatFlags & kAudioFormatFlagIsNonMixable)
182 LogRel2((" NonMixable"));
183 if (pASBD->mFormatFlags & kAudioFormatFlagsAreAllClear)
184 LogRel2((" AllClear"));
185 LogRel2(("\n"));
186 snprintf(pszSampleRate, 32, "%.2f", (float)pASBD->mSampleRate); /** @todo r=andy Use RTStrPrint*. */
187 LogRel2(("CoreAudio:\tSampleRate : %s\n", pszSampleRate));
188 LogRel2(("CoreAudio:\tChannelsPerFrame: %RU32\n", pASBD->mChannelsPerFrame));
189 LogRel2(("CoreAudio:\tFramesPerPacket : %RU32\n", pASBD->mFramesPerPacket));
190 LogRel2(("CoreAudio:\tBitsPerChannel : %RU32\n", pASBD->mBitsPerChannel));
191 LogRel2(("CoreAudio:\tBytesPerFrame : %RU32\n", pASBD->mBytesPerFrame));
192 LogRel2(("CoreAudio:\tBytesPerPacket : %RU32\n", pASBD->mBytesPerPacket));
193}
194
195static void coreAudioPCMPropsToASBD(PDMAUDIOPCMPROPS *pPCMProps, AudioStreamBasicDescription *pASBD)
196{
197 AssertPtrReturnVoid(pPCMProps);
198 AssertPtrReturnVoid(pASBD);
199
200 RT_BZERO(pASBD, sizeof(AudioStreamBasicDescription));
201
202 pASBD->mFormatID = kAudioFormatLinearPCM;
203 pASBD->mFormatFlags = kAudioFormatFlagIsPacked;
204 pASBD->mFramesPerPacket = 1; /* For uncompressed audio, set this to 1. */
205 pASBD->mSampleRate = (Float64)pPCMProps->uHz;
206 pASBD->mChannelsPerFrame = pPCMProps->cChannels;
207 pASBD->mBitsPerChannel = pPCMProps->cBits;
208 if (pPCMProps->fSigned)
209 pASBD->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
210 pASBD->mBytesPerFrame = pASBD->mChannelsPerFrame * (pASBD->mBitsPerChannel / 8);
211 pASBD->mBytesPerPacket = pASBD->mFramesPerPacket * pASBD->mBytesPerFrame;
212}
213
214static int coreAudioStreamCfgToASBD(PPDMAUDIOSTREAMCFG pCfg, AudioStreamBasicDescription *pASBD)
215{
216 AssertPtrReturn(pCfg, VERR_INVALID_PARAMETER);
217 AssertPtrReturn(pASBD, VERR_INVALID_PARAMETER);
218
219 PDMAUDIOPCMPROPS Props;
220 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &Props);
221 if (RT_SUCCESS(rc))
222 coreAudioPCMPropsToASBD(&Props, pASBD);
223
224 return rc;
225}
226
227#ifndef VBOX_WITH_AUDIO_CA_QUEUES
228static int coreAudioASBDToStreamCfg(AudioStreamBasicDescription *pASBD, PPDMAUDIOSTREAMCFG pCfg)
229{
230 AssertPtrReturn(pASBD, VERR_INVALID_PARAMETER);
231 AssertPtrReturn(pCfg, VERR_INVALID_PARAMETER);
232
233 pCfg->cChannels = pASBD->mChannelsPerFrame;
234 pCfg->uHz = (uint32_t)pASBD->mSampleRate;
235 pCfg->enmEndianness = PDMAUDIOENDIANNESS_LITTLE;
236
237 int rc = VINF_SUCCESS;
238
239 if (pASBD->mFormatFlags & kAudioFormatFlagIsSignedInteger)
240 {
241 switch (pASBD->mBitsPerChannel)
242 {
243 case 8: pCfg->enmFormat = PDMAUDIOFMT_S8; break;
244 case 16: pCfg->enmFormat = PDMAUDIOFMT_S16; break;
245 case 32: pCfg->enmFormat = PDMAUDIOFMT_S32; break;
246 default: rc = VERR_NOT_SUPPORTED; break;
247 }
248 }
249 else
250 {
251 switch (pASBD->mBitsPerChannel)
252 {
253 case 8: pCfg->enmFormat = PDMAUDIOFMT_U8; break;
254 case 16: pCfg->enmFormat = PDMAUDIOFMT_U16; break;
255 case 32: pCfg->enmFormat = PDMAUDIOFMT_U32; break;
256 default: rc = VERR_NOT_SUPPORTED; break;
257 }
258 }
259
260 AssertRC(rc);
261 return rc;
262}
263
264static OSStatus coreAudioSetFrameBufferSize(AudioDeviceID deviceID, bool fInput, UInt32 cReqSize, UInt32 *pcActSize)
265{
266 AudioObjectPropertyScope propScope = fInput
267 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
268 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyBufferFrameSize, propScope,
269 kAudioObjectPropertyElementMaster };
270
271 /* First try to set the new frame buffer size. */
272 OSStatus err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);
273
274 /* Check if it really was set. */
275 UInt32 cSize = sizeof(*pcActSize);
276 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
277 if (RT_UNLIKELY(err != noErr))
278 return err;
279
280 /* If both sizes are the same, we are done. */
281 if (cReqSize == *pcActSize)
282 return noErr;
283
284 /* If not we have to check the limits of the device. First get the size of
285 the buffer size range property. */
286 propAdr.mSelector = kAudioDevicePropertyBufferSizeRange;
287 err = AudioObjectGetPropertyDataSize(deviceID, &propAdr, 0, NULL, &cSize);
288 if (RT_UNLIKELY(err != noErr))
289 return err;
290
291 Assert(cSize);
292 AudioValueRange *pRange = (AudioValueRange *)RTMemAllocZ(cSize);
293 if (pRange)
294 {
295 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pRange);
296 if (err == noErr)
297 {
298 Float64 cMin = -1;
299 Float64 cMax = -1;
300 for (size_t a = 0; a < cSize / sizeof(AudioValueRange); a++)
301 {
302 /* Search for the absolute minimum. */
303 if ( pRange[a].mMinimum < cMin
304 || cMin == -1)
305 cMin = pRange[a].mMinimum;
306
307 /* Search for the best maximum which isn't bigger than cReqSize. */
308 if (pRange[a].mMaximum < cReqSize)
309 {
310 if (pRange[a].mMaximum > cMax)
311 cMax = pRange[a].mMaximum;
312 }
313 }
314 if (cMax == -1)
315 cMax = cMin;
316 cReqSize = cMax;
317
318 /* First try to set the new frame buffer size. */
319 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
320 err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);
321 if (err == noErr)
322 {
323 /* Check if it really was set. */
324 cSize = sizeof(*pcActSize);
325 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
326 }
327 }
328
329 RTMemFree(pRange);
330 }
331 else
332 err = notEnoughMemoryErr;
333
334 return err;
335}
336#endif
337
338#if 0 /* unused */
339static int coreAudioCFStringToCString(const CFStringRef pCFString, char **ppszString)
340{
341 CFIndex cLen = CFStringGetLength(pCFString) + 1;
342 char *pszResult = (char *)RTMemAllocZ(cLen * sizeof(char));
343 if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
344 {
345 RTMemFree(pszResult);
346 return VERR_NOT_FOUND;
347 }
348
349 *ppszString = pszResult;
350 return VINF_SUCCESS;
351}
352
353static AudioDeviceID coreAudioDeviceUIDtoID(const char* pszUID)
354{
355 /* Create a CFString out of our CString. */
356 CFStringRef strUID = CFStringCreateWithCString(NULL, pszUID, kCFStringEncodingMacRoman);
357
358 /* Fill the translation structure. */
359 AudioDeviceID deviceID;
360
361 AudioValueTranslation translation;
362 translation.mInputData = &strUID;
363 translation.mInputDataSize = sizeof(CFStringRef);
364 translation.mOutputData = &deviceID;
365 translation.mOutputDataSize = sizeof(AudioDeviceID);
366
367 /* Fetch the translation from the UID to the device ID. */
368 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDeviceForUID, kAudioObjectPropertyScopeGlobal,
369 kAudioObjectPropertyElementMaster };
370
371 UInt32 uSize = sizeof(AudioValueTranslation);
372 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &translation);
373
374 /* Release the temporary CFString */
375 CFRelease(strUID);
376
377 if (RT_LIKELY(err == noErr))
378 return deviceID;
379
380 /* Return the unknown device on error. */
381 return kAudioDeviceUnknown;
382}
383#endif /* unused */
384
385
386/*********************************************************************************************************************************
387* Defined Constants And Macros *
388*********************************************************************************************************************************/
389
390/** @name Initialization status indicator used for the recreation of the AudioUnits.
391 *
392 * Global structures section
393 *
394 ******************************************************************************/
395
396/**
397 * Enumeration for a Core Audio stream status.
398 */
399typedef enum COREAUDIOSTATUS
400{
401 /** The device is uninitialized. */
402 COREAUDIOSTATUS_UNINIT = 0,
403 /** The device is currently initializing. */
404 COREAUDIOSTATUS_IN_INIT,
405 /** The device is initialized. */
406 COREAUDIOSTATUS_INIT,
407 /** The device is currently uninitializing. */
408 COREAUDIOSTATUS_IN_UNINIT,
409#ifndef VBOX_WITH_AUDIO_CALLBACKS
410 /** The device has to be reinitialized.
411 * Note: Only needed if VBOX_WITH_AUDIO_CALLBACKS is not defined, as otherwise
412 * the Audio Connector will take care of this as soon as this backend
413 * tells it to do so via the provided audio callback. */
414 COREAUDIOSTATUS_REINIT,
415#endif
416 /** The usual 32-bit hack. */
417 COREAUDIOSTATUS_32BIT_HACK = 0x7fffffff
418} COREAUDIOSTATUS, *PCOREAUDIOSTATUS;
419
420#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
421 /* Error code which indicates "End of data" */
422 static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */
423#endif
424
425/* Prototypes needed for COREAUDIOSTREAMCBCTX. */
426struct COREAUDIOSTREAM;
427typedef struct COREAUDIOSTREAM *PCOREAUDIOSTREAM;
428
429/**
430 * Structure for keeping a conversion callback context.
431 * This is needed when using an audio converter during input/output processing.
432 */
433typedef struct COREAUDIOCONVCBCTX
434{
435 /** Pointer to the stream this context is bound to. */
436 PCOREAUDIOSTREAM pStream;
437 /** Source stream description. */
438 AudioStreamBasicDescription asbdSrc;
439 /** Destination stream description. */
440 AudioStreamBasicDescription asbdDst;
441 /** Pointer to native buffer list used for rendering the source audio data into. */
442 AudioBufferList *pBufLstSrc;
443 /** Total packet conversion count. */
444 UInt32 uPacketCnt;
445 /** Current packet conversion index. */
446 UInt32 uPacketIdx;
447 /** Error count, for limiting the logging. */
448 UInt32 cErrors;
449} COREAUDIOCONVCBCTX, *PCOREAUDIOCONVCBCTX;
450
451/**
452 * Structure for keeping the input stream specifics.
453 */
454typedef struct COREAUDIOSTREAMIN
455{
456#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
457 /** The audio converter if necessary. NULL if no converter is being used. */
458 AudioConverterRef ConverterRef;
459 /** Callback context for the audio converter. */
460 COREAUDIOCONVCBCTX convCbCtx;
461#endif
462 /** The ratio between the device & the stream sample rate. */
463 Float64 sampleRatio;
464} COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN;
465
466/**
467 * Structure for keeping the output stream specifics.
468 */
469typedef struct COREAUDIOSTREAMOUT
470{
471 /** Nothing here yet. */
472} COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT;
473
474/**
475 * Structure for maintaining a Core Audio stream.
476 */
477typedef struct COREAUDIOSTREAM
478{
479 /** Host input stream.
480 * Note: Always must come first in this structure! */
481 PDMAUDIOSTREAM Stream;
482 /** Note: This *always* must come first! */
483 union
484 {
485 COREAUDIOSTREAMIN In;
486 COREAUDIOSTREAMOUT Out;
487 };
488 /** List node for the device's stream list. */
489 RTLISTNODE Node;
490 /** Pointer to driver instance this stream is bound to. */
491 PDRVHOSTCOREAUDIO pDrv;
492 /** The stream's direction. */
493 PDMAUDIODIR enmDir;
494#ifdef VBOX_WITH_AUDIO_CA_QUEUES
495 /** The stream's thread handle for maintaining the audio queue. */
496 RTTHREAD hThread;
497 /** Flag indicating to start a stream's data processing. */
498 bool fRun;
499 /** Whether the stream is in a running (active) state or not.
500 * For playback streams this means that audio data can be (or is being) played,
501 * for capturing streams this means that audio data is being captured (if available). */
502 bool fIsRunning;
503 /** Thread shutdown indicator. */
504 bool fShutdown;
505 /** Critical section for serializing access between thread + callbacks. */
506 RTCRITSECT CritSect;
507 /** The actual audio queue being used. */
508 AudioQueueRef audioQueue;
509 /** The audio buffers which are used with the above audio queue. */
510 AudioQueueBufferRef audioBuffer[3];
511 AudioStreamBasicDescription asbdAcq;
512#endif
513 /** The audio unit for this stream. */
514 COREAUDIOUNIT Unit;
515 /** Initialization status tracker. Used when some of the device parameters
516 * or the device itself is changed during the runtime. */
517 volatile uint32_t enmStatus;
518 /** An internal ring buffer for transferring data from/to the rendering callbacks. */
519 PRTCIRCBUF pCircBuf;
520} COREAUDIOSTREAM, *PCOREAUDIOSTREAM;
521
522static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev);
523#ifndef VBOX_WITH_AUDIO_CALLBACKS
524static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev);
525#endif
526static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream);
527
528#ifndef VBOX_WITH_AUDIO_CA_QUEUES
529static int coreAudioStreamInitIn(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq);
530static int coreAudioStreamInitOut(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq);
531#endif
532
533static int coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd);
534
535static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev);
536static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev);
537static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, bool fIsInput, PDRVHOSTCOREAUDIO pDrv);
538
539static OSStatus coreAudioDevPropChgCb(AudioObjectID propertyID, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void *pvUser);
540
541#ifndef VBOX_WITH_AUDIO_CA_QUEUES
542static OSStatus coreAudioPlaybackCb(void *pvUser, AudioUnitRenderActionFlags *pActionFlags, const AudioTimeStamp *pAudioTS, UInt32 uBusID, UInt32 cFrames, AudioBufferList* pBufData);
543#else
544static void coreAudioInputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer, const AudioTimeStamp *pAudioTS, UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc);
545static void coreAudioOutputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer);
546#endif
547
548#ifndef VBOX_WITH_AUDIO_CA_QUEUES
549/**
550 * Returns whether an assigned audio unit to a given stream is running or not.
551 *
552 * @return True if audio unit is running, false if not.
553 * @param pStream Audio stream to check.
554 */
555static bool coreAudioUnitIsRunning(PCOREAUDIOSTREAM pStream)
556{
557 AssertPtrReturn(pStream, false);
558
559 UInt32 uFlag = 0;
560 UInt32 uSize = sizeof(uFlag);
561 OSStatus err = AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global,
562 0, &uFlag, &uSize);
563 if (err != kAudioHardwareNoError)
564 LogRel(("CoreAudio: Could not determine whether the audio unit is running (%RI32)\n", err));
565
566 Log3Func(("%s -> %RU32\n", pStream->enmDir == PDMAUDIODIR_IN ? "Input" : "Output", uFlag));
567
568 return (uFlag >= 1);
569}
570#endif /* VBOX_WITH_AUDIO_CA_QUEUES */
571
572#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
573/**
574 * Initializes a conversion callback context.
575 *
576 * @return IPRT status code.
577 * @param pConvCbCtx Conversion callback context to initialize.
578 * @param pStream Pointer to stream to use.
579 * @param pASBDSrc Input (source) stream description to use.
580 * @param pASBDDst Output (destination) stream description to use.
581 */
582static int coreAudioInitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx, PCOREAUDIOSTREAM pStream,
583 AudioStreamBasicDescription *pASBDSrc, AudioStreamBasicDescription *pASBDDst)
584{
585 AssertPtrReturn(pConvCbCtx, VERR_INVALID_POINTER);
586 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
587 AssertPtrReturn(pASBDSrc, VERR_INVALID_POINTER);
588 AssertPtrReturn(pASBDDst, VERR_INVALID_POINTER);
589
590#ifdef DEBUG
591 coreAudioPrintASBD("CbCtx: Src", pASBDSrc);
592 coreAudioPrintASBD("CbCtx: Dst", pASBDDst);
593#endif
594
595 pConvCbCtx->pStream = pStream;
596
597 memcpy(&pConvCbCtx->asbdSrc, pASBDSrc, sizeof(AudioStreamBasicDescription));
598 memcpy(&pConvCbCtx->asbdDst, pASBDDst, sizeof(AudioStreamBasicDescription));
599
600 pConvCbCtx->pBufLstSrc = NULL;
601 pConvCbCtx->cErrors = 0;
602
603 return VINF_SUCCESS;
604}
605
606
607/**
608 * Uninitializes a conversion callback context.
609 *
610 * @return IPRT status code.
611 * @param pConvCbCtx Conversion callback context to uninitialize.
612 */
613static void coreAudioUninitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx)
614{
615 AssertPtrReturnVoid(pConvCbCtx);
616
617 pConvCbCtx->pStream = NULL;
618
619 RT_ZERO(pConvCbCtx->asbdSrc);
620 RT_ZERO(pConvCbCtx->asbdDst);
621
622 pConvCbCtx->pBufLstSrc = NULL;
623 pConvCbCtx->cErrors = 0;
624}
625#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
626
627
628/**
629 * Does a (re-)enumeration of the host's playback + recording devices.
630 *
631 * @return IPRT status code.
632 * @param pThis Host audio driver instance.
633 * @param enmUsage Which devices to enumerate.
634 * @param pDevEnm Where to store the enumerated devices.
635 */
636static int coreAudioDevicesEnumerate(PDRVHOSTCOREAUDIO pThis, PDMAUDIODIR enmUsage, PPDMAUDIODEVICEENUM pDevEnm)
637{
638 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
639 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER);
640
641 int rc = VINF_SUCCESS;
642
643 do
644 {
645 AudioDeviceID defaultDeviceID = kAudioDeviceUnknown;
646
647 /* Fetch the default audio device currently in use. */
648 AudioObjectPropertyAddress propAdrDefaultDev = { enmUsage == PDMAUDIODIR_IN
649 ? kAudioHardwarePropertyDefaultInputDevice
650 : kAudioHardwarePropertyDefaultOutputDevice,
651 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
652 UInt32 uSize = sizeof(defaultDeviceID);
653 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdrDefaultDev, 0, NULL, &uSize, &defaultDeviceID);
654 if (err != noErr)
655 {
656 LogRel(("CoreAudio: Unable to determine default %s device (%RI32)\n",
657 enmUsage == PDMAUDIODIR_IN ? "capturing" : "playback", err));
658 return VERR_NOT_FOUND;
659 }
660
661 if (defaultDeviceID == kAudioDeviceUnknown)
662 {
663 LogFunc(("No default %s device found\n", enmUsage == PDMAUDIODIR_IN ? "capturing" : "playback"));
664 /* Keep going. */
665 }
666
667 AudioObjectPropertyAddress propAdrDevList = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
668 kAudioObjectPropertyElementMaster };
669
670 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize);
671 if (err != kAudioHardwareNoError)
672 break;
673
674 AudioDeviceID *pDevIDs = (AudioDeviceID *)alloca(uSize);
675 if (pDevIDs == NULL)
676 break;
677
678 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize, pDevIDs);
679 if (err != kAudioHardwareNoError)
680 break;
681
682 rc = DrvAudioHlpDeviceEnumInit(pDevEnm);
683 if (RT_FAILURE(rc))
684 break;
685
686 UInt16 cDevices = uSize / sizeof(AudioDeviceID);
687
688 PPDMAUDIODEVICE pDev = NULL;
689 for (UInt16 i = 0; i < cDevices; i++)
690 {
691 if (pDev) /* Some (skipped) device to clean up first? */
692 DrvAudioHlpDeviceFree(pDev);
693
694 pDev = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA));
695 if (!pDev)
696 {
697 rc = VERR_NO_MEMORY;
698 break;
699 }
700
701 /* Set usage. */
702 pDev->enmUsage = enmUsage;
703
704 /* Init backend-specific device data. */
705 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pDev->pvData;
706 AssertPtr(pDevData);
707 coreAudioDeviceDataInit(pDevData, pDevIDs[i], enmUsage == PDMAUDIODIR_IN, pThis);
708
709 /* Check if the device is valid. */
710 AudioDeviceID curDevID = pDevData->deviceID;
711
712 /* Is the device the default device? */
713 if (curDevID == defaultDeviceID)
714 pDev->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT;
715
716 AudioObjectPropertyAddress propAddrCfg = { kAudioDevicePropertyStreamConfiguration,
717 enmUsage == PDMAUDIODIR_IN
718 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
719 kAudioObjectPropertyElementMaster };
720
721 err = AudioObjectGetPropertyDataSize(curDevID, &propAddrCfg, 0, NULL, &uSize);
722 if (err != noErr)
723 continue;
724
725 AudioBufferList *pBufList = (AudioBufferList *)RTMemAlloc(uSize);
726 if (!pBufList)
727 continue;
728
729 err = AudioObjectGetPropertyData(curDevID, &propAddrCfg, 0, NULL, &uSize, pBufList);
730 if (err == noErr)
731 {
732 for (UInt32 a = 0; a < pBufList->mNumberBuffers; a++)
733 {
734 if (enmUsage == PDMAUDIODIR_IN)
735 pDev->cMaxInputChannels += pBufList->mBuffers[a].mNumberChannels;
736 else if (enmUsage == PDMAUDIODIR_OUT)
737 pDev->cMaxOutputChannels += pBufList->mBuffers[a].mNumberChannels;
738 }
739 }
740
741 if (pBufList)
742 {
743 RTMemFree(pBufList);
744 pBufList = NULL;
745 }
746
747 /* Check if the device is valid, e.g. has any input/output channels according to its usage. */
748 if ( enmUsage == PDMAUDIODIR_IN
749 && !pDev->cMaxInputChannels)
750 {
751 continue;
752 }
753 else if ( enmUsage == PDMAUDIODIR_OUT
754 && !pDev->cMaxOutputChannels)
755 {
756 continue;
757 }
758
759 /* Resolve the device's name. */
760 AudioObjectPropertyAddress propAddrName = { kAudioObjectPropertyName,
761 enmUsage == PDMAUDIODIR_IN
762 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
763 kAudioObjectPropertyElementMaster };
764 uSize = sizeof(CFStringRef);
765 CFStringRef pcfstrName = NULL;
766
767 err = AudioObjectGetPropertyData(curDevID, &propAddrName, 0, NULL, &uSize, &pcfstrName);
768 if (err != kAudioHardwareNoError)
769 continue;
770
771 CFIndex uMax = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pcfstrName), kCFStringEncodingUTF8) + 1;
772 if (uMax)
773 {
774 char *pszName = (char *)RTStrAlloc(uMax);
775 if ( pszName
776 && CFStringGetCString(pcfstrName, pszName, uMax, kCFStringEncodingUTF8))
777 {
778 RTStrPrintf(pDev->szName, sizeof(pDev->szName), "%s", pszName);
779 }
780
781 LogFunc(("Device '%s': %RU32\n", pszName, curDevID));
782
783 if (pszName)
784 {
785 RTStrFree(pszName);
786 pszName = NULL;
787 }
788 }
789
790 CFRelease(pcfstrName);
791
792 /* Check if the device is alive for the intended usage. */
793 AudioObjectPropertyAddress propAddrAlive = { kAudioDevicePropertyDeviceIsAlive,
794 enmUsage == PDMAUDIODIR_IN
795 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
796 kAudioObjectPropertyElementMaster };
797
798 UInt32 uAlive = 0;
799 uSize = sizeof(uAlive);
800
801 err = AudioObjectGetPropertyData(curDevID, &propAddrAlive, 0, NULL, &uSize, &uAlive);
802 if ( (err == noErr)
803 && !uAlive)
804 {
805 pDev->fFlags |= PDMAUDIODEV_FLAGS_DEAD;
806 }
807
808 /* Check if the device is being hogged by someone else. */
809 AudioObjectPropertyAddress propAddrHogged = { kAudioDevicePropertyHogMode,
810 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
811
812 pid_t pid = 0;
813 uSize = sizeof(pid);
814
815 err = AudioObjectGetPropertyData(curDevID, &propAddrHogged, 0, NULL, &uSize, &pid);
816 if ( (err == noErr)
817 && (pid != -1))
818 {
819 pDev->fFlags |= PDMAUDIODEV_FLAGS_LOCKED;
820 }
821
822 /* Add the device to the enumeration. */
823 rc = DrvAudioHlpDeviceEnumAdd(pDevEnm, pDev);
824 if (RT_FAILURE(rc))
825 break;
826
827 /* NULL device pointer because it's now part of the device enumeration. */
828 pDev = NULL;
829 }
830
831 if (RT_FAILURE(rc))
832 {
833 DrvAudioHlpDeviceFree(pDev);
834 pDev = NULL;
835 }
836
837 } while (0);
838
839 if (RT_SUCCESS(rc))
840 {
841#ifdef DEBUG
842 LogFunc(("Devices for pDevEnm=%p, enmUsage=%RU32:\n", pDevEnm, enmUsage));
843 DrvAudioHlpDeviceEnumPrint("Core Audio", pDevEnm);
844#endif
845 }
846 else
847 DrvAudioHlpDeviceEnumFree(pDevEnm);
848
849 LogFlowFuncLeaveRC(rc);
850 return rc;
851}
852
853
854/**
855 * Checks if an audio device with a specific device ID is in the given device
856 * enumeration or not.
857 *
858 * @retval true if the node is the last element in the list.
859 * @retval false otherwise.
860 *
861 * @param pEnmSrc Device enumeration to search device ID in.
862 * @param deviceID Device ID to search.
863 */
864bool coreAudioDevicesHasDevice(PPDMAUDIODEVICEENUM pEnmSrc, AudioDeviceID deviceID)
865{
866 PPDMAUDIODEVICE pDevSrc;
867 RTListForEach(&pEnmSrc->lstDevices, pDevSrc, PDMAUDIODEVICE, Node)
868 {
869 PCOREAUDIODEVICEDATA pDevSrcData = (PCOREAUDIODEVICEDATA)pDevSrc->pvData;
870 AssertPtr(pDevSrcData);
871
872 if (pDevSrcData->deviceID == deviceID)
873 return true;
874 }
875
876 return false;
877}
878
879
880/**
881 * Enumerates all host devices and builds a final device enumeration list, consisting
882 * of (duplex) input and output devices.
883 *
884 * @return IPRT status code.
885 * @param pThis Host audio driver instance.
886 * @param pEnmDst Where to store the device enumeration list.
887 */
888int coreAudioDevicesEnumerateAll(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICEENUM pEnmDst)
889{
890 PDMAUDIODEVICEENUM devEnmIn;
891 int rc = coreAudioDevicesEnumerate(pThis, PDMAUDIODIR_IN, &devEnmIn);
892 if (RT_SUCCESS(rc))
893 {
894 PDMAUDIODEVICEENUM devEnmOut;
895 rc = coreAudioDevicesEnumerate(pThis, PDMAUDIODIR_OUT, &devEnmOut);
896 if (RT_SUCCESS(rc))
897 {
898 /*
899 * Build up the final device enumeration, based on the input and output device lists
900 * just enumerated.
901 *
902 * Also make sure to handle duplex devices, that is, devices which act as input and output
903 * at the same time.
904 */
905
906 rc = DrvAudioHlpDeviceEnumInit(pEnmDst);
907 if (RT_SUCCESS(rc))
908 {
909 PPDMAUDIODEVICE pDevSrcIn;
910 RTListForEach(&devEnmIn.lstDevices, pDevSrcIn, PDMAUDIODEVICE, Node)
911 {
912 PCOREAUDIODEVICEDATA pDevSrcInData = (PCOREAUDIODEVICEDATA)pDevSrcIn->pvData;
913 AssertPtr(pDevSrcInData);
914
915 PPDMAUDIODEVICE pDevDst = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA));
916 if (!pDevDst)
917 {
918 rc = VERR_NO_MEMORY;
919 break;
920 }
921
922 PCOREAUDIODEVICEDATA pDevDstData = (PCOREAUDIODEVICEDATA)pDevDst->pvData;
923 AssertPtr(pDevDstData);
924 coreAudioDeviceDataInit(pDevDstData, pDevSrcInData->deviceID, true /* fIsInput */, pThis);
925
926 RTStrCopy(pDevDst->szName, sizeof(pDevDst->szName), pDevSrcIn->szName);
927
928 pDevDst->enmUsage = PDMAUDIODIR_IN; /* Input device by default (simplex). */
929 pDevDst->cMaxInputChannels = pDevSrcIn->cMaxInputChannels;
930
931 /* Handle flags. */
932 if (pDevSrcIn->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
933 pDevDst->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT;
934 /** @todo Handle hot plugging? */
935
936 /*
937 * Now search through the list of all found output devices and check if we found
938 * an output device with the same device ID as the currently handled input device.
939 *
940 * If found, this means we have to treat that device as a duplex device then.
941 */
942 PPDMAUDIODEVICE pDevSrcOut;
943 RTListForEach(&devEnmOut.lstDevices, pDevSrcOut, PDMAUDIODEVICE, Node)
944 {
945 PCOREAUDIODEVICEDATA pDevSrcOutData = (PCOREAUDIODEVICEDATA)pDevSrcOut->pvData;
946 AssertPtr(pDevSrcOutData);
947
948 if (pDevSrcInData->deviceID == pDevSrcOutData->deviceID)
949 {
950 pDevDst->enmUsage = PDMAUDIODIR_ANY;
951 pDevDst->cMaxOutputChannels = pDevSrcOut->cMaxOutputChannels;
952 break;
953 }
954 }
955
956 if (RT_SUCCESS(rc))
957 {
958 rc = DrvAudioHlpDeviceEnumAdd(pEnmDst, pDevDst);
959 }
960 else
961 {
962 DrvAudioHlpDeviceFree(pDevDst);
963 pDevDst = NULL;
964 }
965 }
966
967 if (RT_SUCCESS(rc))
968 {
969 /*
970 * As a last step, add all remaining output devices which have not been handled in the loop above,
971 * that is, all output devices which operate in simplex mode.
972 */
973 PPDMAUDIODEVICE pDevSrcOut;
974 RTListForEach(&devEnmOut.lstDevices, pDevSrcOut, PDMAUDIODEVICE, Node)
975 {
976 PCOREAUDIODEVICEDATA pDevSrcOutData = (PCOREAUDIODEVICEDATA)pDevSrcOut->pvData;
977 AssertPtr(pDevSrcOutData);
978
979 if (coreAudioDevicesHasDevice(pEnmDst, pDevSrcOutData->deviceID))
980 continue; /* Already in our list, skip. */
981
982 PPDMAUDIODEVICE pDevDst = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA));
983 if (!pDevDst)
984 {
985 rc = VERR_NO_MEMORY;
986 break;
987 }
988
989 PCOREAUDIODEVICEDATA pDevDstData = (PCOREAUDIODEVICEDATA)pDevDst->pvData;
990 AssertPtr(pDevDstData);
991 coreAudioDeviceDataInit(pDevDstData, pDevSrcOutData->deviceID, false /* fIsInput */, pThis);
992
993 RTStrCopy(pDevDst->szName, sizeof(pDevDst->szName), pDevSrcOut->szName);
994
995 pDevDst->enmUsage = PDMAUDIODIR_OUT;
996 pDevDst->cMaxOutputChannels = pDevSrcOut->cMaxOutputChannels;
997
998 pDevDstData->deviceID = pDevSrcOutData->deviceID;
999
1000 /* Handle flags. */
1001 if (pDevSrcOut->fFlags & PDMAUDIODEV_FLAGS_DEFAULT)
1002 pDevDst->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT;
1003 /** @todo Handle hot plugging? */
1004
1005 rc = DrvAudioHlpDeviceEnumAdd(pEnmDst, pDevDst);
1006 if (RT_FAILURE(rc))
1007 {
1008 DrvAudioHlpDeviceFree(pDevDst);
1009 break;
1010 }
1011 }
1012 }
1013
1014 if (RT_FAILURE(rc))
1015 DrvAudioHlpDeviceEnumFree(pEnmDst);
1016 }
1017
1018 DrvAudioHlpDeviceEnumFree(&devEnmOut);
1019 }
1020
1021 DrvAudioHlpDeviceEnumFree(&devEnmIn);
1022 }
1023
1024#ifdef DEBUG
1025 if (RT_SUCCESS(rc))
1026 DrvAudioHlpDeviceEnumPrint("Core Audio (Final)", pEnmDst);
1027#endif
1028
1029 LogFlowFuncLeaveRC(rc);
1030 return rc;
1031}
1032
1033
1034#if 0
1035static int coreAudioDeviceInit(PPDMAUDIODEVICE pDev, PDRVHOSTCOREAUDIO pDrv, PDMAUDIODIR enmUsage, AudioDeviceID deviceID)
1036{
1037 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
1038
1039 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1040 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1041
1042 /* Init common parameters. */
1043 pDev->enmUsage = enmUsage;
1044
1045 /* Init Core Audio-specifics. */
1046 pData->deviceID = deviceID;
1047 pData->pDrv = pDrv;
1048
1049 RTListInit(&pData->lstStreams);
1050
1051 return VINF_SUCCESS;
1052}
1053#endif
1054
1055
1056/**
1057 * Initializes a Core Audio-specific device data structure.
1058 *
1059 * @returns IPRT status code.
1060 * @param pDevData Device data structure to initialize.
1061 * @param deviceID Core Audio device ID to assign this structure to.
1062 * @param fIsInput Whether this is an input device or not.
1063 * @param pDrv Driver instance to use.
1064 */
1065static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, bool fIsInput, PDRVHOSTCOREAUDIO pDrv)
1066{
1067 AssertPtrReturnVoid(pDevData);
1068 AssertPtrReturnVoid(pDrv);
1069
1070 pDevData->deviceID = deviceID;
1071 pDevData->pDrv = pDrv;
1072
1073 /* Get the device UUID. */
1074 AudioObjectPropertyAddress propAdrDevUUID = { kAudioDevicePropertyDeviceUID,
1075 fIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
1076 kAudioObjectPropertyElementMaster };
1077 UInt32 uSize = sizeof(pDevData->UUID);
1078 OSStatus err = AudioObjectGetPropertyData(pDevData->deviceID, &propAdrDevUUID, 0, NULL, &uSize, &pDevData->UUID);
1079 if (err != noErr)
1080 LogRel(("CoreAudio: Failed to retrieve device UUID for device %RU32 (%RI32)\n", deviceID, err));
1081
1082 RTListInit(&pDevData->lstStreams);
1083}
1084
1085
1086/**
1087 * Propagates an audio device status to all its connected Core Audio streams.
1088 *
1089 * @return IPRT status code.
1090 * @param pDev Audio device to propagate status for.
1091 * @param enmSts Status to propagate.
1092 */
1093static int coreAudioDevicePropagateStatus(PPDMAUDIODEVICE pDev, COREAUDIOSTATUS enmSts)
1094{
1095 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
1096
1097 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1098 AssertPtrReturn(pDevData, VERR_INVALID_POINTER);
1099
1100 /* Sanity. */
1101 AssertPtr(pDevData->pDrv);
1102
1103 LogFlowFunc(("pDev=%p, pDevData=%p, enmSts=%RU32\n", pDev, pDevData, enmSts));
1104
1105 PCOREAUDIOSTREAM pCAStream;
1106 RTListForEach(&pDevData->lstStreams, pCAStream, COREAUDIOSTREAM, Node)
1107 {
1108 LogFlowFunc(("pCAStream=%p\n", pCAStream));
1109
1110 /* We move the reinitialization to the next output event.
1111 * This make sure this thread isn't blocked and the
1112 * reinitialization is done when necessary only. */
1113 ASMAtomicXchgU32(&pCAStream->enmStatus, enmSts);
1114 }
1115
1116 return VINF_SUCCESS;
1117}
1118
1119
1120static DECLCALLBACK(OSStatus) coreAudioDeviceStateChangedCb(AudioObjectID propertyID,
1121 UInt32 nAddresses,
1122 const AudioObjectPropertyAddress properties[],
1123 void *pvUser)
1124{
1125 RT_NOREF(propertyID, nAddresses, properties);
1126
1127 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser));
1128
1129 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)pvUser;
1130 AssertPtr(pDev);
1131
1132 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1133 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1134
1135 PDRVHOSTCOREAUDIO pThis = pData->pDrv;
1136 AssertPtr(pThis);
1137
1138 int rc2 = RTCritSectEnter(&pThis->CritSect);
1139 AssertRC(rc2);
1140
1141 UInt32 uAlive = 1;
1142 UInt32 uSize = sizeof(UInt32);
1143
1144 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
1145 kAudioObjectPropertyElementMaster };
1146
1147 AudioDeviceID deviceID = pData->deviceID;
1148
1149 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uAlive);
1150
1151 bool fIsDead = false;
1152
1153 if (err == kAudioHardwareBadDeviceError)
1154 fIsDead = true; /* Unplugged. */
1155 else if ((err == kAudioHardwareNoError) && (!RT_BOOL(uAlive)))
1156 fIsDead = true; /* Something else happened. */
1157
1158 if (fIsDead)
1159 {
1160 LogRel2(("CoreAudio: Device '%s' stopped functioning\n", pDev->szName));
1161
1162 /* Mark device as dead. */
1163 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_UNINIT);
1164 AssertRC(rc2);
1165 }
1166
1167 rc2 = RTCritSectLeave(&pThis->CritSect);
1168 AssertRC(rc2);
1169
1170 return noErr;
1171}
1172
1173/* Callback for getting notified when the default recording/playback device has been changed. */
1174static DECLCALLBACK(OSStatus) coreAudioDefaultDeviceChangedCb(AudioObjectID propertyID,
1175 UInt32 nAddresses,
1176 const AudioObjectPropertyAddress properties[],
1177 void *pvUser)
1178{
1179 RT_NOREF(propertyID, nAddresses);
1180
1181 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser));
1182
1183 PDRVHOSTCOREAUDIO pThis = (PDRVHOSTCOREAUDIO)pvUser;
1184 AssertPtr(pThis);
1185
1186 int rc2 = RTCritSectEnter(&pThis->CritSect);
1187 AssertRC(rc2);
1188
1189 for (UInt32 idxAddress = 0; idxAddress < nAddresses; idxAddress++)
1190 {
1191 PPDMAUDIODEVICE pDev = NULL;
1192
1193 /*
1194 * Check if the default input / output device has been changed.
1195 */
1196 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress];
1197 switch (pProperty->mSelector)
1198 {
1199 case kAudioHardwarePropertyDefaultInputDevice:
1200 LogFlowFunc(("kAudioHardwarePropertyDefaultInputDevice\n"));
1201 pDev = pThis->pDefaultDevIn;
1202 break;
1203
1204 case kAudioHardwarePropertyDefaultOutputDevice:
1205 LogFlowFunc(("kAudioHardwarePropertyDefaultOutputDevice\n"));
1206 pDev = pThis->pDefaultDevOut;
1207 break;
1208
1209 default:
1210 /* Skip others. */
1211 break;
1212 }
1213
1214 LogFlowFunc(("pDev=%p\n", pDev));
1215
1216#ifndef VBOX_WITH_AUDIO_CALLBACKS
1217 if (pDev)
1218 {
1219 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1220 AssertPtr(pData);
1221
1222 /* This listener is called on every change of the hardware
1223 * device. So check if the default device has really changed. */
1224 UInt32 uSize = sizeof(AudioDeviceID);
1225 UInt32 uResp = 0;
1226
1227 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp);
1228 if (err == noErr)
1229 {
1230 if (pData->deviceID != uResp) /* Has the device ID changed? */
1231 {
1232 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT);
1233 AssertRC(rc2);
1234 }
1235 }
1236 }
1237#endif /* VBOX_WITH_AUDIO_CALLBACKS */
1238 }
1239
1240#ifdef VBOX_WITH_AUDIO_CALLBACKS
1241 PFNPDMHOSTAUDIOCALLBACK pfnCallback = pThis->pfnCallback;
1242#endif
1243
1244 /* Make sure to leave the critical section before calling the callback. */
1245 rc2 = RTCritSectLeave(&pThis->CritSect);
1246 AssertRC(rc2);
1247
1248#ifdef VBOX_WITH_AUDIO_CALLBACKS
1249 if (pfnCallback)
1250 /* Ignore rc */ pfnCallback(pThis->pDrvIns, PDMAUDIOCBTYPE_DEVICES_CHANGED, NULL, 0);
1251#endif
1252
1253 return noErr;
1254}
1255
1256#ifndef VBOX_WITH_AUDIO_CALLBACKS
1257/**
1258 * Re-initializes a Core Audio stream with a specific audio device and stream configuration.
1259 *
1260 * @return IPRT status code.
1261 * @param pThis Driver instance.
1262 * @param pCAStream Audio stream to re-initialize.
1263 * @param pDev Audio device to use for re-initialization.
1264 * @param pCfg Stream configuration to use for re-initialization.
1265 */
1266static int coreAudioStreamReinitEx(PDRVHOSTCOREAUDIO pThis,
1267 PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev, PPDMAUDIOSTREAMCFG pCfg)
1268{
1269 LogFunc(("pCAStream=%p\n", pCAStream));
1270
1271 int rc = coreAudioStreamUninit(pCAStream);
1272 if (RT_SUCCESS(rc))
1273 {
1274 rc = coreAudioStreamInit(pCAStream, pThis, pDev);
1275 if (RT_SUCCESS(rc))
1276 {
1277#ifdef VBOX_WITH_AUDIO_CA_QUEUES
1278 rc = coreAudioStreamInitQueue(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */);
1279#else
1280 if (pCAStream->enmDir == PDMAUDIODIR_IN)
1281 rc = coreAudioStreamInitIn (pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */);
1282 else
1283 rc = coreAudioStreamInitOut(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */);
1284#endif
1285 if (RT_SUCCESS(rc))
1286 rc = coreAudioStreamControl(pCAStream->pDrv, pCAStream, PDMAUDIOSTREAMCMD_ENABLE);
1287
1288 if (RT_FAILURE(rc))
1289 {
1290 int rc2 = coreAudioStreamUninit(pCAStream);
1291 AssertRC(rc2);
1292 }
1293 }
1294 }
1295
1296 if (RT_FAILURE(rc))
1297 LogRel(("CoreAudio: Unable to re-init stream: %Rrc\n", rc));
1298
1299 return rc;
1300}
1301
1302/**
1303 * Re-initializes a Core Audio stream with a specific audio device.
1304 *
1305 * @return IPRT status code.
1306 * @param pThis Driver instance.
1307 * @param pCAStream Audio stream to re-initialize.
1308 * @param pDev Audio device to use for re-initialization.
1309 */
1310static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev)
1311{
1312 int rc = coreAudioStreamUninit(pCAStream);
1313 if (RT_SUCCESS(rc))
1314 {
1315 /* Use the acquired stream configuration from the former initialization to
1316 * re-initialize the stream. */
1317 PDMAUDIOSTREAMCFG CfgAcq;
1318 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, &CfgAcq);
1319 if (RT_SUCCESS(rc))
1320 rc = coreAudioStreamReinitEx(pThis, pCAStream, pDev, &CfgAcq);
1321 }
1322
1323 return rc;
1324}
1325#endif /* VBOX_WITH_AUDIO_CALLBACKS */
1326
1327#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
1328/* Callback to convert audio input data from one format to another. */
1329static DECLCALLBACK(OSStatus) coreAudioConverterCb(AudioConverterRef inAudioConverter,
1330 UInt32 *ioNumberDataPackets,
1331 AudioBufferList *ioData,
1332 AudioStreamPacketDescription **ppASPD,
1333 void *pvUser)
1334{
1335 RT_NOREF(inAudioConverter);
1336
1337 AssertPtrReturn(ioNumberDataPackets, caConverterEOFDErr);
1338 AssertPtrReturn(ioData, caConverterEOFDErr);
1339
1340 PCOREAUDIOCONVCBCTX pConvCbCtx = (PCOREAUDIOCONVCBCTX)pvUser;
1341 AssertPtr(pConvCbCtx);
1342
1343 /* Initialize values. */
1344 ioData->mBuffers[0].mNumberChannels = 0;
1345 ioData->mBuffers[0].mDataByteSize = 0;
1346 ioData->mBuffers[0].mData = NULL;
1347
1348 if (ppASPD)
1349 {
1350 Log3Func(("Handling packet description not implemented\n"));
1351 }
1352 else
1353 {
1354 /** @todo Check converter ID? */
1355
1356 /** @todo Handled non-interleaved data by going through the full buffer list,
1357 * not only through the first buffer like we do now. */
1358 Log3Func(("ioNumberDataPackets=%RU32\n", *ioNumberDataPackets));
1359
1360 UInt32 cNumberDataPackets = *ioNumberDataPackets;
1361 Assert(pConvCbCtx->uPacketIdx + cNumberDataPackets <= pConvCbCtx->uPacketCnt);
1362
1363 if (cNumberDataPackets)
1364 {
1365 AssertPtr(pConvCbCtx->pBufLstSrc);
1366 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers == 1); /* Only one buffer for the source supported atm. */
1367
1368 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc;
1369 AudioBuffer *pSrcBuf = &pConvCbCtx->pBufLstSrc->mBuffers[0];
1370
1371 size_t cbOff = pConvCbCtx->uPacketIdx * pSrcASBD->mBytesPerPacket;
1372
1373 cNumberDataPackets = RT_MIN((pSrcBuf->mDataByteSize - cbOff) / pSrcASBD->mBytesPerPacket,
1374 cNumberDataPackets);
1375
1376 void *pvAvail = (uint8_t *)pSrcBuf->mData + cbOff;
1377 size_t cbAvail = RT_MIN(pSrcBuf->mDataByteSize - cbOff, cNumberDataPackets * pSrcASBD->mBytesPerPacket);
1378
1379 Log3Func(("cNumberDataPackets=%RU32, cbOff=%zu, cbAvail=%zu\n", cNumberDataPackets, cbOff, cbAvail));
1380
1381 /* Set input data for the converter to use.
1382 * Note: For VBR (Variable Bit Rates) or interleaved data handling we need multiple buffers here. */
1383 ioData->mNumberBuffers = 1;
1384
1385 ioData->mBuffers[0].mNumberChannels = pSrcBuf->mNumberChannels;
1386 ioData->mBuffers[0].mDataByteSize = cbAvail;
1387 ioData->mBuffers[0].mData = pvAvail;
1388
1389#ifdef DEBUG_DUMP_PCM_DATA
1390 RTFILE fh;
1391 int rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-converter-cb-input.pcm",
1392 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1393 if (RT_SUCCESS(rc))
1394 {
1395 RTFileWrite(fh, pvAvail, cbAvail, NULL);
1396 RTFileClose(fh);
1397 }
1398 else
1399 AssertFailed();
1400#endif
1401 pConvCbCtx->uPacketIdx += cNumberDataPackets;
1402 Assert(pConvCbCtx->uPacketIdx <= pConvCbCtx->uPacketCnt);
1403
1404 *ioNumberDataPackets = cNumberDataPackets;
1405 }
1406 }
1407
1408 Log3Func(("%RU32 / %RU32 -> ioNumberDataPackets=%RU32\n",
1409 pConvCbCtx->uPacketIdx, pConvCbCtx->uPacketCnt, *ioNumberDataPackets));
1410
1411 return noErr;
1412}
1413#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
1414
1415#ifndef VBOX_WITH_AUDIO_CA_QUEUES
1416/* Callback to feed audio input buffer. */
1417static DECLCALLBACK(OSStatus) coreAudioCaptureCb(void *pvUser,
1418 AudioUnitRenderActionFlags *pActionFlags,
1419 const AudioTimeStamp *pAudioTS,
1420 UInt32 uBusID,
1421 UInt32 cFrames,
1422 AudioBufferList *pBufData)
1423{
1424 RT_NOREF(uBusID, pBufData);
1425
1426 /* If nothing is pending return immediately. */
1427 if (cFrames == 0)
1428 return noErr;
1429
1430 PCOREAUDIOSTREAM pStream = (PCOREAUDIOSTREAM)pvUser;
1431
1432 /* Sanity. */
1433 AssertPtr(pStream);
1434 AssertPtr(pStream->pDrv);
1435 Assert (pStream->enmDir == PDMAUDIODIR_IN);
1436 AssertPtr(pStream->Unit.pDevice);
1437
1438 if (ASMAtomicReadU32(&pStream->enmStatus) != COREAUDIOSTATUS_INIT)
1439 return noErr;
1440
1441 OSStatus err = noErr;
1442 int rc = VINF_SUCCESS;
1443
1444 AudioBufferList srcBufLst;
1445 RT_ZERO(srcBufLst);
1446
1447 do
1448 {
1449#ifdef DEBUG
1450 AudioStreamBasicDescription asbdIn;
1451 UInt32 uSize = sizeof(asbdIn);
1452 AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1453 1, &asbdIn, &uSize);
1454 coreAudioPrintASBD("DevIn", &asbdIn);
1455
1456 AudioStreamBasicDescription asbdOut;
1457 uSize = sizeof(asbdOut);
1458 AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1459 1, &asbdOut, &uSize);
1460 coreAudioPrintASBD("DevOut", &asbdOut);
1461#endif
1462
1463#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
1464 /* Are we using a converter? */
1465 if (pStreamIn->ConverterRef)
1466 {
1467 PCOREAUDIOCONVCBCTX pConvCbCtx = &pStreamIn->convCbCtx;
1468
1469 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc;
1470 AudioStreamBasicDescription *pDstASBD = &pConvCbCtx->asbdDst;
1471# ifdef DEBUG
1472 coreAudioPrintASBD("Src", pSrcASBD);
1473 coreAudioPrintASBD("Dst", pDstASBD);
1474# endif
1475 /* Initialize source list buffer. */
1476 srcBufLst.mNumberBuffers = 1;
1477
1478 /* Initialize the first buffer. */
1479 srcBufLst.mBuffers[0].mNumberChannels = pSrcASBD->mChannelsPerFrame;
1480 srcBufLst.mBuffers[0].mDataByteSize = pSrcASBD->mBytesPerFrame * cFrames;
1481 srcBufLst.mBuffers[0].mData = RTMemAllocZ(srcBufLst.mBuffers[0].mDataByteSize);
1482 if (!srcBufLst.mBuffers[0].mData)
1483 {
1484 rc = VERR_NO_MEMORY;
1485 break;
1486 }
1487
1488 #if 0
1489 /* Initialize the second buffer. */
1490 srcBufLst.mBuffers[1].mNumberChannels = 1; //pSrcASBD->mChannelsPerFrame;
1491 srcBufLst.mBuffers[1].mDataByteSize = pSrcASBD->mBytesPerFrame * cFrames;
1492 srcBufLst.mBuffers[1].mData = RTMemAllocZ(srcBufLst.mBuffers[1].mDataByteSize);
1493 if (!srcBufLst.mBuffers[1].mData)
1494 {
1495 rc = VERR_NO_MEMORY;
1496 break;
1497 }
1498 #endif
1499
1500 /* Set the buffer list for our callback context. */
1501 pConvCbCtx->pBufLstSrc = &srcBufLst;
1502
1503 /* Sanity. */
1504 AssertPtr(pConvCbCtx->pBufLstSrc);
1505 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers >= 1);
1506
1507 /* Get the first buffer. */
1508 AudioBuffer *pSrcBuf = &srcBufLst.mBuffers[0];
1509
1510 Log3Func(("pSrcBuf->mDataByteSize1=%RU32\n", pSrcBuf->mDataByteSize));
1511
1512 /* First, render the source data as usual. */
1513 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames,
1514 &srcBufLst);
1515 if (err != noErr)
1516 {
1517 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */
1518 {
1519 LogRel2(("CoreAudio: Failed rendering converted audio input data (%RI32:%c%c%c%c)\n", err,
1520 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
1521 pConvCbCtx->cErrors++;
1522 }
1523
1524 rc = VERR_IO_GEN_FAILURE;
1525 break;
1526 }
1527
1528 /* Note: pSrcBuf->mDataByteSize can have changed after calling AudioUnitRender above! */
1529 Log3Func(("pSrcBuf->mDataByteSize2=%RU32\n", pSrcBuf->mDataByteSize));
1530
1531# ifdef DEBUG_DUMP_PCM_DATA
1532 RTFILE fh;
1533 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-src.pcm",
1534 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1535 if (RT_SUCCESS(rc))
1536 {
1537 RTFileWrite(fh, pSrcBuf->mData, pSrcBuf->mDataByteSize, NULL);
1538 RTFileClose(fh);
1539 }
1540 else
1541 AssertFailed();
1542# endif
1543 AudioBufferList dstBufLst;
1544 RT_ZERO(dstBufLst);
1545
1546 dstBufLst.mNumberBuffers = 1; /* We only use one buffer at once. */
1547
1548 AudioBuffer *pDstBuf = &dstBufLst.mBuffers[0];
1549
1550 UInt32 cbDst = pDstASBD->mBytesPerFrame * cFrames;
1551 void *pvDst = RTMemAlloc(cbDst);
1552 if (!pvDst)
1553 {
1554 rc = VERR_NO_MEMORY;
1555 break;
1556 }
1557
1558 pDstBuf->mDataByteSize = cbDst;
1559 pDstBuf->mData = pvDst;
1560
1561 AudioConverterReset(pStreamIn->ConverterRef);
1562
1563 Log3Func(("cbSrcBufSize=%RU32 (BPF=%RU32), cbDstBufSize=%RU32 (BPF=%RU32)\n",
1564 pSrcBuf->mDataByteSize, pSrcASBD->mBytesPerFrame,
1565 pDstBuf->mDataByteSize, pDstASBD->mBytesPerFrame));
1566
1567 if (pSrcASBD->mSampleRate == pDstASBD->mSampleRate)
1568 {
1569 err = AudioConverterConvertBuffer(pStreamIn->ConverterRef,
1570 #if 0
1571 cbT1, pvT1, &cbT2, pvT2);
1572 #else
1573 pSrcBuf->mDataByteSize, pSrcBuf->mData /* Input */,
1574 &cbDst, pvDst /* Output */);
1575 #endif
1576 if (err != noErr)
1577 {
1578 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */
1579 {
1580 LogRel2(("CoreAudio: Failed to convert audio input data (%RI32:%c%c%c%c)\n", err,
1581 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
1582 pConvCbCtx->cErrors++;
1583 }
1584
1585 rc = VERR_IO_GEN_FAILURE;
1586 break;
1587 }
1588
1589# ifdef DEBUG_DUMP_PCM_DATA
1590 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-conv-dst.pcm",
1591 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1592 if (RT_SUCCESS(rc))
1593 {
1594 RTFileWrite(fh, pvDst, cbDst, NULL);
1595 RTFileClose(fh);
1596 }
1597 else
1598 AssertFailed();
1599# endif
1600 }
1601 else /* Invoke FillComplexBuffer because the sample rate is different. */
1602 {
1603 pConvCbCtx->uPacketCnt = pSrcBuf->mDataByteSize / pSrcASBD->mBytesPerPacket;
1604 pConvCbCtx->uPacketIdx = 0;
1605
1606 Log3Func(("cFrames=%RU32 (%RU32 dest frames per packet) -> %RU32 input frames\n",
1607 cFrames, pDstASBD->mFramesPerPacket, pConvCbCtx->uPacketCnt));
1608
1609 UInt32 cPacketsToWrite = pDstBuf->mDataByteSize / pDstASBD->mBytesPerPacket;
1610 Assert(cPacketsToWrite);
1611
1612 UInt32 cPacketsWritten = 0;
1613
1614 Log3Func(("cPacketsToWrite=%RU32\n", cPacketsToWrite));
1615
1616 while (cPacketsToWrite)
1617 {
1618 UInt32 cPacketsIO = cPacketsToWrite;
1619
1620 Log3Func(("cPacketsIO=%RU32 (In)\n", cPacketsIO));
1621
1622 err = AudioConverterFillComplexBuffer(pStreamIn->ConverterRef,
1623 coreAudioConverterCb, pConvCbCtx /* pvData */,
1624 &cPacketsIO, &dstBufLst, NULL);
1625 if (err != noErr)
1626 {
1627 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */
1628 {
1629 LogRel2(("CoreAudio: Failed to convert complex audio data (%RI32:%c%c%c%c)\n", err,
1630 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
1631 pConvCbCtx->cErrors++;
1632 }
1633
1634 rc = VERR_IO_GEN_FAILURE;
1635 break;
1636 }
1637
1638 Log3Func(("cPacketsIO=%RU32 (Out)\n", cPacketsIO));
1639
1640 cPacketsWritten = cPacketsIO;
1641
1642 Assert(cPacketsToWrite >= cPacketsWritten);
1643 cPacketsToWrite -= cPacketsWritten;
1644
1645 size_t cbPacketsWritten = cPacketsWritten * pDstASBD->mBytesPerPacket;
1646 Log3Func(("%RU32 packets written (%zu bytes), err=%RI32\n", cPacketsWritten, cbPacketsWritten, err));
1647
1648 if (!cPacketsWritten)
1649 break;
1650
1651# ifdef DEBUG_DUMP_PCM_DATA
1652 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-complex-dst.pcm",
1653 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1654 if (RT_SUCCESS(rc))
1655 {
1656 RTFileWrite(fh, pvDst, cbDst, NULL);
1657 RTFileClose(fh);
1658 }
1659 else
1660 AssertFailed();
1661# endif
1662 size_t cbFree = RTCircBufFree(pStreamIn->pCircBuf);
1663 if (cbFree < cbDst)
1664 {
1665 LogRel2(("CoreAudio: Recording is lagging behind (%zu bytes available but only %zu bytes free)\n",
1666 cbDst, cbFree));
1667 break;
1668 }
1669
1670 size_t cbDstChunk;
1671 void *puDst;
1672 RTCircBufAcquireWriteBlock(pStreamIn->pCircBuf, cbDst, (void **)&puDst, &cbDstChunk);
1673
1674 if (cbDstChunk)
1675 memcpy(puDst, pvDst, cbDstChunk);
1676
1677 RTCircBufReleaseWriteBlock(pStreamIn->pCircBuf, cbDstChunk);
1678 }
1679 }
1680
1681 if (pvDst)
1682 {
1683 RTMemFree(pvDst);
1684 pvDst = NULL;
1685 }
1686 }
1687 else /* No converter being used. */
1688 {
1689#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
1690
1691 AudioStreamBasicDescription *pStreamFmt = &pStream->Unit.streamFmt;
1692
1693 AssertBreakStmt(pStreamFmt->mChannelsPerFrame >= 1, rc = VERR_INVALID_PARAMETER);
1694 AssertBreakStmt(pStreamFmt->mBytesPerFrame >= 1, rc = VERR_INVALID_PARAMETER);
1695
1696 srcBufLst.mNumberBuffers = 1;
1697
1698 AudioBuffer *pSrcBuf = &srcBufLst.mBuffers[0];
1699
1700 pSrcBuf->mNumberChannels = pStreamFmt->mChannelsPerFrame;
1701 pSrcBuf->mDataByteSize = pStreamFmt->mBytesPerFrame * cFrames;
1702 pSrcBuf->mData = RTMemAlloc(pSrcBuf->mDataByteSize);
1703 if (!pSrcBuf->mData)
1704 {
1705 rc = VERR_NO_MEMORY;
1706 break;
1707 }
1708
1709 err = AudioUnitRender(pStream->Unit.audioUnit, pActionFlags, pAudioTS, 1 /* Input bus */, cFrames, &srcBufLst);
1710 if (err != noErr)
1711 {
1712 LogRel2(("CoreAudio: Failed rendering non-converted audio input data (%RI32)\n", err));
1713 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
1714 break;
1715 }
1716
1717#ifdef DEBUG_DUMP_PCM_DATA
1718 RTFILE fh;
1719 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-src.pcm",
1720 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1721 if (RT_SUCCESS(rc))
1722 {
1723 RTFileWrite(fh, pSrcBuf->mData, pSrcBuf->mDataByteSize, NULL);
1724 RTFileClose(fh);
1725 }
1726 else
1727 AssertFailed();
1728#endif
1729 PRTCIRCBUF pCircBuf = pStream->pCircBuf;
1730
1731 const uint32_t cbDataSize = pSrcBuf->mDataByteSize;
1732 const size_t cbBufFree = RTCircBufFree(pCircBuf);
1733 size_t cbAvail = RT_MIN(cbDataSize, cbBufFree);
1734
1735 Log3Func(("cbDataSize=%RU32, cbBufFree=%zu, cbAvail=%zu\n", cbDataSize, cbBufFree, cbAvail));
1736
1737 /* Iterate as long as data is available. */
1738 uint8_t *puDst = NULL;
1739 uint32_t cbWrittenTotal = 0;
1740 while (cbAvail)
1741 {
1742 /* Try to acquire the necessary space from the ring buffer. */
1743 size_t cbToWrite = 0;
1744 RTCircBufAcquireWriteBlock(pCircBuf, cbAvail, (void **)&puDst, &cbToWrite);
1745 if (!cbToWrite)
1746 break;
1747
1748 /* Copy the data from the Core Audio buffer to the ring buffer. */
1749 memcpy(puDst, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite);
1750
1751#ifdef DEBUG_DUMP_PCM_DATA
1752 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-dst.pcm",
1753 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1754 if (RT_SUCCESS(rc))
1755 {
1756 RTFileWrite(fh, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite, NULL);
1757 RTFileClose(fh);
1758 }
1759 else
1760 AssertFailed();
1761#endif
1762 /* Release the ring buffer, so the main thread could start reading this data. */
1763 RTCircBufReleaseWriteBlock(pCircBuf, cbToWrite);
1764
1765 cbWrittenTotal += cbToWrite;
1766
1767 Assert(cbAvail >= cbToWrite);
1768 cbAvail -= cbToWrite;
1769 }
1770
1771 Log3Func(("cbWrittenTotal=%RU32, cbLeft=%zu\n", cbWrittenTotal, cbAvail));
1772
1773#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
1774 }
1775#endif
1776
1777 } while (0);
1778
1779 for (UInt32 i = 0; i < srcBufLst.mNumberBuffers; i++)
1780 {
1781 if (srcBufLst.mBuffers[i].mData)
1782 {
1783 RTMemFree(srcBufLst.mBuffers[i].mData);
1784 srcBufLst.mBuffers[i].mData = NULL;
1785 }
1786 }
1787
1788 return err;
1789}
1790#endif /* !VBOX_WITH_AUDIO_CA_QUEUES */
1791
1792/**
1793 * Initializes a Core Audio stream.
1794 *
1795 * @return IPRT status code.
1796 * @param pThis Driver instance.
1797 * @param pCAStream Stream to initialize.
1798 * @param pDev Audio device to use for this stream.
1799 */
1800static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev)
1801{
1802 AssertPtrReturn(pCAStream, VERR_INVALID_POINTER);
1803 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1804 AssertPtrReturn(pDev, VERR_INVALID_POINTER);
1805
1806 Assert(pCAStream->Unit.pDevice == NULL); /* Make sure no device is assigned yet. */
1807 AssertPtr(pDev->pvData);
1808 Assert(pDev->cbData == sizeof(COREAUDIODEVICEDATA));
1809
1810#ifdef DEBUG
1811 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
1812 LogFunc(("pCAStream=%p, pDev=%p ('%s', ID=%RU32)\n", pCAStream, pDev, pDev->szName, pData->deviceID));
1813#endif
1814
1815 pCAStream->Unit.pDevice = pDev;
1816 pCAStream->pDrv = pThis;
1817
1818 return VINF_SUCCESS;
1819}
1820
1821# define CA_BREAK_STMT(stmt) \
1822 stmt; \
1823 break;
1824
1825#ifdef VBOX_WITH_AUDIO_CA_QUEUES
1826/**
1827 * Thread for a Core Audio stream's audio queue handling.
1828 * This thread is required per audio queue to pump data to/from the Core Audio stream and
1829 * handling its callbacks.
1830 *
1831 * @returns IPRT status code.
1832 * @param hThreadSelf Thread handle.
1833 * @param pvUser User argument.
1834 */
1835static DECLCALLBACK(int) coreAudioQueueThread(RTTHREAD hThreadSelf, void *pvUser)
1836{
1837 RT_NOREF(hThreadSelf);
1838
1839 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser;
1840 AssertPtr(pCAStream);
1841
1842 LogFunc(("Starting pCAStream=%p\n", pCAStream));
1843
1844 /*
1845 * Create audio queue.
1846 */
1847 OSStatus err;
1848 if (pCAStream->enmDir == PDMAUDIODIR_IN)
1849 err = AudioQueueNewInput(&pCAStream->asbdAcq, coreAudioInputQueueCb, pCAStream /* pvData */,
1850 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue);
1851 else
1852 err = AudioQueueNewOutput(&pCAStream->asbdAcq, coreAudioOutputQueueCb, pCAStream /* pvData */,
1853 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue);
1854
1855 if (err != noErr)
1856 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1857
1858 /*
1859 * Assign device to queue.
1860 */
1861 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pCAStream->Unit.pDevice->pvData;
1862 AssertPtr(pData);
1863
1864 UInt32 uSize = sizeof(pData->UUID);
1865 err = AudioQueueSetProperty(pCAStream->audioQueue, kAudioQueueProperty_CurrentDevice, &pData->UUID, uSize);
1866 if (err != noErr)
1867 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1868
1869 const size_t cbBufSize = _4K; /** @todo Make this configurable! */
1870
1871 /*
1872 * Allocate audio buffers.
1873 */
1874 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++)
1875 {
1876 err = AudioQueueAllocateBuffer(pCAStream->audioQueue, cbBufSize, &pCAStream->audioBuffer[i]);
1877 if (err != noErr)
1878 break;
1879 }
1880
1881 if (err != noErr)
1882 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
1883
1884 /* Signal the main thread before entering the main loop. */
1885 RTThreadUserSignal(RTThreadSelf());
1886
1887 /*
1888 * Enter the main loop.
1889 */
1890 const bool fIn = pCAStream->enmDir == PDMAUDIODIR_IN;
1891
1892 while (!ASMAtomicReadBool(&pCAStream->fShutdown))
1893 {
1894 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
1895 }
1896
1897 /*
1898 * Cleanup.
1899 */
1900 if (fIn)
1901 {
1902 AudioQueueStop(pCAStream->audioQueue, 1);
1903 }
1904 else
1905 {
1906 AudioQueueStop(pCAStream->audioQueue, 0);
1907 }
1908
1909 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++)
1910 {
1911 if (pCAStream->audioBuffer[i])
1912 AudioQueueFreeBuffer(pCAStream->audioQueue, pCAStream->audioBuffer[i]);
1913 }
1914
1915 AudioQueueDispose(pCAStream->audioQueue, 1);
1916
1917 LogFunc(("Ended pCAStream=%p\n", pCAStream));
1918 return VINF_SUCCESS;
1919}
1920
1921/**
1922 * Processes input data of an audio queue buffer and stores it into a Core Audio stream.
1923 *
1924 * @returns IPRT status code.
1925 * @param pCAStream Core Audio stream to store input data into.
1926 * @param audioBuffer Audio buffer to process input data from.
1927 */
1928int coreAudioInputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer)
1929{
1930 size_t cbWritten = 0;
1931
1932 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf;
1933 AssertPtr(pCircBuf);
1934
1935 UInt8 *pvSrc = (UInt8 *)audioBuffer->mAudioData;
1936 UInt8 *pvDst = NULL;
1937
1938 size_t cbToWrite = audioBuffer->mAudioDataByteSize;
1939 size_t cbLeft = cbToWrite;
1940
1941 while (cbLeft)
1942 {
1943 /* Try to acquire the necessary block from the ring buffer. */
1944 RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, (void **)&pvDst, &cbToWrite);
1945
1946 if (!cbToWrite)
1947 break;
1948
1949 /* Copy the data from our ring buffer to the core audio buffer. */
1950 memcpy((UInt8 *)pvDst + cbWritten, pvSrc + cbWritten, cbToWrite);
1951
1952 /* Release the read buffer, so it could be used for new data. */
1953 RTCircBufReleaseWriteBlock(pCircBuf, cbToWrite);
1954
1955 cbWritten += cbToWrite;
1956
1957 Assert(cbLeft >= cbToWrite);
1958 cbLeft -= cbToWrite;
1959 }
1960
1961 audioBuffer->mAudioDataByteSize = cbWritten;
1962
1963 Log3Func(("pCAStream=%p, cbWritten=%zu\n", pCAStream, cbWritten));
1964
1965 return VINF_SUCCESS;
1966}
1967
1968/**
1969 * Input audio queue callback. Called whenever input data from the audio queue becomes available.
1970 *
1971 * @param pvUser User argument.
1972 * @param audioQueue Audio queue to process input data from.
1973 * @param audioBuffer Audio buffer to process input data from. Must be part of audio queue.
1974 * @param pAudioTS Audio timestamp.
1975 * @param cPacketDesc Number of packet descriptors.
1976 * @param paPacketDesc Array of packet descriptors.
1977 */
1978static DECLCALLBACK(void) coreAudioInputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer,
1979 const AudioTimeStamp *pAudioTS,
1980 UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc)
1981{
1982 RT_NOREF(audioQueue, pAudioTS, cPacketDesc, paPacketDesc);
1983
1984 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser;
1985 AssertPtr(pCAStream);
1986
1987 int rc = RTCritSectEnter(&pCAStream->CritSect);
1988 AssertRC(rc);
1989
1990 rc = coreAudioInputQueueProcBuffer(pCAStream, audioBuffer);
1991 if (RT_SUCCESS(rc))
1992 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL);
1993
1994 rc = RTCritSectLeave(&pCAStream->CritSect);
1995 AssertRC(rc);
1996}
1997
1998/**
1999 * Processes output data of a Core Audio stream into an audio queue buffer.
2000 *
2001 * @returns IPRT status code.
2002 * @param pCAStream Core Audio stream to process output data for.
2003 * @param audioBuffer Audio buffer to store data into.
2004 */
2005int coreAudioOutputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer)
2006{
2007 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf;
2008 AssertPtr(pCircBuf);
2009
2010 size_t cbRead = 0;
2011
2012 UInt8 *pvSrc = NULL;
2013 UInt8 *pvDst = (UInt8 *)audioBuffer->mAudioData;
2014
2015 size_t cbToRead = RT_MIN(RTCircBufUsed(pCircBuf), audioBuffer->mAudioDataBytesCapacity);
2016 size_t cbLeft = cbToRead;
2017
2018 while (cbLeft)
2019 {
2020 /* Try to acquire the necessary block from the ring buffer. */
2021 RTCircBufAcquireReadBlock(pCircBuf, cbLeft, (void **)&pvSrc, &cbToRead);
2022
2023 /* Break if nothing is used anymore. */
2024 if (!cbToRead)
2025 break;
2026
2027 /* Copy the data from our ring buffer to the core audio buffer. */
2028 memcpy((UInt8 *)pvDst + cbRead, pvSrc, cbToRead);
2029
2030 /* Release the read buffer, so it could be used for new data. */
2031 RTCircBufReleaseReadBlock(pCircBuf, cbToRead);
2032
2033 /* Move offset. */
2034 cbRead += cbToRead;
2035 Assert(cbRead <= audioBuffer->mAudioDataBytesCapacity);
2036
2037 Assert(cbToRead <= cbLeft);
2038 cbLeft -= cbToRead;
2039 }
2040
2041 audioBuffer->mAudioDataByteSize = cbRead;
2042
2043 if (audioBuffer->mAudioDataByteSize < audioBuffer->mAudioDataBytesCapacity)
2044 {
2045 RT_BZERO((UInt8 *)audioBuffer->mAudioData + audioBuffer->mAudioDataByteSize,
2046 audioBuffer->mAudioDataBytesCapacity - audioBuffer->mAudioDataByteSize);
2047
2048 audioBuffer->mAudioDataByteSize = audioBuffer->mAudioDataBytesCapacity;
2049 }
2050
2051 Log3Func(("pCAStream=%p, cbCapacity=%RU32, cbRead=%zu\n",
2052 pCAStream, audioBuffer->mAudioDataBytesCapacity, cbRead));
2053
2054 return VINF_SUCCESS;
2055}
2056
2057/**
2058 * Output audio queue callback. Called whenever an audio queue is ready to process more output data.
2059 *
2060 * @param pvUser User argument.
2061 * @param audioQueue Audio queue to process output data for.
2062 * @param audioBuffer Audio buffer to store output data in. Must be part of audio queue.
2063 */
2064static DECLCALLBACK(void) coreAudioOutputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer)
2065{
2066 RT_NOREF(audioQueue);
2067
2068 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser;
2069 AssertPtr(pCAStream);
2070
2071 int rc = RTCritSectEnter(&pCAStream->CritSect);
2072 AssertRC(rc);
2073
2074 rc = coreAudioOutputQueueProcBuffer(pCAStream, audioBuffer);
2075 if (RT_SUCCESS(rc))
2076 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL);
2077
2078 rc = RTCritSectLeave(&pCAStream->CritSect);
2079 AssertRC(rc);
2080}
2081
2082/**
2083 * Invalidates a Core Audio stream's audio queue.
2084 *
2085 * @returns IPRT status code.
2086 * @param pCAStream Core Audio stream to invalidate its queue for.
2087 */
2088static int coreAudioStreamInvalidateQueue(PCOREAUDIOSTREAM pCAStream)
2089{
2090 int rc = VINF_SUCCESS;
2091
2092 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++)
2093 {
2094 AudioQueueBufferRef pBuf = pCAStream->audioBuffer[i];
2095
2096 if (pCAStream->enmDir == PDMAUDIODIR_IN)
2097 {
2098 }
2099 else if (pCAStream->enmDir == PDMAUDIODIR_OUT)
2100 {
2101 int rc2 = coreAudioOutputQueueProcBuffer(pCAStream, pBuf);
2102 if ( RT_SUCCESS(rc2)
2103 && pBuf->mAudioDataByteSize)
2104 {
2105 AudioQueueEnqueueBuffer(pCAStream->audioQueue, pBuf, 0, NULL);
2106 }
2107
2108 if (RT_SUCCESS(rc))
2109 rc = rc2;
2110 }
2111 else
2112 AssertFailed();
2113 }
2114
2115 return rc;
2116}
2117
2118/**
2119 * Initializes a Core Audio stream's audio queue.
2120 *
2121 * @returns IPRT status code.
2122 * @param pCAStream Core Audio stream to initialize audio queue for.
2123 * @param pCfgReq Requested stream configuration.
2124 * @param pCfgAcq Acquired stream configuration on success.
2125 */
2126static int coreAudioStreamInitQueue(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2127{
2128 LogFunc(("pCAStream=%p, pCfgReq=%p, pCfgAcq=%p\n", pCAStream, pCfgReq, pCfgAcq));
2129
2130 /* No device assigned? Bail out early. */
2131 if (pCAStream->Unit.pDevice == NULL)
2132 return VERR_NOT_AVAILABLE;
2133
2134 pCAStream->enmDir = pCfgReq->enmDir;
2135
2136 const bool fIn = pCAStream->enmDir == PDMAUDIODIR_IN;
2137
2138 /* Create the recording device's out format based on our required audio settings. */
2139 int rc = coreAudioStreamCfgToASBD(pCfgReq, &pCAStream->asbdAcq);
2140 if (RT_FAILURE(rc))
2141 {
2142 LogRel(("CoreAudio: Failed to convert requested %s format to native format (%Rrc)\n",
2143 fIn ? "input" : "output", rc));
2144 return rc;
2145 }
2146
2147 coreAudioPrintASBD( fIn
2148 ? "Capturing queue format"
2149 : "Playback queue format", &pCAStream->asbdAcq);
2150
2151 rc = RTCircBufCreate(&pCAStream->pCircBuf, 8096 << 1 /*pHstStrmIn->Props.cShift*/); /** @todo FIX THIS !!! */
2152 if (RT_FAILURE(rc))
2153 return rc;
2154
2155 /*
2156 * Start the thread.
2157 */
2158 rc = RTThreadCreate(&pCAStream->hThread, coreAudioQueueThread,
2159 pCAStream /* pvUser */, 0 /* Default stack size */,
2160 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CAQUEUE");
2161 if (RT_SUCCESS(rc))
2162 rc = RTThreadUserWait(pCAStream->hThread, 10 * 1000 /* 10s timeout */);
2163
2164 LogFunc(("Returning %Rrc\n", rc));
2165 return rc;
2166}
2167
2168/**
2169 * Unitializes a Core Audio stream's audio queue.
2170 *
2171 * @returns IPRT status code.
2172 * @param pCAStream Core Audio stream to unitialize audio queue for.
2173 */
2174static int coreAudioStreamUninitQueue(PCOREAUDIOSTREAM pCAStream)
2175{
2176 LogFunc(("pCAStream=%p\n", pCAStream));
2177
2178 int rc;
2179
2180 if (pCAStream->hThread != NIL_RTTHREAD)
2181 {
2182 LogFunc(("Waiting for thread ...\n"));
2183
2184 ASMAtomicXchgBool(&pCAStream->fShutdown, true);
2185
2186 int rcThread;
2187 rc = RTThreadWait(pCAStream->hThread, 30 * 1000, &rcThread);
2188 if (RT_FAILURE(rc))
2189 return rc;
2190
2191 RT_NOREF(rcThread);
2192 LogFunc(("Thread stopped with %Rrc\n", rcThread));
2193
2194 pCAStream->hThread = NIL_RTTHREAD;
2195 }
2196
2197 if (pCAStream->pCircBuf)
2198 {
2199 RTCircBufDestroy(pCAStream->pCircBuf);
2200 pCAStream->pCircBuf = NULL;
2201 }
2202
2203 LogFunc(("Returning\n"));
2204 return VINF_SUCCESS;
2205}
2206
2207/**
2208 * Unitializes a Core Audio stream.
2209 *
2210 * @returns IPRT status code.
2211 * @param pCAStream Core Audio stream to uninitialize.
2212 */
2213static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream)
2214{
2215 LogFunc(("pCAStream=%p\n", pCAStream));
2216
2217 int rc = coreAudioStreamUninitQueue(pCAStream);
2218 return rc;
2219}
2220#else /* !VBOX_WITH_AUDIO_CA_QUEUES */
2221/** @todo Eventually split up this function, as this already is huge! */
2222static int coreAudioStreamInitIn(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2223{
2224 int rc = VINF_SUCCESS;
2225
2226 UInt32 cSamples = 0;
2227
2228 OSStatus err = noErr;
2229
2230 LogFunc(("pCAStream=%p, pCfgReq=%p, pCfgAcq=%p\n", pCAStream, pCfgReq, pCfgAcq));
2231
2232 PPDMAUDIODEVICE pDev = pCAStream->Unit.pDevice;
2233 AssertPtr(pDev);
2234
2235 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
2236 AssertPtr(pData);
2237
2238 AudioDeviceID deviceID = pData->deviceID;
2239 LogFunc(("deviceID=%RU32\n", deviceID));
2240 Assert(deviceID != kAudioDeviceUnknown);
2241
2242 do
2243 {
2244 /* Get the default frames buffer size, so that we can setup our internal buffers. */
2245 UInt32 cFrames;
2246 UInt32 uSize = sizeof(cFrames);
2247
2248 AudioObjectPropertyAddress propAdr;
2249 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
2250 propAdr.mScope = kAudioDevicePropertyScopeInput;
2251 propAdr.mElement = kAudioObjectPropertyElementMaster;
2252 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
2253 if (err != noErr)
2254 {
2255 /* Can happen if no recording device is available by default. Happens on some Macs,
2256 * so don't log this by default to not scare people. */
2257 LogRel2(("CoreAudio: Failed to determine frame buffer size of the audio recording device (%RI32)\n", err));
2258 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2259 }
2260
2261 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
2262 err = coreAudioSetFrameBufferSize(deviceID, true /* fInput */, cFrames, &cFrames);
2263 if (err != noErr)
2264 {
2265 LogRel(("CoreAudio: Failed to set frame buffer size for the audio recording device (%RI32)\n", err));
2266 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2267 }
2268
2269 LogFlowFunc(("cFrames=%RU32\n", cFrames));
2270
2271 /* Try to find the default HAL output component. */
2272 AudioComponentDescription cd;
2273
2274 RT_ZERO(cd);
2275 cd.componentType = kAudioUnitType_Output;
2276 cd.componentSubType = kAudioUnitSubType_HALOutput;
2277 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
2278
2279 AudioComponent cp = AudioComponentFindNext(NULL, &cd);
2280 if (cp == 0)
2281 {
2282 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
2283 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2284 }
2285
2286 /* Open the default HAL output component. */
2287 err = AudioComponentInstanceNew(cp, &pCAStream->Unit.audioUnit);
2288 if (err != noErr)
2289 {
2290 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
2291 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2292 }
2293
2294 /* Switch the I/O mode for input to on. */
2295 UInt32 uFlag = 1;
2296 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
2297 1, &uFlag, sizeof(uFlag));
2298 if (err != noErr)
2299 {
2300 LogRel(("CoreAudio: Failed to disable input I/O mode for input stream (%RI32)\n", err));
2301 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2302 }
2303
2304 /* Switch the I/O mode for output to off. This is important, as this is a pure input stream. */
2305 uFlag = 0;
2306 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
2307 0, &uFlag, sizeof(uFlag));
2308 if (err != noErr)
2309 {
2310 LogRel(("CoreAudio: Failed to disable output I/O mode for input stream (%RI32)\n", err));
2311 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2312 }
2313
2314 /* Set the default audio recording device as the device for the new AudioUnit. */
2315 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
2316 0, &deviceID, sizeof(deviceID));
2317 if (err != noErr)
2318 {
2319 LogRel(("CoreAudio: Failed to set current input device (%RI32)\n", err));
2320 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2321 }
2322
2323 /*
2324 * CoreAudio will inform us on a second thread for new incoming audio data.
2325 * Therefore register a callback function which will process the new data.
2326 */
2327 AURenderCallbackStruct cb;
2328 RT_ZERO(cb);
2329 cb.inputProc = coreAudioCaptureCb;
2330 cb.inputProcRefCon = pCAStream;
2331
2332 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,
2333 0, &cb, sizeof(cb));
2334 if (err != noErr)
2335 {
2336 LogRel(("CoreAudio: Failed to register input callback (%RI32)\n", err));
2337 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2338 }
2339
2340 /* Create the recording device's out format based on our required audio settings. */
2341 AudioStreamBasicDescription reqFmt;
2342 rc = coreAudioStreamCfgToASBD(pCfgReq, &reqFmt);
2343 if (RT_FAILURE(rc))
2344 {
2345 LogRel(("CoreAudio: Failed to convert requested input format to native format (%Rrc)\n", rc));
2346 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2347 }
2348
2349 coreAudioPrintASBD("Requested stream input format", &reqFmt);
2350
2351 /* Fetch the input format of the recording device. */
2352 AudioStreamBasicDescription devInFmt;
2353 RT_ZERO(devInFmt);
2354 uSize = sizeof(devInFmt);
2355 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
2356 1, &devInFmt, &uSize);
2357 if (err != noErr)
2358 {
2359 LogRel(("CoreAudio: Failed to get input device input format (%RI32)\n", err));
2360 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2361 }
2362
2363 coreAudioPrintASBD("Input device in (initial)", &devInFmt);
2364
2365 /* Fetch the output format of the recording device. */
2366 AudioStreamBasicDescription devOutFmt;
2367 RT_ZERO(devOutFmt);
2368 uSize = sizeof(devOutFmt);
2369 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
2370 1, &devOutFmt, &uSize);
2371 if (err != noErr)
2372 {
2373 LogRel(("CoreAudio: Failed to get input device output format (%RI32)\n", err));
2374 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2375 }
2376
2377 coreAudioPrintASBD("Input device out (initial)", &devOutFmt);
2378
2379 /* Set the output format for the input device so that it matches the initial input format.
2380 * This apparently is needed for some picky / buggy USB headsets. */
2381
2382 /*
2383 * The only thing we tweak here is the actual format flags: A lot of USB headsets tend
2384 * to have float PCM data, which we can't handle (yet).
2385 *
2386 * So set this as signed integers in a packed, non-iterleaved format.
2387 */
2388 devInFmt.mFormatFlags = kAudioFormatFlagIsSignedInteger
2389 | kAudioFormatFlagIsPacked;
2390
2391 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
2392 1, &devInFmt, sizeof(devInFmt));
2393 if (err != noErr)
2394 {
2395 LogRel(("CoreAudio: Failed to set new output format for input device (%RI32)\n", err));
2396 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2397 }
2398
2399 /*
2400 * Also set the frame buffer size of the device on our AudioUnit. This
2401 * should make sure that the frames count which we receive in the render
2402 * thread is as we like.
2403 */
2404 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
2405 1, &cFrames, sizeof(cFrames));
2406 if (err != noErr)
2407 {
2408 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err));
2409 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2410 }
2411
2412 /*
2413 * Initialize the new AudioUnit.
2414 */
2415 err = AudioUnitInitialize(pCAStream->Unit.audioUnit);
2416 if (err != noErr)
2417 {
2418 LogRel(("CoreAudio: Failed to initialize input audio device (%RI32)\n", err));
2419 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2420 }
2421
2422 /* Get final input format afer initialization. */
2423 uSize = sizeof(devInFmt);
2424 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
2425 1, &devInFmt, &uSize);
2426 if (err != noErr)
2427 {
2428 LogRel(("CoreAudio: Failed to re-getting input device input format (%RI32)\n", err));
2429 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2430 }
2431
2432 /* Print the device/stream formats again after the audio unit has been initialized.
2433 * Note that the formats could have changed now. */
2434 coreAudioPrintASBD("Input device in (after initialization)", &devInFmt);
2435
2436 /* Get final output format afer initialization. */
2437 uSize = sizeof(devOutFmt);
2438 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
2439 1, &devOutFmt, &uSize);
2440 if (err != noErr)
2441 {
2442 LogRel(("CoreAudio: Failed to re-getting input device output format (%RI32)\n", err));
2443 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2444 }
2445
2446 /* Print the device/stream formats again after the audio unit has been initialized.
2447 * Note that the formats could have changed now. */
2448 coreAudioPrintASBD("Input device out (after initialization)", &devOutFmt);
2449
2450#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
2451
2452 /* If the frequency of the device' output format different from the requested one we
2453 * need a converter. The same counts if the number of channels or the bits per channel are different. */
2454 if ( devOutFmt.mChannelsPerFrame != reqFmt.mChannelsPerFrame
2455 || devOutFmt.mSampleRate != reqFmt.mSampleRate)
2456 {
2457 LogRel2(("CoreAudio: Input converter is active\n"));
2458
2459 err = AudioConverterNew(&devOutFmt /* Input */, &reqFmt /* Output */, &pCAStream->In.ConverterRef);
2460 if (RT_UNLIKELY(err != noErr))
2461 {
2462 LogRel(("CoreAudio: Failed to create the audio converter (%RI32)\n", err));
2463 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2464 }
2465
2466 if ( devOutFmt.mChannelsPerFrame == 1 /* Mono */
2467 && reqFmt.mChannelsPerFrame == 2 /* Stereo */)
2468 {
2469 LogRel2(("CoreAudio: Mono to stereo conversion active\n"));
2470
2471 /*
2472 * If the channel count is different we have to tell this the converter
2473 * and supply a channel mapping. For now we only support mapping
2474 * from mono to stereo. For all other cases the core audio defaults
2475 * are used, which means dropping additional channels in most
2476 * cases.
2477 */
2478 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo. */
2479
2480 err = AudioConverterSetProperty(pCAStream->In.ConverterRef, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
2481 if (err != noErr)
2482 {
2483 LogRel(("CoreAudio: Failed to set channel mapping (mono -> stereo) for the audio input converter (%RI32)\n", err));
2484 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2485 }
2486 }
2487
2488 /* Set sample rate converter quality to maximum. */
2489 uFlag = kAudioConverterQuality_Max;
2490 err = AudioConverterSetProperty(pCAStream->In.ConverterRef, kAudioConverterSampleRateConverterQuality,
2491 sizeof(uFlag), &uFlag);
2492 if (err != noErr)
2493 LogRel2(("CoreAudio: Failed to set input audio converter quality to the maximum (%RI32)\n", err));
2494
2495 uSize = sizeof(UInt32);
2496 UInt32 maxOutputSize;
2497 err = AudioConverterGetProperty(pStreamIn->ConverterRef, kAudioConverterPropertyMaximumOutputPacketSize,
2498 &uSize, &maxOutputSize);
2499 if (RT_UNLIKELY(err != noErr))
2500 {
2501 LogRel(("CoreAudio: Failed to retrieve converter's maximum output size (%RI32)\n", err));
2502 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2503 }
2504
2505 LogFunc(("Maximum converter packet output size is: %RI32\n", maxOutputSize));
2506 }
2507#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
2508
2509#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
2510 if (pCAStream->In.ConverterRef)
2511 {
2512 /* Save the requested format as our stream format. */
2513 memcpy(&pCAStream->Unit.streamFmt, &reqFmt, sizeof(AudioStreamBasicDescription));
2514 }
2515 else
2516 {
2517#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
2518
2519 /* Save the final output format as our stream format. */
2520 memcpy(&pCAStream->Unit.streamFmt, &devOutFmt, sizeof(AudioStreamBasicDescription));
2521
2522#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
2523 }
2524#endif
2525 /*
2526 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
2527 * the frame buffer size set in the previous calls. So finally get the
2528 * frame buffer size after the AudioUnit was initialized.
2529 */
2530 uSize = sizeof(cFrames);
2531 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
2532 0, &cFrames, &uSize);
2533 if (err != noErr)
2534 {
2535 LogRel(("CoreAudio: Failed to get maximum frame buffer size from input audio device (%RI32)\n", err));
2536 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2537 }
2538
2539 /* Calculate the ratio between the device and the stream sample rate. */
2540 pCAStream->In.sampleRatio = devOutFmt.mSampleRate / devInFmt.mSampleRate;
2541
2542 /*
2543 * Make sure that the ring buffer is big enough to hold the recording
2544 * data. Compare the maximum frames per slice value with the frames
2545 * necessary when using the converter where the sample rate could differ.
2546 * The result is always multiplied by the channels per frame to get the
2547 * samples count.
2548 */
2549 cSamples = RT_MAX(cFrames,
2550 (cFrames * reqFmt.mBytesPerFrame * pCAStream->In.sampleRatio)
2551 / reqFmt.mBytesPerFrame)
2552 * reqFmt.mChannelsPerFrame;
2553 if (!cSamples)
2554 {
2555 LogRel(("CoreAudio: Failed to determine samples buffer count input stream\n"));
2556 CA_BREAK_STMT(rc = VERR_INVALID_PARAMETER);
2557 }
2558
2559 rc = RTCircBufCreate(&pCAStream->pCircBuf, cSamples << 1 /*pHstStrmIn->Props.cShift*/); /** @todo FIX THIS !!! */
2560 if (RT_FAILURE(rc))
2561 break;
2562
2563#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
2564 /* Init the converter callback context. */
2565
2566 /* As source, use the input device' output format,
2567 * as destination, use the initially requested format. */
2568 rc = coreAudioInitConvCbCtx(&pCAStream->In.convCbCtx, pCAStream,
2569 &devOutFmt /* Source */, &reqFmt /* Dest */);
2570#endif
2571
2572 } while (0);
2573
2574 if (RT_SUCCESS(rc))
2575 {
2576 pCAStream->enmDir = PDMAUDIODIR_IN;
2577
2578 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, pCfgAcq);
2579 AssertRC(rc);
2580
2581 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT);
2582
2583 pCfgAcq->cSampleBufferSize = cSamples;
2584 }
2585
2586 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
2587 return rc;
2588}
2589
2590/** @todo Eventually split up this function, as this already is huge! */
2591static int coreAudioStreamInitOut(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2592{
2593 int rc = VINF_SUCCESS;
2594 UInt32 cSamples = 0;
2595
2596 OSStatus err = noErr;
2597
2598 LogFunc(("pCAStream=%p, pCfgReq=%p, pCfgAcq=%p\n", pCAStream, pCfgReq, pCfgAcq));
2599
2600 PPDMAUDIODEVICE pDev = pCAStream->Unit.pDevice;
2601 AssertPtr(pDev);
2602
2603 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
2604 AssertPtr(pData);
2605
2606 AudioDeviceID deviceID = pData->deviceID;
2607 LogFunc(("deviceID=%RU32\n", deviceID));
2608 Assert(deviceID != kAudioDeviceUnknown);
2609
2610 do
2611 {
2612 /* Get the default frames buffer size, so that we can setup our internal buffers. */
2613 UInt32 cFrames;
2614 UInt32 uSize = sizeof(cFrames);
2615
2616 AudioObjectPropertyAddress propAdr;
2617 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
2618 propAdr.mScope = kAudioDevicePropertyScopeInput;
2619 propAdr.mElement = kAudioObjectPropertyElementMaster;
2620 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
2621 if (err != noErr)
2622 {
2623 LogRel(("CoreAudio: Failed to determine frame buffer size of the audio playback device (%RI32)\n", err));
2624 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2625 }
2626
2627 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
2628 err = coreAudioSetFrameBufferSize(deviceID, false /* fInput */, cFrames, &cFrames);
2629 if (err != noErr)
2630 {
2631 LogRel(("CoreAudio: Failed to set frame buffer size for the audio playback device (%RI32)\n", err));
2632 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2633 }
2634
2635 /* Try to find the default HAL output component. */
2636 AudioComponentDescription cd;
2637 RT_ZERO(cd);
2638
2639 cd.componentType = kAudioUnitType_Output;
2640 cd.componentSubType = kAudioUnitSubType_HALOutput;
2641 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
2642
2643 AudioComponent cp = AudioComponentFindNext(NULL, &cd);
2644 if (cp == 0)
2645 {
2646 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
2647 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2648 }
2649
2650 /* Open the default HAL output component. */
2651 err = AudioComponentInstanceNew(cp, &pCAStream->Unit.audioUnit);
2652 if (err != noErr)
2653 {
2654 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
2655 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2656 }
2657
2658 /* Switch the I/O mode for output to on. */
2659 UInt32 uFlag = 1;
2660 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
2661 0, &uFlag, sizeof(uFlag));
2662 if (err != noErr)
2663 {
2664 LogRel(("CoreAudio: Failed to disable I/O mode for output stream (%RI32)\n", err));
2665 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2666 }
2667
2668 /* Set the default audio playback device as the device for the new AudioUnit. */
2669 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
2670 0, &deviceID, sizeof(deviceID));
2671 if (err != noErr)
2672 {
2673 LogRel(("CoreAudio: Failed to set current device for output stream (%RI32)\n", err));
2674 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2675 }
2676
2677 /*
2678 * CoreAudio will inform us on a second thread for new incoming audio data.
2679 * Therefor register a callback function which will process the new data.
2680 */
2681 AURenderCallbackStruct cb;
2682 RT_ZERO(cb);
2683 cb.inputProc = coreAudioPlaybackCb;
2684 cb.inputProcRefCon = pCAStream; /* pvUser */
2685
2686 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
2687 0, &cb, sizeof(cb));
2688 if (err != noErr)
2689 {
2690 LogRel(("CoreAudio: Failed to register playback callback (%RI32)\n", err));
2691 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2692 }
2693
2694 AudioStreamBasicDescription reqFmt;
2695 coreAudioStreamCfgToASBD(pCfgReq, &reqFmt);
2696 coreAudioPrintASBD("Requested stream output format", &reqFmt);
2697
2698 /* Fetch the initial input format of the device. */
2699 AudioStreamBasicDescription devInFmt;
2700 uSize = sizeof(devInFmt);
2701 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
2702 0, &devInFmt, &uSize);
2703 if (err != noErr)
2704 {
2705 LogRel(("CoreAudio: Failed to get input format of playback device (%RI32)\n", err));
2706 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2707 }
2708
2709 coreAudioPrintASBD("Output device in (initial)", &devInFmt);
2710
2711 /* Fetch the initial output format of the device. */
2712 AudioStreamBasicDescription devOutFmt;
2713 uSize = sizeof(devOutFmt);
2714 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
2715 0, &devOutFmt, &uSize);
2716 if (err != noErr)
2717 {
2718 LogRel(("CoreAudio: Failed to get output format of playback device (%RI32)\n", err));
2719 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2720 }
2721
2722 coreAudioPrintASBD("Output device out (initial)", &devOutFmt);
2723
2724 /* Set the new input format for the output device. */
2725 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
2726 0, &reqFmt, sizeof(reqFmt));
2727 if (err != noErr)
2728 {
2729 LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err));
2730 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2731 }
2732
2733 /*
2734 * Also set the frame buffer size off the device on our AudioUnit. This
2735 * should make sure that the frames count which we receive in the render
2736 * thread is as we like.
2737 */
2738 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
2739 0, &cFrames, sizeof(cFrames));
2740 if (err != noErr)
2741 {
2742 LogRel(("CoreAudio: Failed to set maximum frame buffer size for output AudioUnit (%RI32)\n", err));
2743 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2744 }
2745
2746 /*
2747 * Initialize the new AudioUnit.
2748 */
2749 err = AudioUnitInitialize(pCAStream->Unit.audioUnit);
2750 if (err != noErr)
2751 {
2752 LogRel(("CoreAudio: Failed to initialize the output audio device (%RI32)\n", err));
2753 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2754 }
2755
2756 /* Fetch the final output format of the device after the audio unit has been initialized. */
2757 uSize = sizeof(devInFmt);
2758 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
2759 0, &devInFmt, &uSize);
2760 if (err != noErr)
2761 {
2762 LogRel(("CoreAudio: Failed re-getting input format of output device (%RI32)\n", err));
2763 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2764 }
2765
2766 coreAudioPrintASBD("Output device in (after initialization)", &devInFmt);
2767
2768 /* Save this final output format as our stream format. */
2769 memcpy(&pCAStream->Unit.streamFmt, &devInFmt, sizeof(AudioStreamBasicDescription));
2770
2771 /*
2772 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
2773 * the frame buffer size set in the previous calls. So finally get the
2774 * frame buffer size after the AudioUnit was initialized.
2775 */
2776 uSize = sizeof(cFrames);
2777 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
2778 0, &cFrames, &uSize);
2779 if (err != noErr)
2780 {
2781 LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err));
2782 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
2783 }
2784
2785 /*
2786 * Make sure that the ring buffer is big enough to hold the recording
2787 * data. Compare the maximum frames per slice value with the frames
2788 * necessary when using the converter where the sample rate could differ.
2789 * The result is always multiplied by the channels per frame to get the
2790 * samples count.
2791 */
2792 cSamples = cFrames * reqFmt.mChannelsPerFrame;
2793 if (!cSamples)
2794 {
2795 LogRel(("CoreAudio: Failed to determine samples buffer count output stream\n"));
2796 CA_BREAK_STMT(rc = VERR_INVALID_PARAMETER);
2797 }
2798
2799 /* Create the internal ring buffer. */
2800 rc = RTCircBufCreate(&pCAStream->pCircBuf, cSamples << 1 /*pHstStrmOut->Props.cShift*/); /** @todo FIX THIS !!! */
2801
2802 } while (0);
2803
2804 if (RT_SUCCESS(rc))
2805 {
2806 pCAStream->enmDir = PDMAUDIODIR_OUT;
2807
2808 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, pCfgAcq);
2809 AssertRC(rc);
2810
2811 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT);
2812
2813 pCfgAcq->cSampleBufferSize = cSamples;
2814 }
2815
2816 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
2817 return rc;
2818}
2819
2820/**
2821 * Uninitializes a Core Audio stream.
2822 *
2823 * @return IPRT status code.
2824 * @param pCAStream Stream to uninitialize.
2825 */
2826static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream)
2827{
2828 OSStatus err = noErr;
2829
2830 if (pCAStream->Unit.audioUnit)
2831 {
2832 err = AudioUnitUninitialize(pCAStream->Unit.audioUnit);
2833 if (err == noErr)
2834 {
2835 err = AudioComponentInstanceDispose(pCAStream->Unit.audioUnit);
2836 if (err == noErr)
2837 pCAStream->Unit.audioUnit = NULL;
2838 }
2839 }
2840
2841 if (err == noErr)
2842 {
2843 if (pCAStream->pCircBuf)
2844 {
2845 RTCircBufDestroy(pCAStream->pCircBuf);
2846 pCAStream->pCircBuf = NULL;
2847 }
2848
2849 pCAStream->enmStatus = COREAUDIOSTATUS_UNINIT;
2850
2851 pCAStream->enmDir = PDMAUDIODIR_UNKNOWN;
2852 pCAStream->pDrv = NULL;
2853
2854 pCAStream->Unit.pDevice = NULL;
2855 RT_ZERO(pCAStream->Unit.streamFmt);
2856
2857 if (pCAStream->enmDir == PDMAUDIODIR_IN)
2858 {
2859#ifdef VBOX_WITH_AUDIO_CA_CONVERTER
2860 if (pCAStream->In.ConverterRef)
2861 {
2862 AudioConverterDispose(pCAStream->In.ConverterRef);
2863 pCAStream->In.ConverterRef = NULL;
2864 }
2865
2866 drvHostCoreAudioUninitConvCbCtx(&pCAStream->In.convCbCtx);
2867#endif
2868 pCAStream->In.sampleRatio = 1;
2869 }
2870 else if (pCAStream->enmDir == PDMAUDIODIR_OUT)
2871 {
2872
2873 }
2874 }
2875 else
2876 LogRel(("CoreAudio: Failed to uninit stream (%RI32)\n", err));
2877
2878 return err == noErr ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge! */
2879}
2880#endif /* !VBOX_WITH_AUDIO_CA_QUEUES */
2881
2882/**
2883 * Registers callbacks for a specific Core Audio device.
2884 *
2885 * @return IPRT status code.
2886 * @param pThis Host audio driver instance.
2887 * @param pDev Audio device to use for the registered callbacks.
2888 */
2889static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev)
2890{
2891 RT_NOREF(pThis);
2892
2893 AudioDeviceID deviceID = kAudioDeviceUnknown;
2894
2895 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
2896 if (pData)
2897 deviceID = pData->deviceID;
2898
2899 if (deviceID != kAudioDeviceUnknown)
2900 {
2901 LogFunc(("deviceID=%RU32\n", deviceID));
2902
2903 /*
2904 * Register device callbacks.
2905 */
2906 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2907 kAudioObjectPropertyElementMaster };
2908 OSStatus err = AudioObjectAddPropertyListener(deviceID, &propAdr,
2909 coreAudioDeviceStateChangedCb, pDev /* pvUser */);
2910 if ( err != noErr
2911 && err != kAudioHardwareIllegalOperationError)
2912 {
2913 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err));
2914 }
2915
2916 propAdr.mSelector = kAudioDeviceProcessorOverload;
2917 propAdr.mScope = kAudioUnitScope_Global;
2918 err = AudioObjectAddPropertyListener(deviceID, &propAdr,
2919 coreAudioDevPropChgCb, pDev /* pvUser */);
2920 if (err != noErr)
2921 LogRel(("CoreAudio: Failed to register processor overload listener (%RI32)\n", err));
2922
2923 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
2924 propAdr.mScope = kAudioUnitScope_Global;
2925 err = AudioObjectAddPropertyListener(deviceID, &propAdr,
2926 coreAudioDevPropChgCb, pDev /* pvUser */);
2927 if (err != noErr)
2928 LogRel(("CoreAudio: Failed to register sample rate changed listener (%RI32)\n", err));
2929 }
2930
2931 return VINF_SUCCESS;
2932}
2933
2934/**
2935 * Unregisters all formerly registered callbacks of a Core Audio device again.
2936 *
2937 * @return IPRT status code.
2938 * @param pThis Host audio driver instance.
2939 * @param pDev Audio device to use for the registered callbacks.
2940 */
2941static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev)
2942{
2943 RT_NOREF(pThis);
2944
2945 AudioDeviceID deviceID = kAudioDeviceUnknown;
2946
2947 if (pDev)
2948 {
2949 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData;
2950 if (pData)
2951 deviceID = pData->deviceID;
2952 }
2953
2954 if (deviceID != kAudioDeviceUnknown)
2955 {
2956 LogFunc(("deviceID=%RU32\n", deviceID));
2957
2958 /*
2959 * Unregister per-device callbacks.
2960 */
2961 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
2962 kAudioObjectPropertyElementMaster };
2963 OSStatus err = AudioObjectRemovePropertyListener(deviceID, &propAdr,
2964 coreAudioDevPropChgCb, pDev /* pvUser */);
2965 if ( err != noErr
2966 && err != kAudioHardwareBadObjectError)
2967 {
2968 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err));
2969 }
2970
2971 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
2972 err = AudioObjectRemovePropertyListener(deviceID, &propAdr,
2973 coreAudioDevPropChgCb, pDev /* pvUser */);
2974 if ( err != noErr
2975 && err != kAudioHardwareBadObjectError)
2976 {
2977 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
2978 }
2979
2980 propAdr.mSelector = kAudioDevicePropertyDeviceIsAlive;
2981 err = AudioObjectRemovePropertyListener(deviceID, &propAdr,
2982 coreAudioDeviceStateChangedCb, pDev /* pvUser */);
2983 if ( err != noErr
2984 && err != kAudioHardwareBadObjectError)
2985 {
2986 LogRel(("CoreAudio: Failed to remove the device alive listener (%RI32)\n", err));
2987 }
2988 }
2989
2990 return VINF_SUCCESS;
2991}
2992
2993/* Callback for getting notified when some of the properties of an audio device have changed. */
2994static DECLCALLBACK(OSStatus) coreAudioDevPropChgCb(AudioObjectID propertyID,
2995 UInt32 cAddresses,
2996 const AudioObjectPropertyAddress properties[],
2997 void *pvUser)
2998{
2999 RT_NOREF(cAddresses, properties, pvUser);
3000
3001#ifdef DEBUG
3002 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)pvUser;
3003 AssertPtr(pDev);
3004
3005 LogFlowFunc(("propertyID=%u, nAddresses=%u, pDev=%p\n", propertyID, cAddresses, pDev));
3006#endif
3007
3008 switch (propertyID)
3009 {
3010#ifdef DEBUG
3011 case kAudioDeviceProcessorOverload:
3012 {
3013 LogFunc(("Processor overload detected!\n"));
3014 break;
3015 }
3016#endif /* DEBUG */
3017 case kAudioDevicePropertyNominalSampleRate:
3018 {
3019#ifndef VBOX_WITH_AUDIO_CALLBACKS
3020 int rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT);
3021 AssertRC(rc2);
3022#endif
3023 break;
3024 }
3025
3026 default:
3027 /* Just skip. */
3028 break;
3029 }
3030
3031 return noErr;
3032}
3033
3034/**
3035 * Enumerates all available host audio devices internally.
3036 *
3037 * @returns IPRT status code.
3038 * @param pThis Host audio driver instance.
3039 */
3040static int coreAudioEnumerateDevices(PDRVHOSTCOREAUDIO pThis)
3041{
3042 LogFlowFuncEnter();
3043
3044 /*
3045 * Unregister old default devices, if any.
3046 */
3047 if (pThis->pDefaultDevIn)
3048 {
3049 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevIn);
3050 pThis->pDefaultDevIn = NULL;
3051 }
3052
3053 if (pThis->pDefaultDevOut)
3054 {
3055 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevOut);
3056 pThis->pDefaultDevOut = NULL;
3057 }
3058
3059 /* Remove old / stale device entries. */
3060 DrvAudioHlpDeviceEnumFree(&pThis->Devices);
3061
3062 /* Enumerate all devices internally. */
3063 int rc = coreAudioDevicesEnumerateAll(pThis, &pThis->Devices);
3064 if (RT_SUCCESS(rc))
3065 {
3066 /*
3067 * Default input device.
3068 */
3069 pThis->pDefaultDevIn = DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->Devices, PDMAUDIODIR_IN);
3070 if (pThis->pDefaultDevIn)
3071 {
3072 LogRel2(("CoreAudio: Default capturing device is '%s'\n", pThis->pDefaultDevIn->szName));
3073
3074#ifdef DEBUG
3075 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pThis->pDefaultDevIn->pvData;
3076 AssertPtr(pDevData);
3077 LogFunc(("pDefaultDevIn=%p, ID=%RU32\n", pThis->pDefaultDevIn, pDevData->deviceID));
3078#endif
3079 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevIn);
3080 }
3081 else
3082 LogRel2(("CoreAudio: No default capturing device found\n"));
3083
3084 /*
3085 * Default output device.
3086 */
3087 pThis->pDefaultDevOut = DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->Devices, PDMAUDIODIR_OUT);
3088 if (pThis->pDefaultDevOut)
3089 {
3090 LogRel2(("CoreAudio: Default playback device is '%s'\n", pThis->pDefaultDevOut->szName));
3091
3092#ifdef DEBUG
3093 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pThis->pDefaultDevOut->pvData;
3094 AssertPtr(pDevData);
3095 LogFunc(("pDefaultDevOut=%p, ID=%RU32\n", pThis->pDefaultDevOut, pDevData->deviceID));
3096#endif
3097 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevOut);
3098 }
3099 else
3100 LogRel2(("CoreAudio: No default playback device found\n"));
3101 }
3102
3103 LogFunc(("Returning %Rrc\n", rc));
3104 return rc;
3105}
3106
3107#ifndef VBOX_WITH_AUDIO_CA_QUEUES
3108/* Callback to feed audio output buffer. */
3109static DECLCALLBACK(OSStatus) coreAudioPlaybackCb(void *pvUser,
3110 AudioUnitRenderActionFlags *pActionFlags,
3111 const AudioTimeStamp *pAudioTS,
3112 UInt32 uBusID,
3113 UInt32 cFrames,
3114 AudioBufferList *pBufData)
3115{
3116 RT_NOREF(pActionFlags, pAudioTS, uBusID, cFrames);
3117
3118 PCOREAUDIOSTREAM pStream = (PCOREAUDIOSTREAM)pvUser;
3119
3120 /* Sanity. */
3121 AssertPtr(pStream);
3122 AssertPtr(pStream->pDrv);
3123 Assert (pStream->enmDir == PDMAUDIODIR_OUT);
3124 AssertPtr(pStream->Unit.pDevice);
3125
3126 if (ASMAtomicReadU32(&pStream->enmStatus) != COREAUDIOSTATUS_INIT)
3127 {
3128 pBufData->mBuffers[0].mDataByteSize = 0;
3129 return noErr;
3130 }
3131
3132 /* How much space is used in the ring buffer? */
3133 size_t cbToRead = RT_MIN(RTCircBufUsed(pStream->pCircBuf), pBufData->mBuffers[0].mDataByteSize);
3134 if (!cbToRead)
3135 {
3136 pBufData->mBuffers[0].mDataByteSize = 0;
3137 return noErr;
3138 }
3139
3140 uint8_t *pbSrc = NULL;
3141 size_t cbRead = 0;
3142
3143 size_t cbLeft = cbToRead;
3144 while (cbLeft)
3145 {
3146 /* Try to acquire the necessary block from the ring buffer. */
3147 RTCircBufAcquireReadBlock(pStream->pCircBuf, cbLeft, (void **)&pbSrc, &cbToRead);
3148
3149 /* Break if nothing is used anymore. */
3150 if (!cbToRead)
3151 break;
3152
3153 /* Copy the data from our ring buffer to the core audio buffer. */
3154 memcpy((uint8_t *)pBufData->mBuffers[0].mData + cbRead, pbSrc, cbToRead);
3155
3156 /* Release the read buffer, so it could be used for new data. */
3157 RTCircBufReleaseReadBlock(pStream->pCircBuf, cbToRead);
3158
3159 /* Move offset. */
3160 cbRead += cbToRead;
3161
3162 /* Check if we're lagging behind. */
3163 if (cbRead > pBufData->mBuffers[0].mDataByteSize)
3164 {
3165 LogRel2(("CoreAudio: Host output lagging behind, expect stuttering guest audio output\n"));
3166 cbRead = pBufData->mBuffers[0].mDataByteSize;
3167 break;
3168 }
3169
3170 Assert(cbToRead <= cbLeft);
3171 cbLeft -= cbToRead;
3172 }
3173
3174 /* Write the bytes to the core audio buffer which were really written. */
3175 Assert(pBufData->mBuffers[0].mDataByteSize >= cbRead);
3176 pBufData->mBuffers[0].mDataByteSize = cbRead;
3177
3178 Log3Func(("Read %zu / %zu bytes\n", cbRead, cbToRead));
3179
3180 return noErr;
3181}
3182#endif /* !VBOX_WITH_AUDIO_CA_QUEUES */
3183
3184/**
3185 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamCapture}
3186 */
3187static DECLCALLBACK(int) drvHostCoreAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
3188 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
3189{
3190 RT_NOREF(pvBuf, cbBuf); /** @todo r=bird: this looks totally weird at first glance! */
3191
3192 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3193 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3194 /* pcbRead is optional. */
3195
3196 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
3197 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3198
3199#ifndef VBOX_WITH_AUDIO_CALLBACKS
3200 /* Check if the audio device should be reinitialized. If so do it. */
3201 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT)
3202 {
3203 /* For now re just re-initialize with the current input device. */
3204 if (pThis->pDefaultDevIn)
3205 {
3206 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevIn);
3207 if (RT_FAILURE(rc2))
3208 return VERR_NOT_AVAILABLE;
3209 }
3210 else
3211 return VERR_NOT_AVAILABLE;
3212 }
3213#else
3214 RT_NOREF(pThis);
3215#endif
3216
3217 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT)
3218 {
3219 if (pcbRead)
3220 *pcbRead = 0;
3221 return VINF_SUCCESS;
3222 }
3223
3224 int rc = VINF_SUCCESS;
3225 uint32_t cbWrittenTotal = 0;
3226
3227#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3228 rc = RTCritSectEnter(&pCAStream->CritSect);
3229 AssertRC(rc);
3230#endif
3231
3232 do
3233 {
3234 size_t cbMixBuf = AudioMixBufSizeBytes(&pStream->MixBuf);
3235 size_t cbToWrite = RT_MIN(cbMixBuf, RTCircBufUsed(pCAStream->pCircBuf));
3236
3237 uint32_t cWritten, cbWritten;
3238 uint8_t *puBuf;
3239 size_t cbToRead = 0;
3240
3241 Log3Func(("cbMixBuf=%zu, cbToWrite=%zu/%zu\n", cbMixBuf, cbToWrite, RTCircBufSize(pCAStream->pCircBuf)));
3242
3243 while (cbToWrite)
3244 {
3245 /* Try to acquire the necessary block from the ring buffer. */
3246 RTCircBufAcquireReadBlock(pCAStream->pCircBuf, cbToWrite, (void **)&puBuf, &cbToRead);
3247 if (!cbToRead)
3248 break;
3249
3250#ifdef DEBUG_DUMP_PCM_DATA
3251 RTFILE fh;
3252 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-capture.pcm",
3253 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
3254 if (RT_SUCCESS(rc))
3255 {
3256 RTFileWrite(fh, puBuf + cbWrittenTotal, cbToRead, NULL);
3257 RTFileClose(fh);
3258 }
3259 else
3260 AssertFailed();
3261#endif
3262 rc = AudioMixBufWriteCirc(&pStream->MixBuf, puBuf + cbWrittenTotal, cbToRead, &cWritten);
3263
3264 /* Release the read buffer, so it could be used for new data. */
3265 RTCircBufReleaseReadBlock(pCAStream->pCircBuf, cbToRead);
3266
3267 if ( RT_FAILURE(rc)
3268 || !cWritten)
3269 {
3270 break;
3271 }
3272
3273 cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
3274
3275 Assert(cbToWrite >= cbWritten);
3276 cbToWrite -= cbWritten;
3277 cbWrittenTotal += cbWritten;
3278 }
3279
3280 Log3Func(("cbToWrite=%zu, cbToRead=%zu, cbWrittenTotal=%RU32, rc=%Rrc\n", cbToWrite, cbToRead, cbWrittenTotal, rc));
3281 }
3282 while (0);
3283
3284#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3285 int rc2 = RTCritSectLeave(&pCAStream->CritSect);
3286 AssertRC(rc2);
3287#endif
3288
3289 if (RT_SUCCESS(rc))
3290 {
3291 uint32_t cCaptured = 0;
3292 uint32_t cWrittenTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbWrittenTotal);
3293 if (cWrittenTotal)
3294 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal, &cCaptured);
3295
3296 Log3Func(("cWrittenTotal=%RU32 (%RU32 bytes), cCaptured=%RU32, rc=%Rrc\n", cWrittenTotal, cbWrittenTotal, cCaptured, rc));
3297
3298 if (cCaptured)
3299 LogFlowFunc(("%RU32 samples captured\n", cCaptured));
3300
3301 if (pcbRead)
3302 *pcbRead = cCaptured;
3303 }
3304
3305 if (RT_FAILURE(rc))
3306 LogFunc(("Failed with rc=%Rrc\n", rc));
3307
3308 return rc;
3309}
3310
3311/**
3312 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamPlay}
3313 */
3314static DECLCALLBACK(int) drvHostCoreAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
3315 PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf,
3316 uint32_t *pcbWritten)
3317{
3318 RT_NOREF(pvBuf, cbBuf);
3319
3320 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3321 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
3322
3323#ifndef VBOX_WITH_AUDIO_CALLBACKS
3324 /* Check if the audio device should be reinitialized. If so do it. */
3325 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT)
3326 {
3327 if (pThis->pDefaultDevOut)
3328 {
3329 /* For now re just re-initialize with the current output device. */
3330 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevOut);
3331 if (RT_FAILURE(rc2))
3332 return VERR_NOT_AVAILABLE;
3333 }
3334 else
3335 return VERR_NOT_AVAILABLE;
3336 }
3337#else
3338 RT_NOREF(pThis);
3339#endif
3340
3341 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT)
3342 {
3343 if (pcbWritten)
3344 *pcbWritten = 0;
3345 return VINF_SUCCESS;
3346 }
3347
3348 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
3349 if (!cLive) /* Not live samples to play? Bail out. */
3350 {
3351 if (pcbWritten)
3352 *pcbWritten = 0;
3353 return VINF_SUCCESS;
3354 }
3355
3356 size_t cbLive = AUDIOMIXBUF_S2B(&pStream->MixBuf, cLive);
3357
3358 uint32_t cbReadTotal = 0;
3359
3360 int rc = VINF_SUCCESS;
3361
3362#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3363 rc = RTCritSectEnter(&pCAStream->CritSect);
3364 AssertRC(rc);
3365#endif
3366
3367 size_t cbToRead = RT_MIN(cbLive, RTCircBufFree(pCAStream->pCircBuf));
3368 Log3Func(("cbLive=%zu, cbToRead=%zu\n", cbLive, cbToRead));
3369
3370 while (cbToRead)
3371 {
3372 uint32_t cRead, cbRead;
3373 uint8_t *puBuf;
3374 size_t cbCopy;
3375
3376 /* Try to acquire the necessary space from the ring buffer. */
3377 RTCircBufAcquireWriteBlock(pCAStream->pCircBuf, cbToRead, (void **)&puBuf, &cbCopy);
3378 if (!cbCopy)
3379 {
3380 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbCopy);
3381 break;
3382 }
3383
3384 Assert(cbCopy <= cbToRead);
3385
3386 rc = AudioMixBufReadCirc(&pStream->MixBuf, puBuf, cbCopy, &cRead);
3387 if ( RT_FAILURE(rc)
3388 || !cRead)
3389 {
3390 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, 0);
3391 break;
3392 }
3393
3394 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
3395
3396 /* Release the ring buffer, so the read thread could start reading this data. */
3397 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbRead);
3398
3399 Assert(cbToRead >= cbRead);
3400 cbToRead -= cbRead;
3401 cbReadTotal += cbRead;
3402 }
3403
3404#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3405 if ( RT_SUCCESS(rc)
3406 && pCAStream->fRun
3407 && !pCAStream->fIsRunning)
3408 {
3409 rc = coreAudioStreamInvalidateQueue(pCAStream);
3410 if (RT_SUCCESS(rc))
3411 {
3412 AudioQueueStart(pCAStream->audioQueue, NULL);
3413 pCAStream->fRun = false;
3414 pCAStream->fIsRunning = true;
3415 }
3416 }
3417
3418 int rc2 = RTCritSectLeave(&pCAStream->CritSect);
3419 AssertRC(rc2);
3420#endif
3421
3422 if (RT_SUCCESS(rc))
3423 {
3424 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
3425 if (cReadTotal)
3426 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
3427
3428 Log3Func(("cReadTotal=%RU32 (%RU32 bytes)\n", cReadTotal, cbReadTotal));
3429
3430 if (pcbWritten)
3431 *pcbWritten = cReadTotal;
3432 }
3433
3434 return rc;
3435}
3436
3437static DECLCALLBACK(int) coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis,
3438 PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd)
3439{
3440 RT_NOREF(pThis);
3441
3442 uint32_t enmStatus = ASMAtomicReadU32(&pCAStream->enmStatus);
3443
3444 LogFlowFunc(("enmStreamCmd=%RU32, enmStatus=%RU32\n", enmStreamCmd, enmStatus));
3445
3446 if (!( enmStatus == COREAUDIOSTATUS_INIT
3447#ifndef VBOX_WITH_AUDIO_CALLBACKS
3448 || enmStatus == COREAUDIOSTATUS_REINIT
3449#endif
3450 ))
3451 {
3452 return VINF_SUCCESS;
3453 }
3454
3455 int rc = VINF_SUCCESS;
3456
3457 switch (enmStreamCmd)
3458 {
3459 case PDMAUDIOSTREAMCMD_ENABLE:
3460 case PDMAUDIOSTREAMCMD_RESUME:
3461 {
3462#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3463 LogFunc(("Queue enable\n"));
3464 ASMAtomicXchgBool(&pCAStream->fRun, true);
3465#else
3466 /* Only start the device if it is actually stopped */
3467 if (!coreAudioUnitIsRunning(pCAStream))
3468 {
3469 OSStatus err = AudioUnitReset(pCAStream->Unit.audioUnit, kAudioUnitScope_Input, 0);
3470 if (err != noErr)
3471 {
3472 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
3473 /* Keep going. */
3474 }
3475
3476 RTCircBufReset(pCAStream->pCircBuf);
3477
3478 err = AudioOutputUnitStart(pCAStream->Unit.audioUnit);
3479 if (err != noErr)
3480 {
3481 LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));
3482 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
3483 }
3484 }
3485#endif /* VBOX_WITH_AUDIO_CA_QUEUES */
3486 break;
3487 }
3488
3489 case PDMAUDIOSTREAMCMD_DISABLE:
3490 {
3491#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3492 LogFunc(("Queue disable\n"));
3493 AudioQueueStop(pCAStream->audioQueue, 1 /* Immediately */);
3494 ASMAtomicXchgBool(&pCAStream->fRun, false);
3495 ASMAtomicXchgBool(&pCAStream->fIsRunning, false);
3496#endif
3497 break;
3498 }
3499
3500 case PDMAUDIOSTREAMCMD_PAUSE:
3501 {
3502#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3503 LogFunc(("Queue pause\n"));
3504 AudioQueuePause(pCAStream->audioQueue);
3505 ASMAtomicXchgBool(&pCAStream->fIsRunning, false);
3506#else
3507 /* Only stop the device if it is actually running */
3508 if (coreAudioUnitIsRunning(pCAStream))
3509 {
3510 OSStatus err = AudioOutputUnitStop(pCAStream->Unit.audioUnit);
3511 if (err != noErr)
3512 {
3513 LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
3514 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
3515 break;
3516 }
3517
3518 err = AudioUnitReset(pCAStream->Unit.audioUnit, kAudioUnitScope_Input, 0);
3519 if (err != noErr)
3520 {
3521 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
3522 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
3523 }
3524 }
3525#endif /* VBOX_WITH_AUDIO_CA_QUEUES */
3526 break;
3527 }
3528
3529 default:
3530 rc = VERR_NOT_SUPPORTED;
3531 break;
3532 }
3533
3534 LogFlowFuncLeaveRC(rc);
3535 return rc;
3536}
3537
3538
3539/**
3540 * @interface_method_impl{PDMIHOSTAUDIO, pfnGetConfig}
3541 */
3542static DECLCALLBACK(int) drvHostCoreAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
3543{
3544 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3545 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
3546
3547 RT_BZERO(pBackendCfg, sizeof(PDMAUDIOBACKENDCFG));
3548
3549 pBackendCfg->cbStreamIn = sizeof(COREAUDIOSTREAM);
3550 pBackendCfg->cbStreamOut = sizeof(COREAUDIOSTREAM);
3551
3552 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
3553 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
3554
3555 LogFlowFunc(("Returning %Rrc\n", VINF_SUCCESS));
3556 return VINF_SUCCESS;
3557}
3558
3559
3560/**
3561 * @interface_method_impl{PDMIHOSTAUDIO, pfnGetDevices}
3562 */
3563static DECLCALLBACK(int) drvHostCoreAudioGetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIODEVICEENUM pDeviceEnum)
3564{
3565 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3566 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER);
3567
3568 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3569
3570 int rc = RTCritSectEnter(&pThis->CritSect);
3571 if (RT_SUCCESS(rc))
3572 {
3573 rc = coreAudioEnumerateDevices(pThis);
3574 if (RT_SUCCESS(rc))
3575 {
3576 if (pDeviceEnum)
3577 {
3578 rc = DrvAudioHlpDeviceEnumInit(pDeviceEnum);
3579 if (RT_SUCCESS(rc))
3580 rc = DrvAudioHlpDeviceEnumCopy(pDeviceEnum, &pThis->Devices);
3581
3582 if (RT_FAILURE(rc))
3583 DrvAudioHlpDeviceEnumFree(pDeviceEnum);
3584 }
3585 }
3586
3587 int rc2 = RTCritSectLeave(&pThis->CritSect);
3588 AssertRC(rc2);
3589 }
3590
3591 LogFlowFunc(("Returning %Rrc\n", rc));
3592 return rc;
3593}
3594
3595
3596#ifdef VBOX_WITH_AUDIO_CALLBACKS
3597/**
3598 * @interface_method_impl{PDMIHOSTAUDIO, pfnSetCallback}
3599 */
3600static DECLCALLBACK(int) drvHostCoreAudioSetCallback(PPDMIHOSTAUDIO pInterface, PFNPDMHOSTAUDIOCALLBACK pfnCallback)
3601{
3602 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3603 /* pfnCallback will be handled below. */
3604
3605 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3606
3607 int rc = RTCritSectEnter(&pThis->CritSect);
3608 if (RT_SUCCESS(rc))
3609 {
3610 LogFunc(("pfnCallback=%p\n", pfnCallback));
3611
3612 if (pfnCallback) /* Register. */
3613 {
3614 Assert(pThis->pfnCallback == NULL);
3615 pThis->pfnCallback = pfnCallback;
3616 }
3617 else /* Unregister. */
3618 {
3619 if (pThis->pfnCallback)
3620 pThis->pfnCallback = NULL;
3621 }
3622
3623 int rc2 = RTCritSectLeave(&pThis->CritSect);
3624 AssertRC(rc2);
3625 }
3626
3627 return rc;
3628}
3629#endif
3630
3631
3632/**
3633 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamGetStatus}
3634 */
3635static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
3636{
3637 RT_NOREF(enmDir);
3638 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
3639
3640 return PDMAUDIOBACKENDSTS_RUNNING;
3641}
3642
3643
3644/**
3645 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamCreate}
3646 */
3647static DECLCALLBACK(int) drvHostCoreAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
3648 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
3649{
3650 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3651 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3652 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
3653 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
3654
3655 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3656
3657 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
3658
3659 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
3660
3661 int rc;
3662
3663#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3664 rc = RTCritSectInit(&pCAStream->CritSect);
3665 if (RT_FAILURE(rc))
3666 return rc;
3667
3668 pCAStream->hThread = NIL_RTTHREAD;
3669 pCAStream->fRun = false;
3670 pCAStream->fIsRunning = false;
3671 pCAStream->fShutdown = false;
3672#endif
3673
3674 /* Input or output device? */
3675 bool fIn = pCfgReq->enmDir == PDMAUDIODIR_IN;
3676
3677 /* For now, just use the default device available. */
3678 PPDMAUDIODEVICE pDev = fIn ? pThis->pDefaultDevIn : pThis->pDefaultDevOut;
3679
3680 LogFunc(("pStream=%p, pCfgReq=%p, pCfgAcq=%p, fIn=%RTbool, pDev=%p\n", pStream, pCfgReq, pCfgAcq, fIn, pDev));
3681
3682 if (pDev) /* (Default) device available? */
3683 {
3684 /* Sanity. */
3685 AssertPtr(pDev->pvData);
3686 Assert(pDev->cbData);
3687
3688 /* Init the Core Audio stream. */
3689 rc = coreAudioStreamInit(pCAStream, pThis, pDev);
3690 if (RT_SUCCESS(rc))
3691 {
3692#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3693 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_INIT);
3694
3695 rc = coreAudioStreamInitQueue(pCAStream, pCfgReq, pCfgAcq);
3696
3697 pCfgAcq->cSampleBufferSize = _4K; /** @todo FIX THIS !!! */
3698#else
3699 if (fIn)
3700 rc = coreAudioStreamInitIn (pCAStream, pCfgReq, pCfgAcq);
3701 else
3702 rc = coreAudioStreamInitOut(pCAStream, pCfgReq, pCfgAcq);
3703#endif
3704 if (RT_SUCCESS(rc))
3705 {
3706 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT);
3707 }
3708 else
3709 {
3710 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_UNINIT);
3711
3712 int rc2 = coreAudioStreamUninit(pCAStream);
3713 AssertRC(rc2);
3714
3715 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_UNINIT);
3716 }
3717 }
3718 }
3719 else
3720 rc = VERR_NOT_AVAILABLE;
3721
3722 LogFunc(("Returning %Rrc\n", rc));
3723 return rc;
3724}
3725
3726
3727/**
3728 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamDestroy}
3729 */
3730static DECLCALLBACK(int) drvHostCoreAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
3731{
3732 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3733 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3734
3735 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3736
3737 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
3738
3739 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
3740
3741 uint32_t status = ASMAtomicReadU32(&pCAStream->enmStatus);
3742 if (!( status == COREAUDIOSTATUS_INIT
3743#ifndef VBOX_WITH_AUDIO_CALLBACKS
3744 || status == COREAUDIOSTATUS_REINIT
3745#endif
3746 ))
3747 {
3748 AssertFailed();
3749 return VINF_SUCCESS;
3750 }
3751
3752 int rc = coreAudioStreamControl(pThis, pCAStream, PDMAUDIOSTREAMCMD_DISABLE);
3753 if (RT_SUCCESS(rc))
3754 {
3755 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_UNINIT);
3756
3757 rc = coreAudioStreamUninit(pCAStream);
3758
3759 if (RT_SUCCESS(rc))
3760 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_UNINIT);
3761 }
3762
3763#ifdef VBOX_WITH_AUDIO_CA_QUEUES
3764 if (RT_SUCCESS(rc))
3765 {
3766 if (RTCritSectIsInitialized(&pCAStream->CritSect))
3767 RTCritSectDelete(&pCAStream->CritSect);
3768 }
3769#endif
3770
3771 LogFunc(("rc=%Rrc\n", rc));
3772 return rc;
3773}
3774
3775
3776/**
3777 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamControl}
3778 */
3779static DECLCALLBACK(int) drvHostCoreAudioStreamControl(PPDMIHOSTAUDIO pInterface,
3780 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
3781{
3782 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3783 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3784
3785 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3786
3787 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
3788
3789 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
3790
3791 return coreAudioStreamControl(pThis, pCAStream, enmStreamCmd);
3792}
3793
3794
3795/**
3796 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamGetStatus}
3797 */
3798static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostCoreAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
3799{
3800 RT_NOREF(pInterface);
3801 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3802
3803 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
3804
3805 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE;
3806
3807 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;
3808
3809 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_INIT)
3810 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED;
3811
3812 if (pStream->enmDir == PDMAUDIODIR_IN)
3813 {
3814 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED)
3815 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_READABLE;
3816 }
3817 else if (pStream->enmDir == PDMAUDIODIR_OUT)
3818 {
3819 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED)
3820 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
3821 }
3822 else
3823 AssertFailed();
3824
3825 return strmSts;
3826}
3827
3828
3829/**
3830 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamIterate}
3831 */
3832static DECLCALLBACK(int) drvHostCoreAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
3833{
3834 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
3835 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
3836
3837 /* Nothing to do here for Core Audio. */
3838 return VINF_SUCCESS;
3839}
3840
3841
3842/**
3843 * @interface_method_impl{PDMIHOSTAUDIO, pfnInit}
3844 */
3845static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)
3846{
3847 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3848
3849 int rc = DrvAudioHlpDeviceEnumInit(&pThis->Devices);
3850 if (RT_SUCCESS(rc))
3851 {
3852 /* Do the first (initial) internal device enumeration. */
3853 rc = coreAudioEnumerateDevices(pThis);
3854 }
3855
3856 if (RT_SUCCESS(rc))
3857 {
3858 /* Register system callbacks. */
3859 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
3860 kAudioObjectPropertyElementMaster };
3861
3862 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
3863 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
3864 if ( err != noErr
3865 && err != kAudioHardwareIllegalOperationError)
3866 {
3867 LogRel(("CoreAudio: Failed to add the input default device changed listener (%RI32)\n", err));
3868 }
3869
3870 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
3871 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
3872 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
3873 if ( err != noErr
3874 && err != kAudioHardwareIllegalOperationError)
3875 {
3876 LogRel(("CoreAudio: Failed to add the output default device changed listener (%RI32)\n", err));
3877 }
3878 }
3879
3880 LogFlowFunc(("Returning %Rrc\n", rc));
3881 return rc;
3882}
3883
3884
3885/**
3886 * @interface_method_impl{PDMIHOSTAUDIO, pfnShutdown}
3887 */
3888static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
3889{
3890 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
3891
3892 /*
3893 * Unregister system callbacks.
3894 */
3895 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
3896 kAudioObjectPropertyElementMaster };
3897
3898 OSStatus err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
3899 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
3900 if ( err != noErr
3901 && err != kAudioHardwareBadObjectError)
3902 {
3903 LogRel(("CoreAudio: Failed to remove the default input device changed listener (%RI32)\n", err));
3904 }
3905
3906 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
3907 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
3908 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */);
3909 if ( err != noErr
3910 && err != kAudioHardwareBadObjectError)
3911 {
3912 LogRel(("CoreAudio: Failed to remove the default output device changed listener (%RI32)\n", err));
3913 }
3914
3915 LogFlowFuncEnter();
3916}
3917
3918
3919/**
3920 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3921 */
3922static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3923{
3924 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3925 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
3926
3927 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3928 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
3929
3930 return NULL;
3931}
3932
3933
3934/**
3935 * @callback_method_impl{FNPDMDRVCONSTRUCT,
3936 * Construct a Core Audio driver instance.}
3937 */
3938static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3939{
3940 RT_NOREF(pCfg, fFlags);
3941 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3942 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
3943 LogRel(("Audio: Initializing Core Audio driver\n"));
3944
3945 /*
3946 * Init the static parts.
3947 */
3948 pThis->pDrvIns = pDrvIns;
3949 /* IBase */
3950 pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
3951 /* IHostAudio */
3952 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
3953
3954 /* This backend supports device enumeration. */
3955 pThis->IHostAudio.pfnGetDevices = drvHostCoreAudioGetDevices;
3956
3957#ifdef VBOX_WITH_AUDIO_CALLBACKS
3958 /* This backend supports host audio callbacks. */
3959 pThis->IHostAudio.pfnSetCallback = drvHostCoreAudioSetCallback;
3960 pThis->pfnCallback = NULL;
3961#endif
3962
3963 int rc = RTCritSectInit(&pThis->CritSect);
3964
3965 LogFlowFuncLeaveRC(rc);
3966 return rc;
3967}
3968
3969
3970/**
3971 * @callback_method_impl{FNPDMDRVDESTRUCT}
3972 */
3973static DECLCALLBACK(void) drvHostCoreAudioDestruct(PPDMDRVINS pDrvIns)
3974{
3975 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3976 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
3977
3978 int rc2 = RTCritSectDelete(&pThis->CritSect);
3979 AssertRC(rc2);
3980
3981 LogFlowFuncLeaveRC(rc2);
3982}
3983
3984
3985/**
3986 * Char driver registration record.
3987 */
3988const PDMDRVREG g_DrvHostCoreAudio =
3989{
3990 /* u32Version */
3991 PDM_DRVREG_VERSION,
3992 /* szName */
3993 "CoreAudio",
3994 /* szRCMod */
3995 "",
3996 /* szR0Mod */
3997 "",
3998 /* pszDescription */
3999 "Core Audio host driver",
4000 /* fFlags */
4001 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
4002 /* fClass. */
4003 PDM_DRVREG_CLASS_AUDIO,
4004 /* cMaxInstances */
4005 ~0U,
4006 /* cbInstance */
4007 sizeof(DRVHOSTCOREAUDIO),
4008 /* pfnConstruct */
4009 drvHostCoreAudioConstruct,
4010 /* pfnDestruct */
4011 drvHostCoreAudioDestruct,
4012 /* pfnRelocate */
4013 NULL,
4014 /* pfnIOCtl */
4015 NULL,
4016 /* pfnPowerOn */
4017 NULL,
4018 /* pfnReset */
4019 NULL,
4020 /* pfnSuspend */
4021 NULL,
4022 /* pfnResume */
4023 NULL,
4024 /* pfnAttach */
4025 NULL,
4026 /* pfnDetach */
4027 NULL,
4028 /* pfnPowerOff */
4029 NULL,
4030 /* pfnSoftReset */
4031 NULL,
4032 /* u32EndVersion */
4033 PDM_DRVREG_VERSION
4034};
4035
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