VirtualBox

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

Last change on this file since 63699 was 63683, checked in by vboxsync, 8 years ago

Audio/DrvHostCoreAudio.cpp: Fixed interface casting messup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 110.6 KB
Line 
1/* $Id: DrvHostCoreAudio.cpp 63683 2016-09-02 09:23:02Z 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <VBox/log.h>
24#include <VBox/vmm/pdmaudioifs.h>
25
26#include "DrvAudio.h"
27#include "AudioMixBuffer.h"
28
29#include "VBoxDD.h"
30
31#include <iprt/asm.h>
32#include <iprt/cdefs.h>
33#include <iprt/circbuf.h>
34#include <iprt/mem.h>
35
36#include <iprt/uuid.h>
37
38#include <CoreAudio/CoreAudio.h>
39#include <CoreServices/CoreServices.h>
40#include <AudioUnit/AudioUnit.h>
41#include <AudioToolbox/AudioConverter.h>
42
43#if 0
44# include <iprt/file.h>
45# define DEBUG_DUMP_PCM_DATA
46# ifdef RT_OS_WINDOWS
47# define DEBUG_DUMP_PCM_DATA_PATH "c:\\temp\\"
48# else
49# define DEBUG_DUMP_PCM_DATA_PATH "/tmp/"
50# endif
51#endif
52
53/** @todo
54 * - Maybe make sure the threads are immediately stopped if playing/recording stops.
55 */
56
57/*
58 * Most of this is based on:
59 * http://developer.apple.com/mac/library/technotes/tn2004/tn2097.html
60 * http://developer.apple.com/mac/library/technotes/tn2002/tn2091.html
61 * http://developer.apple.com/mac/library/qa/qa2007/qa1533.html
62 * http://developer.apple.com/mac/library/qa/qa2001/qa1317.html
63 * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
64 */
65
66
67/*******************************************************************************
68 *
69 * Helper function section
70 *
71 ******************************************************************************/
72
73/* Move these down below the internal function prototypes... */
74
75static void coreAudioPrintASBD(const char *pszDesc, const AudioStreamBasicDescription *pASBD)
76{
77 char pszSampleRate[32];
78 LogRel2(("CoreAudio: %s description:\n", pszDesc));
79 LogRel2(("CoreAudio: Format ID: %RU32 (%c%c%c%c)\n", pASBD->mFormatID,
80 RT_BYTE4(pASBD->mFormatID), RT_BYTE3(pASBD->mFormatID),
81 RT_BYTE2(pASBD->mFormatID), RT_BYTE1(pASBD->mFormatID)));
82 LogRel2(("CoreAudio: Flags: %RU32", pASBD->mFormatFlags));
83 if (pASBD->mFormatFlags & kAudioFormatFlagIsFloat)
84 LogRel2((" Float"));
85 if (pASBD->mFormatFlags & kAudioFormatFlagIsBigEndian)
86 LogRel2((" BigEndian"));
87 if (pASBD->mFormatFlags & kAudioFormatFlagIsSignedInteger)
88 LogRel2((" SignedInteger"));
89 if (pASBD->mFormatFlags & kAudioFormatFlagIsPacked)
90 LogRel2((" Packed"));
91 if (pASBD->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
92 LogRel2((" AlignedHigh"));
93 if (pASBD->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
94 LogRel2((" NonInterleaved"));
95 if (pASBD->mFormatFlags & kAudioFormatFlagIsNonMixable)
96 LogRel2((" NonMixable"));
97 if (pASBD->mFormatFlags & kAudioFormatFlagsAreAllClear)
98 LogRel2((" AllClear"));
99 LogRel2(("\n"));
100 snprintf(pszSampleRate, 32, "%.2f", (float)pASBD->mSampleRate); /** @todo r=andy Use RTStrPrint*. */
101 LogRel2(("CoreAudio: SampleRate : %s\n", pszSampleRate));
102 LogRel2(("CoreAudio: ChannelsPerFrame: %RU32\n", pASBD->mChannelsPerFrame));
103 LogRel2(("CoreAudio: FramesPerPacket : %RU32\n", pASBD->mFramesPerPacket));
104 LogRel2(("CoreAudio: BitsPerChannel : %RU32\n", pASBD->mBitsPerChannel));
105 LogRel2(("CoreAudio: BytesPerFrame : %RU32\n", pASBD->mBytesPerFrame));
106 LogRel2(("CoreAudio: BytesPerPacket : %RU32\n", pASBD->mBytesPerPacket));
107}
108
109static void coreAudioPCMPropsToASBD(PDMAUDIOPCMPROPS *pPCMProps, AudioStreamBasicDescription *pASBD)
110{
111 AssertPtrReturnVoid(pPCMProps);
112 AssertPtrReturnVoid(pASBD);
113
114 RT_BZERO(pASBD, sizeof(AudioStreamBasicDescription));
115
116 pASBD->mFormatID = kAudioFormatLinearPCM;
117 pASBD->mFormatFlags = kAudioFormatFlagIsPacked;
118 pASBD->mFramesPerPacket = 1; /* For uncompressed audio, set this to 1. */
119 pASBD->mSampleRate = (Float64)pPCMProps->uHz;
120 pASBD->mChannelsPerFrame = pPCMProps->cChannels;
121 pASBD->mBitsPerChannel = pPCMProps->cBits;
122 if (pPCMProps->fSigned)
123 pASBD->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
124 pASBD->mBytesPerFrame = pASBD->mChannelsPerFrame * (pASBD->mBitsPerChannel / 8);
125 pASBD->mBytesPerPacket = pASBD->mFramesPerPacket * pASBD->mBytesPerFrame;
126}
127
128static int coreAudioStreamCfgToASBD(PPDMAUDIOSTREAMCFG pCfg, AudioStreamBasicDescription *pASBD)
129{
130 AssertPtrReturn(pCfg, VERR_INVALID_PARAMETER);
131 AssertPtrReturn(pASBD, VERR_INVALID_PARAMETER);
132
133 PDMAUDIOPCMPROPS Props;
134 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &Props);
135 if (RT_SUCCESS(rc))
136 coreAudioPCMPropsToASBD(&Props, pASBD);
137
138 return rc;
139}
140
141static OSStatus coreAudioSetFrameBufferSize(AudioDeviceID deviceID, bool fInput, UInt32 cReqSize, UInt32 *pcActSize)
142{
143 AudioObjectPropertyScope propScope = fInput
144 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
145 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyBufferFrameSize, propScope,
146 kAudioObjectPropertyElementMaster };
147
148 /* First try to set the new frame buffer size. */
149 OSStatus err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);
150
151 /* Check if it really was set. */
152 UInt32 cSize = sizeof(*pcActSize);
153 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
154 if (RT_UNLIKELY(err != noErr))
155 return err;
156
157 /* If both sizes are the same, we are done. */
158 if (cReqSize == *pcActSize)
159 return noErr;
160
161 /* If not we have to check the limits of the device. First get the size of
162 the buffer size range property. */
163 propAdr.mSelector = kAudioDevicePropertyBufferSizeRange;
164 err = AudioObjectGetPropertyDataSize(deviceID, &propAdr, 0, NULL, &cSize);
165 if (RT_UNLIKELY(err != noErr))
166 return err;
167
168 Assert(cSize);
169 AudioValueRange *pRange = (AudioValueRange *)RTMemAllocZ(cSize);
170 if (pRange)
171 {
172 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pRange);
173 if (err == noErr)
174 {
175 Float64 cMin = -1;
176 Float64 cMax = -1;
177 for (size_t a = 0; a < cSize / sizeof(AudioValueRange); a++)
178 {
179 /* Search for the absolute minimum. */
180 if ( pRange[a].mMinimum < cMin
181 || cMin == -1)
182 cMin = pRange[a].mMinimum;
183
184 /* Search for the best maximum which isn't bigger than cReqSize. */
185 if (pRange[a].mMaximum < cReqSize)
186 {
187 if (pRange[a].mMaximum > cMax)
188 cMax = pRange[a].mMaximum;
189 }
190 }
191 if (cMax == -1)
192 cMax = cMin;
193 cReqSize = cMax;
194
195 /* First try to set the new frame buffer size. */
196 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
197 err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);
198 if (err == noErr)
199 {
200 /* Check if it really was set. */
201 cSize = sizeof(*pcActSize);
202 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
203 }
204 }
205
206 RTMemFree(pRange);
207 }
208 else
209 err = notEnoughMemoryErr;
210
211 return err;
212}
213
214DECL_FORCE_INLINE(bool) coreAudioIsRunning(AudioDeviceID deviceID)
215{
216 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsRunning, kAudioObjectPropertyScopeGlobal,
217 kAudioObjectPropertyElementMaster };
218 UInt32 uFlag = 0;
219 UInt32 uSize = sizeof(uFlag);
220 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uFlag);
221 if (err != kAudioHardwareNoError)
222 LogRel(("CoreAudio: Could not determine whether the device is running (%RI32)\n", err));
223
224 return (uFlag >= 1);
225}
226
227static int coreAudioCFStringToCString(const CFStringRef pCFString, char **ppszString)
228{
229 CFIndex cLen = CFStringGetLength(pCFString) + 1;
230 char *pszResult = (char *)RTMemAllocZ(cLen * sizeof(char));
231 if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
232 {
233 RTMemFree(pszResult);
234 return VERR_NOT_FOUND;
235 }
236
237 *ppszString = pszResult;
238 return VINF_SUCCESS;
239}
240
241#if 0 /* unused */
242static AudioDeviceID coreAudioDeviceUIDtoID(const char* pszUID)
243{
244 /* Create a CFString out of our CString. */
245 CFStringRef strUID = CFStringCreateWithCString(NULL, pszUID, kCFStringEncodingMacRoman);
246
247 /* Fill the translation structure. */
248 AudioDeviceID deviceID;
249
250 AudioValueTranslation translation;
251 translation.mInputData = &strUID;
252 translation.mInputDataSize = sizeof(CFStringRef);
253 translation.mOutputData = &deviceID;
254 translation.mOutputDataSize = sizeof(AudioDeviceID);
255
256 /* Fetch the translation from the UID to the device ID. */
257 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDeviceForUID, kAudioObjectPropertyScopeGlobal,
258 kAudioObjectPropertyElementMaster };
259
260 UInt32 uSize = sizeof(AudioValueTranslation);
261 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &translation);
262
263 /* Release the temporary CFString */
264 CFRelease(strUID);
265
266 if (RT_LIKELY(err == noErr))
267 return deviceID;
268
269 /* Return the unknown device on error. */
270 return kAudioDeviceUnknown;
271}
272#endif /* unused */
273
274
275/*********************************************************************************************************************************
276* Defined Constants And Macros *
277*********************************************************************************************************************************/
278/** @name Initialization status indicator used for the recreation of the AudioUnits.
279 * @{ */
280#define CA_STATUS_UNINIT UINT32_C(0) /**< The device is uninitialized */
281#define CA_STATUS_IN_INIT UINT32_C(1) /**< The device is currently initializing */
282#define CA_STATUS_INIT UINT32_C(2) /**< The device is initialized */
283#define CA_STATUS_IN_UNINIT UINT32_C(3) /**< The device is currently uninitializing */
284#define CA_STATUS_REINIT UINT32_C(4) /**< The device has to be reinitialized */
285/** @} */
286
287
288/*********************************************************************************************************************************
289* Global Variables *
290*********************************************************************************************************************************/
291/* Error code which indicates "End of data" */
292static const OSStatus g_caConverterEOFDErr = 0x656F6664; /* 'eofd' */
293
294
295/*********************************************************************************************************************************
296* Structures and Typedefs *
297*********************************************************************************************************************************/
298/* Prototypes needed for COREAUDIOSTREAMCBCTX. */
299struct COREAUDIOSTREAMIN;
300typedef struct COREAUDIOSTREAMIN *PCOREAUDIOSTREAMIN;
301struct COREAUDIOSTREAMOUT;
302typedef struct COREAUDIOSTREAMOUT *PCOREAUDIOSTREAMOUT;
303
304/**
305 * Host Coreaudio driver instance data.
306 * @implements PDMIAUDIOCONNECTOR
307 */
308typedef struct DRVHOSTCOREAUDIO
309{
310 /** Pointer to the driver instance structure. */
311 PPDMDRVINS pDrvIns;
312 /** Pointer to host audio interface. */
313 PDMIHOSTAUDIO IHostAudio;
314} DRVHOSTCOREAUDIO, *PDRVHOSTCOREAUDIO;
315
316/** Converts a pointer to DRVHOSTCOREAUDIO::IHostAudio to a PDRVHOSTCOREAUDIO. */
317#define PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTCOREAUDIO, IHostAudio)
318
319/**
320 * Simple structure for maintaining a stream's callback context.
321 ** @todo Remove this as soon as we have unified input/output streams in this backend.
322 */
323typedef struct COREAUDIOSTREAMCBCTX
324{
325 /** Pointer to driver instance. */
326 PDRVHOSTCOREAUDIO pThis;
327 /** The stream's direction. */
328 PDMAUDIODIR enmDir;
329 union
330 {
331 /** Pointer to self, if it's an input stream. */
332 PCOREAUDIOSTREAMIN pIn;
333 /** Pointer to self, if it's an output stream. */
334 PCOREAUDIOSTREAMOUT pOut;
335 };
336} COREAUDIOSTREAMCBCTX, *PCOREAUDIOSTREAMCBCTX;
337
338/**
339 * Structure for keeping a conversion callback context.
340 * This is needed when using an audio converter during input/output processing.
341 */
342typedef struct COREAUDIOCONVCBCTX
343{
344 /** Pointer to stream context this converter callback context
345 * is bound to. */
346 /** @todo Remove this as soon as we have unified input/output streams in this backend. */
347 COREAUDIOSTREAMCBCTX pStream;
348 /** Source stream description. */
349 AudioStreamBasicDescription asbdSrc;
350 /** Destination stream description. */
351 AudioStreamBasicDescription asbdDst;
352 /** Native buffer list used for rendering the source audio data into. */
353 AudioBufferList bufLstSrc;
354 /** Total packet conversion count. */
355 UInt32 uPacketCnt;
356 /** Current packet conversion index. */
357 UInt32 uPacketIdx;
358} COREAUDIOCONVCBCTX, *PCOREAUDIOCONVCBCTX;
359
360/** @todo Unify COREAUDIOSTREAMOUT / COREAUDIOSTREAMIN. */
361typedef struct COREAUDIOSTREAMOUT
362{
363 /** Host output stream.
364 * Note: Always must come first in this structure! */
365 PDMAUDIOSTREAM Stream;
366 /** Stream description which is default on the device. */
367 AudioStreamBasicDescription deviceFormat;
368 /** Stream description which is selected for using with VBox. */
369 AudioStreamBasicDescription streamFormat;
370 /** The audio device ID of the currently used device. */
371 AudioDeviceID deviceID;
372 /** The AudioUnit being used. */
373 AudioUnit audioUnit;
374 /** A ring buffer for transferring data to the playback thread. */
375 PRTCIRCBUF pCircBuf;
376 /** Initialization status tracker. Used when some of the device parameters
377 * or the device itself is changed during the runtime. */
378 volatile uint32_t status;
379 /** Flag whether the "default device changed" listener was registered. */
380 bool fDefDevChgListReg;
381 /** Flag whether the "device state changed" listener was registered. */
382 bool fDevStateChgListReg;
383 /** Callback context for this stream for handing this stream in to
384 * a CoreAudio callback.
385 ** @todo Remove this as soon as we have unified input/output streams in this backend. */
386 COREAUDIOSTREAMCBCTX cbCtx;
387} COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT;
388
389typedef struct COREAUDIOSTREAMIN
390{
391 /** Host input stream.
392 * Note: Always must come first in this structure! */
393 PDMAUDIOSTREAM Stream;
394 /** Stream description which is default on the device. */
395 AudioStreamBasicDescription deviceFormat;
396 /** Stream description which is selected for using with VBox. */
397 AudioStreamBasicDescription streamFormat;
398 /** The audio device ID of the currently used device. */
399 AudioDeviceID deviceID;
400 /** The AudioUnit used. */
401 AudioUnit audioUnit;
402 /** A ring buffer for transferring data from the capturing thread. */
403 PRTCIRCBUF pCircBuf;
404 /** The audio converter if necessary. NULL if no converter is being used. */
405 AudioConverterRef pConverter;
406 /** Callback context for the audio converter. */
407 COREAUDIOCONVCBCTX convCbCtx;
408 /** The ratio between the device & the stream sample rate. */
409 Float64 sampleRatio;
410 /** Initialization status tracker. Used when some of the device parameters
411 * or the device itself is changed during the runtime. */
412 volatile uint32_t status;
413 /** Flag whether the "default device changed" listener was registered. */
414 bool fDefDevChgListReg;
415 /** Flag whether the "device state changed" listener was registered. */
416 bool fDevStateChgListReg;
417 /** Callback context for this stream for handing this stream in to
418 * a CoreAudio callback.
419 ** @todo Remove this as soon as we have unified input/output streams in this backend. */
420 COREAUDIOSTREAMCBCTX cbCtx;
421} COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN;
422
423
424/*********************************************************************************************************************************
425* Internal Functions *
426*********************************************************************************************************************************/
427static OSStatus coreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID, UInt32 cAddresses, const AudioObjectPropertyAddress properties[], void *pvUser);
428static OSStatus coreAudioPlaybackCb(void *pvUser, AudioUnitRenderActionFlags *pActionFlags, const AudioTimeStamp *pAudioTS, UInt32 uBusID, UInt32 cFrames, AudioBufferList* pBufData);
429
430
431/**
432 * Initializes a conversion callback context.
433 *
434 * @return IPRT status code.
435 * @param pConvCbCtx Conversion callback context to initialize.
436 * @param pASBDSrc Input (source) stream description to use.
437 * @param pASBDDst Output (destination) stream description to use.
438 */
439static int coreAudioInitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx,
440 AudioStreamBasicDescription *pASBDSrc, AudioStreamBasicDescription *pASBDDst)
441{
442 AssertPtrReturn(pConvCbCtx, VERR_INVALID_POINTER);
443 AssertPtrReturn(pASBDSrc, VERR_INVALID_POINTER);
444 AssertPtrReturn(pASBDDst, VERR_INVALID_POINTER);
445
446 memcpy(&pConvCbCtx->asbdSrc, pASBDSrc, sizeof(AudioStreamBasicDescription));
447 memcpy(&pConvCbCtx->asbdDst, pASBDDst, sizeof(AudioStreamBasicDescription));
448
449 /* Create the AudioBufferList structure with one buffer. */
450 pConvCbCtx->bufLstSrc.mNumberBuffers = 1;
451
452 /* Initialize the conversion buffer. */
453 pConvCbCtx->bufLstSrc.mBuffers[0].mNumberChannels = pConvCbCtx->asbdSrc.mChannelsPerFrame;
454 pConvCbCtx->bufLstSrc.mBuffers[0].mDataByteSize = 0;
455 pConvCbCtx->bufLstSrc.mBuffers[0].mData = NULL;
456
457 return VINF_SUCCESS;
458}
459
460/**
461 * Uninitializes a conversion callback context.
462 *
463 * @return IPRT status code.
464 * @param pConvCbCtx Conversion callback context to uninitialize.
465 */
466static void coreAudioUninitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx)
467{
468 AssertPtrReturnVoid(pConvCbCtx);
469
470 RT_ZERO(pConvCbCtx->asbdSrc);
471 RT_ZERO(pConvCbCtx->asbdDst);
472
473 pConvCbCtx->bufLstSrc.mNumberBuffers = 0;
474}
475
476/**
477 * Does a (Re-)enumeration of the host's playback + recording devices.
478 *
479 * @return IPRT status code.
480 * @param pThis Host audio driver instance.
481 * @param pCfg Where to store the enumeration results.
482 * @param fEnum Enumeration flags.
483 */
484static int coreAudioDevicesEnumerate(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, bool fIn, uint32_t fEnum)
485{
486 RT_NOREF(fEnum);
487 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
488 /* pCfg is optional. */
489
490 int rc = VINF_SUCCESS;
491
492 uint8_t cDevs = 0;
493
494 do
495 {
496 AudioObjectPropertyAddress propAdrDevList = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
497 kAudioObjectPropertyElementMaster };
498 UInt32 uSize = 0;
499 OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize);
500 if (err != kAudioHardwareNoError)
501 break;
502
503 AudioDeviceID *pDevIDs = (AudioDeviceID *)alloca(uSize);
504 if (pDevIDs == NULL)
505 break;
506
507 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize, pDevIDs);
508 if (err != kAudioHardwareNoError)
509 break;
510
511 UInt32 cDevices = uSize / sizeof (AudioDeviceID);
512 for (UInt32 i = 0; i < cDevices; i++)
513 {
514 AudioDeviceID curDevID = pDevIDs[i];
515
516 /* Check if the device is valid. */
517 AudioObjectPropertyAddress propAddrCfg = { kAudioDevicePropertyStreamConfiguration,
518 fIn ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
519 kAudioObjectPropertyElementMaster };
520
521 err = AudioObjectGetPropertyDataSize(curDevID, &propAddrCfg, 0, NULL, &uSize);
522 if (err != noErr)
523 continue;
524
525 AudioBufferList *pBufList = (AudioBufferList *)RTMemAlloc(uSize);
526 if (!pBufList)
527 continue;
528
529 bool fIsValid = false;
530
531 err = AudioObjectGetPropertyData(curDevID, &propAddrCfg, 0, NULL, &uSize, pBufList);
532 if (err == noErr)
533 {
534 for (UInt32 a = 0; a < pBufList->mNumberBuffers; a++)
535 {
536 fIsValid = pBufList->mBuffers[a].mNumberChannels > 0;
537 if (fIsValid)
538 break;
539 }
540 }
541
542 if (pBufList)
543 {
544 RTMemFree(pBufList);
545 pBufList = NULL;
546 }
547
548 if (!fIsValid)
549 continue;
550
551 /* Resolve the device's name. */
552 AudioObjectPropertyAddress propAddrName = { kAudioObjectPropertyName,
553 fIn ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
554 kAudioObjectPropertyElementMaster };
555 uSize = sizeof(CFStringRef);
556 CFStringRef pcfstrName = NULL;
557
558 err = AudioObjectGetPropertyData(curDevID, &propAddrName, 0, NULL, &uSize, &pcfstrName);
559 if (err != kAudioHardwareNoError)
560 continue;
561
562 CFIndex uMax = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pcfstrName), kCFStringEncodingUTF8) + 1;
563 if (uMax)
564 {
565 char *pszName = (char *)RTStrAlloc(uMax);
566 if ( pszName
567 && CFStringGetCString(pcfstrName, pszName, uMax, kCFStringEncodingUTF8))
568 {
569 LogRel2(("CoreAudio: Found %s device '%s'\n", fIn ? "recording" : "playback", pszName));
570 cDevs++;
571 }
572
573 if (pszName)
574 {
575 RTStrFree(pszName);
576 pszName = NULL;
577 }
578 }
579
580 CFRelease(pcfstrName);
581 }
582
583 } while (0);
584
585 if (fIn)
586 LogRel2(("CoreAudio: Found %RU8 recording device(s)\n", cDevs));
587 else
588 LogRel2(("CoreAudio: Found %RU8 playback device(s)\n", cDevs));
589
590 if (pCfg)
591 {
592 if (fIn)
593 pCfg->cSources = cDevs;
594 else
595 pCfg->cSinks = cDevs;
596 }
597
598 LogFlowFuncLeaveRC(rc);
599 return rc;
600}
601
602/**
603 * Updates this host driver's internal status, according to the global, overall input/output
604 * state and all connected (native) audio streams.
605 *
606 * @param pThis Host audio driver instance.
607 * @param pCfg Where to store the backend configuration. Optional.
608 * @param fEnum Enumeration flags.
609 */
610int coreAudioUpdateStatusInternalEx(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
611{
612 RT_NOREF(fEnum);
613 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
614 /* pCfg is optional. */
615
616 PDMAUDIOBACKENDCFG Cfg;
617 RT_ZERO(Cfg);
618
619 Cfg.cbStreamOut = sizeof(COREAUDIOSTREAMOUT);
620 Cfg.cbStreamIn = sizeof(COREAUDIOSTREAMIN);
621 Cfg.cMaxStreamsIn = UINT32_MAX;
622 Cfg.cMaxStreamsOut = UINT32_MAX;
623
624 int rc = coreAudioDevicesEnumerate(pThis, &Cfg, false /* fIn */, 0 /* fEnum */);
625 AssertRC(rc);
626 rc = coreAudioDevicesEnumerate(pThis, &Cfg, true /* fIn */, 0 /* fEnum */);
627 AssertRC(rc);
628
629 if (pCfg)
630 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
631
632 LogFlowFuncLeaveRC(rc);
633 return rc;
634}
635
636
637/**
638 * Implements the OS X callback AudioObjectPropertyListenerProc.
639 */
640static OSStatus drvHostCoreAudioDeviceStateChanged(AudioObjectID propertyID,
641 UInt32 cAddresses,
642 const AudioObjectPropertyAddress paProperties[],
643 void *pvUser)
644{
645 RT_NOREF(propertyID, cAddresses, paProperties)
646 LogFlowFunc(("propertyID=%u cAddresses=%u pvUser=%p\n", propertyID, cAddresses, pvUser));
647
648 PCOREAUDIOSTREAMCBCTX pCbCtx = (PCOREAUDIOSTREAMCBCTX)pvUser;
649 AssertPtr(pCbCtx);
650
651 UInt32 uAlive = 1;
652 UInt32 uSize = sizeof(UInt32);
653
654 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
655 kAudioObjectPropertyElementMaster };
656
657 AudioDeviceID deviceID = pCbCtx->enmDir == PDMAUDIODIR_IN
658 ? pCbCtx->pIn->deviceID : pCbCtx->pOut->deviceID;
659
660 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uAlive);
661
662 bool fIsDead = false;
663
664 if (err == kAudioHardwareBadDeviceError)
665 fIsDead = true; /* Unplugged. */
666 else if ((err == kAudioHardwareNoError) && (!RT_BOOL(uAlive)))
667 fIsDead = true; /* Something else happened. */
668
669 if (fIsDead)
670 {
671 switch (pCbCtx->enmDir)
672 {
673 case PDMAUDIODIR_IN:
674 {
675 PCOREAUDIOSTREAMIN pStreamIn = pCbCtx->pIn;
676
677 /* We move the reinitialization to the next output event.
678 * This make sure this thread isn't blocked and the
679 * reinitialization is done when necessary only. */
680 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
681
682 LogRel(("CoreAudio: Recording device stopped functioning\n"));
683 break;
684 }
685
686 case PDMAUDIODIR_OUT:
687 {
688 PCOREAUDIOSTREAMOUT pStreamOut = pCbCtx->pOut;
689
690 /* We move the reinitialization to the next output event.
691 * This make sure this thread isn't blocked and the
692 * reinitialization is done when necessary only. */
693 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_REINIT);
694
695 LogRel(("CoreAudio: Playback device stopped functioning\n"));
696 break;
697 }
698
699 default:
700 AssertMsgFailed(("Not implemented\n"));
701 break;
702 }
703 }
704
705 int rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, false /* fIn */, 0 /* fEnum */);
706 AssertRC(rc2);
707 rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, true /* fIn */, 0 /* fEnum */);
708 AssertRC(rc2);
709
710 return noErr;
711}
712
713
714/**
715 * Implements the OS X callback AudioObjectPropertyListenerProc for getting
716 * notified when the default recording/playback device has been changed.
717 */
718static OSStatus coreAudioDefaultDeviceChanged(AudioObjectID propertyID,
719 UInt32 cAddresses,
720 const AudioObjectPropertyAddress properties[],
721 void *pvUser)
722{
723 RT_NOREF(propertyID);
724 LogFlowFunc(("propertyID=%u cAddresses=%u pvUser=%p\n", propertyID, cAddresses, pvUser));
725
726 PCOREAUDIOSTREAMCBCTX pCbCtx = (PCOREAUDIOSTREAMCBCTX)pvUser;
727 AssertPtr(pCbCtx);
728
729 OSStatus err = noErr;
730 for (UInt32 idxAddress = 0; idxAddress < cAddresses; idxAddress++)
731 {
732 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress];
733
734 switch (pProperty->mSelector)
735 {
736 case kAudioHardwarePropertyDefaultInputDevice:
737 {
738 PCOREAUDIOSTREAMIN pStreamIn = pCbCtx->pIn;
739 AssertPtr(pStreamIn);
740
741 /* This listener is called on every change of the hardware
742 * device. So check if the default device has really changed. */
743 UInt32 uSize = sizeof(pStreamIn->deviceID);
744 UInt32 uResp;
745 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp);
746
747 if (err == noErr)
748 {
749 if (pStreamIn->deviceID != uResp)
750 {
751 LogRel(("CoreAudio: Default device for recording has changed\n"));
752
753 /* We move the reinitialization to the next input event.
754 * This make sure this thread isn't blocked and the
755 * reinitialization is done when necessary only. */
756 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
757 }
758 }
759 break;
760 }
761
762 case kAudioHardwarePropertyDefaultOutputDevice:
763 {
764 PCOREAUDIOSTREAMOUT pStreamOut = pCbCtx->pOut;
765 AssertPtr(pStreamOut);
766
767 /* This listener is called on every change of the hardware
768 * device. So check if the default device has really changed. */
769 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
770 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
771
772 UInt32 uSize = sizeof(pStreamOut->deviceID);
773 UInt32 uResp;
774 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &uResp);
775
776 if (err == noErr)
777 {
778 if (pStreamOut->deviceID != uResp)
779 {
780 LogRel(("CoreAudio: Default device for playback has changed\n"));
781
782 /* We move the reinitialization to the next input event.
783 * This make sure this thread isn't blocked and the
784 * reinitialization is done when necessary only. */
785 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_REINIT);
786 }
787 }
788 break;
789 }
790
791 default:
792 break;
793 }
794 }
795
796 int rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, false /* fIn */, 0 /* fEnum */);
797 AssertRC(rc2);
798 rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, true /* fIn */, 0 /* fEnum */);
799 AssertRC(rc2);
800
801 /** @todo Implement callback notification here to let the audio connector / device emulation
802 * know that something has changed. */
803
804 return noErr;
805}
806
807
808/**
809 * Implements the OS X callback AudioObjectPropertyListenerProc for getting
810 * notified when some of the properties of an audio device has changed.
811 */
812static OSStatus coreAudioRecordingAudioDevicePropertyChanged(AudioObjectID propertyID,
813 UInt32 cAddresses,
814 const AudioObjectPropertyAddress paProperties[],
815 void *pvUser)
816{
817 RT_NOREF(cAddresses, paProperties);
818 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
819
820 switch (propertyID)
821 {
822#ifdef DEBUG
823 case kAudioDeviceProcessorOverload:
824 {
825 LogFunc(("Processor overload detected!\n"));
826 break;
827 }
828#endif /* DEBUG */
829 case kAudioDevicePropertyNominalSampleRate:
830 {
831 LogRel(("CoreAudio: Recording sample rate changed\n"));
832
833 /* We move the reinitialization to the next input event.
834 * This make sure this thread isn't blocked and the
835 * reinitialization is done when necessary only. */
836 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
837 break;
838 }
839
840 default:
841 break;
842 }
843
844 return noErr;
845}
846
847/**
848 * Implements the OS X callback AudioConverterComplexInputDataProc for
849 * converting audio input data from one format to another.
850 */
851static OSStatus coreAudioConverterCallback(AudioConverterRef inAudioConverter,
852 UInt32 *ioNumberDataPackets,
853 AudioBufferList *ioData,
854 AudioStreamPacketDescription **ppASPD,
855 void *pvUser)
856{
857 RT_NOREF(inAudioConverter, ppASPD);
858 AssertPtrReturn(ioNumberDataPackets, g_caConverterEOFDErr);
859 AssertPtrReturn(ioData, g_caConverterEOFDErr);
860
861 PCOREAUDIOCONVCBCTX pConvCbCtx = (PCOREAUDIOCONVCBCTX)pvUser;
862 AssertPtr(pConvCbCtx);
863
864 /* Initialize values. */
865 ioData->mBuffers[0].mNumberChannels = 0;
866 ioData->mBuffers[0].mDataByteSize = 0;
867 ioData->mBuffers[0].mData = NULL;
868
869 /** @todo Check converter ID? */
870
871 /** @todo Handled non-interleaved data by going through the full buffer list,
872 * not only through the first buffer like we do now. */
873
874 Log3Func(("ioNumberDataPackets=%RU32\n", *ioNumberDataPackets));
875
876 if (pConvCbCtx->uPacketIdx + *ioNumberDataPackets > pConvCbCtx->uPacketCnt)
877 {
878 Log3Func(("Limiting ioNumberDataPackets to %RU32\n", pConvCbCtx->uPacketCnt - pConvCbCtx->uPacketIdx));
879 *ioNumberDataPackets = pConvCbCtx->uPacketCnt - pConvCbCtx->uPacketIdx;
880 }
881
882 if (*ioNumberDataPackets)
883 {
884 Assert(pConvCbCtx->bufLstSrc.mNumberBuffers == 1); /* Only one buffer for the source supported atm. */
885
886 size_t cbOff = pConvCbCtx->uPacketIdx * pConvCbCtx->asbdSrc.mBytesPerPacket;
887
888 size_t cbAvail = RT_MIN(*ioNumberDataPackets * pConvCbCtx->asbdSrc.mBytesPerPacket,
889 pConvCbCtx->bufLstSrc.mBuffers[0].mDataByteSize - cbOff);
890
891 void *pvAvail = (uint8_t *)pConvCbCtx->bufLstSrc.mBuffers[0].mData + cbOff;
892
893 Log3Func(("cbOff=%zu, cbAvail=%zu\n", cbOff, cbAvail));
894
895 /* Set input data for the converter to use.
896 * Note: For VBR (Variable Bit Rates) or interleaved data handling we need multiple buffers here. */
897 ioData->mNumberBuffers = 1;
898
899 ioData->mBuffers[0].mNumberChannels = pConvCbCtx->bufLstSrc.mBuffers[0].mNumberChannels;
900 ioData->mBuffers[0].mDataByteSize = cbAvail;
901 ioData->mBuffers[0].mData = pvAvail;
902
903#ifdef DEBUG_DUMP_PCM_DATA
904 RTFILE fh;
905 int rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-converter-cb-input.pcm",
906 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
907 if (RT_SUCCESS(rc))
908 {
909 RTFileWrite(fh, pvAvail, cbAvail, NULL);
910 RTFileClose(fh);
911 }
912 else
913 AssertFailed();
914#endif
915
916 pConvCbCtx->uPacketIdx += *ioNumberDataPackets;
917 Assert(pConvCbCtx->uPacketIdx <= pConvCbCtx->uPacketCnt);
918 }
919
920 Log3Func(("%RU32 / %RU32 -> ioNumberDataPackets=%RU32\n",
921 pConvCbCtx->uPacketIdx, pConvCbCtx->uPacketCnt, *ioNumberDataPackets));
922
923 return noErr;
924}
925
926/**
927 * Implements the OS X callback AURenderCallback in order to feed the audio
928 * input buffer.
929 */
930static OSStatus coreAudioRecordingCb(void *pvUser,
931 AudioUnitRenderActionFlags *pActionFlags,
932 const AudioTimeStamp *pAudioTS,
933 UInt32 uBusID,
934 UInt32 cFrames,
935 AudioBufferList *pBufData)
936{
937 RT_NOREF(pBufData);
938
939 /* If nothing is pending return immediately. */
940 if (cFrames == 0)
941 return noErr;
942
943 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
944
945 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
946 return noErr;
947
948 PCOREAUDIOCONVCBCTX pConvCbCtx = &pStreamIn->convCbCtx;
949
950 OSStatus err = noErr;
951 int rc = VINF_SUCCESS;
952
953 Assert(pConvCbCtx->bufLstSrc.mNumberBuffers == 1);
954 AudioBuffer *pSrcBuf = &pConvCbCtx->bufLstSrc.mBuffers[0];
955
956 pConvCbCtx->uPacketCnt = cFrames / pConvCbCtx->asbdSrc.mFramesPerPacket;
957 pConvCbCtx->uPacketIdx = 0;
958
959 AudioConverterReset(pStreamIn->pConverter);
960
961 Log3Func(("cFrames=%RU32 (%RU32 frames per packet) -> %RU32 packets\n",
962 cFrames, pConvCbCtx->asbdSrc.mFramesPerPacket, pConvCbCtx->uPacketCnt));
963
964 do
965 {
966 /* Are we using a converter? */
967 if (pStreamIn->pConverter)
968 {
969 pSrcBuf->mNumberChannels = pConvCbCtx->asbdSrc.mChannelsPerFrame;
970 pSrcBuf->mDataByteSize = pConvCbCtx->asbdSrc.mBytesPerFrame * cFrames;
971 pSrcBuf->mData = RTMemAllocZ(pSrcBuf->mDataByteSize);
972 if (!pSrcBuf->mData)
973 {
974 rc = VERR_NO_MEMORY;
975 break;
976 }
977
978 /* First, render the source data as usual. */
979 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pConvCbCtx->bufLstSrc);
980 if (err != noErr)
981 {
982 LogRel2(("CoreAudio: Failed rendering converted audio input data (%RI32)\n", err));
983 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
984 break;
985 }
986
987 Log3Func(("cbSrcBufSize=%RU32\n", pSrcBuf->mDataByteSize));
988
989#ifdef DEBUG_DUMP_PCM_DATA
990 RTFILE fh;
991 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-src.pcm",
992 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
993 if (RT_SUCCESS(rc))
994 {
995 RTFileWrite(fh, pSrcBuf->mData, pSrcBuf->mDataByteSize, NULL);
996 RTFileClose(fh);
997 }
998 else
999 AssertFailed();
1000#endif
1001 AudioBufferList dstBufList;
1002
1003 dstBufList.mNumberBuffers = 1; /* We only use one buffer at once. */
1004
1005 AudioBuffer *pDstBuf = &dstBufList.mBuffers[0];
1006 pDstBuf->mDataByteSize = pConvCbCtx->asbdDst.mBytesPerFrame * cFrames;
1007 pDstBuf->mData = RTMemAllocZ(pDstBuf->mDataByteSize);
1008 if (!pDstBuf->mData)
1009 {
1010 rc = VERR_NO_MEMORY;
1011 break;
1012 }
1013
1014 UInt32 cPacketsToWriteAndWritten = pConvCbCtx->uPacketCnt;
1015 Assert(cPacketsToWriteAndWritten);
1016
1017 Log3Func(("cPacketsToWrite=%RU32\n", cPacketsToWriteAndWritten));
1018
1019 do
1020 {
1021 err = AudioConverterFillComplexBuffer(pStreamIn->pConverter,
1022 coreAudioConverterCallback, pConvCbCtx /* pvData */,
1023 &cPacketsToWriteAndWritten, &dstBufList, NULL);
1024
1025 Log3Func(("cPacketsWritten=%RU32 (%zu bytes), err=%RI32\n",
1026 cPacketsToWriteAndWritten, cPacketsToWriteAndWritten * pConvCbCtx->asbdDst.mBytesPerPacket, err));
1027
1028 if (err != noErr)
1029 {
1030 LogFlowFunc(("Failed to convert audio data (%RI32:%c%c%c%c)\n", err,
1031 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
1032 rc = VERR_IO_GEN_FAILURE;
1033 break;
1034 }
1035
1036 if (cPacketsToWriteAndWritten == 0)
1037 break;
1038
1039 size_t cbDst = cPacketsToWriteAndWritten * pConvCbCtx->asbdDst.mBytesPerPacket;
1040
1041#ifdef DEBUG_DUMP_PCM_DATA
1042 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-dst.pcm",
1043 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
1044 if (RT_SUCCESS(rc))
1045 {
1046 RTFileWrite(fh, pDstBuf->mData, cbDst, NULL);
1047 RTFileClose(fh);
1048 }
1049 else
1050 AssertFailed();
1051#endif
1052 size_t cbFree = RTCircBufFree(pStreamIn->pCircBuf);
1053 if (cbFree < cbDst)
1054 {
1055 LogRel2(("CoreAudio: Recording is lagging behind (%zu bytes available but only %zu bytes free)\n",
1056 cbDst, cbFree));
1057 break;
1058 }
1059
1060 size_t cbDstChunk;
1061 void *puDst;
1062 RTCircBufAcquireWriteBlock(pStreamIn->pCircBuf, cbDst, (void **)&puDst, &cbDstChunk);
1063
1064 if (cbDstChunk)
1065 memcpy(puDst, pDstBuf->mData, cbDstChunk);
1066
1067 RTCircBufReleaseWriteBlock(pStreamIn->pCircBuf, cbDstChunk);
1068
1069 } while (1);
1070
1071 if (pDstBuf->mData)
1072 {
1073 RTMemFree(pDstBuf->mData);
1074 pDstBuf->mData = NULL;
1075 }
1076 }
1077 else /* No converter being used. */
1078 {
1079 AssertBreakStmt(pStreamIn->streamFormat.mChannelsPerFrame >= 1, rc = VERR_INVALID_PARAMETER);
1080 AssertBreakStmt(pStreamIn->streamFormat.mBytesPerFrame >= 1, rc = VERR_INVALID_PARAMETER);
1081
1082 AssertBreakStmt(pSrcBuf->mNumberChannels, rc = VERR_INVALID_PARAMETER);
1083
1084 pSrcBuf->mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
1085 pSrcBuf->mDataByteSize = pStreamIn->streamFormat.mBytesPerFrame * cFrames;
1086 pSrcBuf->mData = RTMemAlloc(pSrcBuf->mDataByteSize);
1087 if (!pSrcBuf->mData)
1088 {
1089 rc = VERR_NO_MEMORY;
1090 break;
1091 }
1092
1093 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pConvCbCtx->bufLstSrc);
1094 if (err != noErr)
1095 {
1096 LogRel2(("CoreAudio: Failed rendering non-coverted audio input data (%RI32)\n", err));
1097 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
1098 break;
1099 }
1100
1101 const uint32_t cbDataSize = pSrcBuf->mDataByteSize;
1102 const size_t cbBufFree = RTCircBufFree(pStreamIn->pCircBuf);
1103 size_t cbAvail = RT_MIN(cbDataSize, cbBufFree);
1104
1105 Log3Func(("cbDataSize=%RU32, cbBufFree=%zu, cbAvail=%zu\n", cbDataSize, cbBufFree, cbAvail));
1106
1107 /* Iterate as long as data is available. */
1108 uint8_t *puDst = NULL;
1109 uint32_t cbWrittenTotal = 0;
1110 while (cbAvail)
1111 {
1112 /* Try to acquire the necessary space from the ring buffer. */
1113 size_t cbToWrite = 0;
1114 RTCircBufAcquireWriteBlock(pStreamIn->pCircBuf, cbAvail, (void **)&puDst, &cbToWrite);
1115 if (!cbToWrite)
1116 break;
1117
1118 /* Copy the data from the Core Audio buffer to the ring buffer. */
1119 memcpy(puDst, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite);
1120
1121 /* Release the ring buffer, so the main thread could start reading this data. */
1122 RTCircBufReleaseWriteBlock(pStreamIn->pCircBuf, cbToWrite);
1123
1124 cbWrittenTotal += cbToWrite;
1125
1126 Assert(cbAvail >= cbToWrite);
1127 cbAvail -= cbToWrite;
1128 }
1129
1130 Log3Func(("cbWrittenTotal=%RU32, cbLeft=%zu\n", cbWrittenTotal, cbAvail));
1131 }
1132
1133 } while (0);
1134
1135 if (pSrcBuf->mData)
1136 {
1137 RTMemFree(pSrcBuf->mData);
1138 pSrcBuf->mData = NULL;
1139 }
1140
1141 return err;
1142}
1143
1144#define CA_BREAK_STMT(stmt) if (true) \
1145 { \
1146 stmt; \
1147 break; \
1148 } else do { } while (0)
1149
1150/** @todo Eventually split up this function, as this already is huge! */
1151static int coreAudioInitIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream,
1152 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1153{
1154 RT_NOREF(pThis);
1155
1156 int rc = VINF_SUCCESS;
1157
1158 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
1159 UInt32 cSamples = 0;
1160
1161 OSStatus err = noErr;
1162 UInt32 uSize = 0;
1163
1164 AudioDeviceID deviceID = pStreamIn->deviceID;
1165 if (deviceID == kAudioDeviceUnknown)
1166 {
1167 /* Fetch the default audio recording device currently in use. */
1168 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice,
1169 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1170 uSize = sizeof(deviceID);
1171 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &deviceID);
1172 if (err != noErr)
1173 {
1174 LogFlowFunc(("CoreAudio: Unable to determine default recording device (%RI32)\n", err));
1175 return VERR_AUDIO_NO_FREE_INPUT_STREAMS;
1176 }
1177 }
1178
1179 if (deviceID == kAudioDeviceUnknown)
1180 {
1181 LogFlowFunc(("No default recording device found\n"));
1182 return VERR_AUDIO_NO_FREE_INPUT_STREAMS;
1183 }
1184
1185 do
1186 {
1187 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_INIT);
1188
1189 /* Assign device ID. */
1190 pStreamIn->deviceID = deviceID;
1191
1192 /*
1193 * Try to get the name of the recording device and log it. It's not fatal if it fails.
1194 */
1195 CFStringRef strTemp;
1196
1197 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
1198 kAudioObjectPropertyElementMaster };
1199 uSize = sizeof(CFStringRef);
1200 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1201 if (err == noErr)
1202 {
1203 char *pszDevName = NULL;
1204 err = coreAudioCFStringToCString(strTemp, &pszDevName);
1205 if (err == noErr)
1206 {
1207 CFRelease(strTemp);
1208
1209 /* Get the device' UUID. */
1210 propAdr.mSelector = kAudioDevicePropertyDeviceUID;
1211 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1212 if (err == noErr)
1213 {
1214 char *pszUID = NULL;
1215
1216 err = coreAudioCFStringToCString(strTemp, &pszUID);
1217 if (err == noErr)
1218 {
1219 CFRelease(strTemp);
1220 LogRel(("CoreAudio: Using recording device: %s (UID: %s)\n", pszDevName, pszUID));
1221
1222 RTMemFree(pszUID);
1223 }
1224 }
1225
1226 RTMemFree(pszDevName);
1227 }
1228 }
1229 else
1230 {
1231 /* This is not fatal, can happen for some Macs. */
1232 LogRel2(("CoreAudio: Unable to determine recording device name (%RI32)\n", err));
1233 }
1234
1235 /* Get the default frames buffer size, so that we can setup our internal buffers. */
1236 UInt32 cFrames;
1237 uSize = sizeof(cFrames);
1238 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
1239 propAdr.mScope = kAudioDevicePropertyScopeInput;
1240 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
1241 if (err != noErr)
1242 {
1243 /* Can happen if no recording device is available by default. Happens on some Macs,
1244 * so don't log this by default to not scare people. */
1245 LogRel2(("CoreAudio: Failed to determine frame buffer size of the audio recording device (%RI32)\n", err));
1246 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1247 }
1248
1249 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
1250 err = coreAudioSetFrameBufferSize(pStreamIn->deviceID, true /* fInput */, cFrames, &cFrames);
1251 if (err != noErr)
1252 {
1253 LogRel(("CoreAudio: Failed to set frame buffer size for the audio recording device (%RI32)\n", err));
1254 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1255 }
1256
1257 LogFlowFunc(("cFrames=%RU32\n", cFrames));
1258
1259 /* Try to find the default HAL output component. */
1260 AudioComponentDescription cd;
1261
1262 RT_ZERO(cd);
1263 cd.componentType = kAudioUnitType_Output;
1264 cd.componentSubType = kAudioUnitSubType_HALOutput;
1265 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
1266
1267 AudioComponent cp = AudioComponentFindNext(NULL, &cd);
1268 if (cp == 0)
1269 {
1270 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
1271 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1272 }
1273
1274 /* Open the default HAL output component. */
1275 err = AudioComponentInstanceNew(cp, &pStreamIn->audioUnit);
1276 if (err != noErr)
1277 {
1278 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
1279 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1280 }
1281
1282 /* Switch the I/O mode for input to on. */
1283 UInt32 uFlag = 1;
1284 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
1285 1, &uFlag, sizeof(uFlag));
1286 if (err != noErr)
1287 {
1288 LogRel(("CoreAudio: Failed to disable input I/O mode for input stream (%RI32)\n", err));
1289 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1290 }
1291
1292 /* Switch the I/O mode for output to off. This is important, as this is a pure input stream. */
1293 uFlag = 0;
1294 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
1295 0, &uFlag, sizeof(uFlag));
1296 if (err != noErr)
1297 {
1298 LogRel(("CoreAudio: Failed to disable output I/O mode for input stream (%RI32)\n", err));
1299 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1300 }
1301
1302 /* Set the default audio recording device as the device for the new AudioUnit. */
1303 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
1304 0, &pStreamIn->deviceID, sizeof(pStreamIn->deviceID));
1305 if (err != noErr)
1306 {
1307 LogRel(("CoreAudio: Failed to set current device (%RI32)\n", err));
1308 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1309 }
1310
1311 /*
1312 * CoreAudio will inform us on a second thread for new incoming audio data.
1313 * Therefore register a callback function which will process the new data.
1314 */
1315 AURenderCallbackStruct cb;
1316 RT_ZERO(cb);
1317 cb.inputProc = coreAudioRecordingCb;
1318 cb.inputProcRefCon = pStreamIn;
1319
1320 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,
1321 0, &cb, sizeof(cb));
1322 if (err != noErr)
1323 {
1324 LogRel(("CoreAudio: Failed to register input callback (%RI32)\n", err));
1325 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1326 }
1327
1328 /* Fetch the current stream format of the device. */
1329 RT_ZERO(pStreamIn->deviceFormat);
1330 uSize = sizeof(pStreamIn->deviceFormat);
1331 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1332 1, &pStreamIn->deviceFormat, &uSize);
1333 if (err != noErr)
1334 {
1335 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
1336 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1337 }
1338
1339 /* Create an AudioStreamBasicDescription based on our required audio settings. */
1340 RT_ZERO(pStreamIn->streamFormat);
1341 coreAudioStreamCfgToASBD(pCfgReq, &pStreamIn->streamFormat);
1342
1343 coreAudioPrintASBD("Recording device", &pStreamIn->deviceFormat);
1344 coreAudioPrintASBD("Recording stream", &pStreamIn->streamFormat);
1345
1346 /* If the frequency of the device is different from the requested one we
1347 * need a converter. The same count if the number of channels is different. */
1348 if ( pStreamIn->deviceFormat.mSampleRate != pStreamIn->streamFormat.mSampleRate
1349 || pStreamIn->deviceFormat.mChannelsPerFrame != pStreamIn->streamFormat.mChannelsPerFrame)
1350 {
1351 LogRel2(("CoreAudio: Input converter is active\n"));
1352
1353 err = AudioConverterNew(&pStreamIn->deviceFormat, &pStreamIn->streamFormat, &pStreamIn->pConverter);
1354 if (RT_UNLIKELY(err != noErr))
1355 {
1356 LogRel(("CoreAudio: Failed to create the audio converter (%RI32)\n", err));
1357 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1358 }
1359
1360 if ( pStreamIn->deviceFormat.mChannelsPerFrame == 1 /* Mono */
1361 && pStreamIn->streamFormat.mChannelsPerFrame == 2 /* Stereo */)
1362 {
1363 LogRel2(("CoreAudio: Mono to stereo conversion active\n"));
1364
1365 /*
1366 * If the channel count is different we have to tell this the converter
1367 * and supply a channel mapping. For now we only support mapping
1368 * from mono to stereo. For all other cases the core audio defaults
1369 * are used, which means dropping additional channels in most
1370 * cases.
1371 */
1372 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo. */
1373
1374 err = AudioConverterSetProperty(pStreamIn->pConverter, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
1375 if (err != noErr)
1376 {
1377 LogRel(("CoreAudio: Failed to set channel mapping (mono -> stereo) for the audio input converter (%RI32)\n", err));
1378 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1379 }
1380 }
1381
1382 /* Set sample rate converter quality to maximum. */
1383 uFlag = kAudioConverterQuality_Max;
1384 err = AudioConverterSetProperty(pStreamIn->pConverter, kAudioConverterSampleRateConverterQuality,
1385 sizeof(uFlag), &uFlag);
1386 if (err != noErr)
1387 LogRel2(("CoreAudio: Failed to set input audio converter quality to the maximum (%RI32)\n", err));
1388
1389 uSize = sizeof(UInt32);
1390 UInt32 maxOutputSize;
1391 err = AudioConverterGetProperty(pStreamIn->pConverter, kAudioConverterPropertyMaximumOutputPacketSize,
1392 &uSize, &maxOutputSize);
1393 if (RT_UNLIKELY(err != noErr))
1394 {
1395 LogRel(("CoreAudio: Failed to retrieve converter's maximum output size (%RI32)\n", err));
1396 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1397 }
1398
1399 LogFunc(("Maximum converter packet output size is: %RI32\n", maxOutputSize));
1400
1401 /* Set the input (source) format, that is, the format the device is recording data with. */
1402 err = AudioUnitSetProperty(pStreamIn->audioUnit,
1403 kAudioUnitProperty_StreamFormat,
1404 kAudioUnitScope_Input,
1405 1,
1406 &pStreamIn->deviceFormat,
1407 sizeof(pStreamIn->deviceFormat));
1408 if (RT_UNLIKELY(err != noErr))
1409 {
1410 LogRel(("CoreAudio: Failed to set device input format (%RI32)\n", err));
1411 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1412 }
1413
1414 /* Set the output (target) format, that is, the format we created the input stream with. */
1415 err = AudioUnitSetProperty(pStreamIn->audioUnit,
1416 kAudioUnitProperty_StreamFormat,
1417 kAudioUnitScope_Output,
1418 1,
1419 &pStreamIn->deviceFormat,
1420 sizeof(pStreamIn->deviceFormat));
1421 if (RT_UNLIKELY(err != noErr))
1422 {
1423 LogRel(("CoreAudio: Failed to set stream output format (%RI32)\n", err));
1424 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1425 }
1426 }
1427 else
1428 {
1429 /* Set the new output format description for the input stream. */
1430 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1431 1, &pStreamIn->streamFormat, sizeof(pStreamIn->streamFormat));
1432 if (err != noErr)
1433 {
1434 LogRel(("CoreAudio: Failed to set output format for input stream (%RI32)\n", err));
1435 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1436 }
1437 }
1438
1439 /*
1440 * Also set the frame buffer size off the device on our AudioUnit. This
1441 * should make sure that the frames count which we receive in the render
1442 * thread is as we like.
1443 */
1444 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1445 1, &cFrames, sizeof(cFrames));
1446 if (err != noErr)
1447 {
1448 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err));
1449 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1450 }
1451
1452 /* Finally initialize the new AudioUnit. */
1453 err = AudioUnitInitialize(pStreamIn->audioUnit);
1454 if (err != noErr)
1455 {
1456 LogRel(("CoreAudio: Failed to initialize audio unit for input stream (%RI32)\n", err));
1457 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1458 }
1459
1460 uSize = sizeof(pStreamIn->deviceFormat);
1461 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1462 1, &pStreamIn->deviceFormat, &uSize);
1463 if (err != noErr)
1464 {
1465 LogRel(("CoreAudio: Failed to get recording device format (%RI32)\n", err));
1466 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1467 }
1468
1469 /*
1470 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
1471 * the frame buffer size set in the previous calls. So finally get the
1472 * frame buffer size after the AudioUnit was initialized.
1473 */
1474 uSize = sizeof(cFrames);
1475 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1476 0, &cFrames, &uSize);
1477 if (err != noErr)
1478 {
1479 LogRel(("CoreAudio: Failed to get maximum frame buffer size from input audio device (%RI32)\n", err));
1480 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1481 }
1482
1483 /* Destroy any former internal ring buffer. */
1484 if (pStreamIn->pCircBuf)
1485 {
1486 RTCircBufDestroy(pStreamIn->pCircBuf);
1487 pStreamIn->pCircBuf = NULL;
1488 }
1489
1490 coreAudioUninitConvCbCtx(&pStreamIn->convCbCtx);
1491
1492 /* Calculate the ratio between the device and the stream sample rate. */
1493 pStreamIn->sampleRatio = pStreamIn->streamFormat.mSampleRate / pStreamIn->deviceFormat.mSampleRate;
1494
1495 /*
1496 * Make sure that the ring buffer is big enough to hold the recording
1497 * data. Compare the maximum frames per slice value with the frames
1498 * necessary when using the converter where the sample rate could differ.
1499 * The result is always multiplied by the channels per frame to get the
1500 * samples count.
1501 */
1502 cSamples = RT_MAX(cFrames,
1503 (cFrames * pStreamIn->deviceFormat.mBytesPerFrame * pStreamIn->sampleRatio)
1504 / pStreamIn->streamFormat.mBytesPerFrame)
1505 * pStreamIn->streamFormat.mChannelsPerFrame;
1506 if (!cSamples)
1507 {
1508 LogRel(("CoreAudio: Failed to determine samples buffer count input stream\n"));
1509 CA_BREAK_STMT(rc = VERR_INVALID_PARAMETER);
1510 }
1511
1512 PDMAUDIOPCMPROPS PCMProps;
1513 int rc2 = DrvAudioHlpStreamCfgToProps(pCfgAcq, &PCMProps);
1514 AssertRC(rc2);
1515
1516 rc = RTCircBufCreate(&pStreamIn->pCircBuf, cSamples << PCMProps.cShift);
1517 if (RT_FAILURE(rc))
1518 break;
1519
1520 /* Init the converter callback context. */
1521 rc = coreAudioInitConvCbCtx(&pStreamIn->convCbCtx,
1522 &pStreamIn->deviceFormat /* Source */, &pStreamIn->streamFormat /* Dest */);
1523
1524 if (RT_SUCCESS(rc))
1525 {
1526#ifdef DEBUG
1527 propAdr.mSelector = kAudioDeviceProcessorOverload;
1528 propAdr.mScope = kAudioUnitScope_Global;
1529 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
1530 coreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
1531 if (RT_UNLIKELY(err != noErr))
1532 LogRel2(("CoreAudio: Failed to add the processor overload listener for input stream (%RI32)\n", err));
1533#endif /* DEBUG */
1534 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1535 propAdr.mScope = kAudioUnitScope_Global;
1536 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
1537 coreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
1538 /* Not fatal. */
1539 if (RT_UNLIKELY(err != noErr))
1540 LogRel2(("CoreAudio: Failed to register sample rate changed listener for input stream (%RI32)\n", err));
1541 }
1542
1543 } while (0);
1544
1545 if (RT_SUCCESS(rc))
1546 {
1547 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_INIT);
1548
1549 LogFunc(("cSamples=%RU32\n", cSamples));
1550
1551 if (pCfgAcq)
1552 pCfgAcq->cSampleBufferSize = cSamples;
1553 }
1554 else
1555 {
1556 AudioUnitUninitialize(pStreamIn->audioUnit);
1557
1558 if (pStreamIn->pCircBuf)
1559 {
1560 RTCircBufDestroy(pStreamIn->pCircBuf);
1561 pStreamIn->pCircBuf = NULL;
1562 }
1563
1564 coreAudioUninitConvCbCtx(&pStreamIn->convCbCtx);
1565
1566 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
1567 }
1568
1569 LogFunc(("rc=%Rrc\n", rc));
1570 return rc;
1571}
1572
1573/** @todo Eventually split up this function, as this already is huge! */
1574static int coreAudioInitOut(PDRVHOSTCOREAUDIO pThis,
1575 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1576{
1577 RT_NOREF(pThis);
1578
1579 int rc = VINF_SUCCESS;
1580
1581 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
1582 UInt32 cSamples = 0;
1583
1584 OSStatus err = noErr;
1585 UInt32 uSize = 0;
1586
1587 AudioDeviceID deviceID = pStreamOut->deviceID;
1588 if (deviceID == kAudioDeviceUnknown)
1589 {
1590 /* Fetch the default audio recording device currently in use. */
1591 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
1592 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
1593 uSize = sizeof(deviceID);
1594 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &deviceID);
1595 if (err != noErr)
1596 {
1597 LogRel(("CoreAudio: Unable to determine default playback device (%RI32)\n", err));
1598 return VERR_NOT_FOUND;
1599 }
1600 }
1601
1602 if (deviceID == kAudioDeviceUnknown)
1603 {
1604 LogFlowFunc(("No default playback device found\n"));
1605 return VERR_NOT_FOUND;
1606 }
1607
1608 do
1609 {
1610 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_INIT);
1611
1612 /* Assign device ID. */
1613 pStreamOut->deviceID = deviceID;
1614
1615 /*
1616 * Try to get the name of the playback device and log it. It's not fatal if it fails.
1617 */
1618 CFStringRef strTemp;
1619
1620 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
1621 kAudioObjectPropertyElementMaster };
1622 uSize = sizeof(CFStringRef);
1623 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1624 if (err == noErr)
1625 {
1626 char *pszDevName = NULL;
1627 err = coreAudioCFStringToCString(strTemp, &pszDevName);
1628 if (err == noErr)
1629 {
1630 CFRelease(strTemp);
1631
1632 /* Get the device' UUID. */
1633 propAdr.mSelector = kAudioDevicePropertyDeviceUID;
1634 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
1635 if (err == noErr)
1636 {
1637 char *pszUID = NULL;
1638 err = coreAudioCFStringToCString(strTemp, &pszUID);
1639 if (err == noErr)
1640 {
1641 CFRelease(strTemp);
1642 LogRel(("CoreAudio: Using playback device: %s (UID: %s)\n", pszDevName, pszUID));
1643
1644 RTMemFree(pszUID);
1645 }
1646 }
1647
1648 RTMemFree(pszDevName);
1649 }
1650 }
1651 else
1652 LogRel(("CoreAudio: Unable to determine playback device name (%RI32)\n", err));
1653
1654 /* Get the default frames buffer size, so that we can setup our internal buffers. */
1655 UInt32 cFrames;
1656 uSize = sizeof(cFrames);
1657 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
1658 propAdr.mScope = kAudioDevicePropertyScopeInput;
1659 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
1660 if (err != noErr)
1661 {
1662 LogRel(("CoreAudio: Failed to determine frame buffer size of the audio playback device (%RI32)\n", err));
1663 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1664 }
1665
1666 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
1667 err = coreAudioSetFrameBufferSize(pStreamOut->deviceID, false /* fInput */, cFrames, &cFrames);
1668 if (err != noErr)
1669 {
1670 LogRel(("CoreAudio: Failed to set frame buffer size for the audio playback device (%RI32)\n", err));
1671 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1672 }
1673
1674 /* Try to find the default HAL output component. */
1675 AudioComponentDescription cd;
1676 RT_ZERO(cd);
1677
1678 cd.componentType = kAudioUnitType_Output;
1679 cd.componentSubType = kAudioUnitSubType_HALOutput;
1680 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
1681
1682 AudioComponent cp = AudioComponentFindNext(NULL, &cd);
1683 if (cp == 0)
1684 {
1685 LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
1686 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1687 }
1688
1689 /* Open the default HAL output component. */
1690 err = AudioComponentInstanceNew(cp, &pStreamOut->audioUnit);
1691 if (err != noErr)
1692 {
1693 LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
1694 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1695 }
1696
1697 /* Switch the I/O mode for output to on. */
1698 UInt32 uFlag = 1;
1699 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
1700 0, &uFlag, sizeof(uFlag));
1701 if (err != noErr)
1702 {
1703 LogRel(("CoreAudio: Failed to disable I/O mode for output stream (%RI32)\n", err));
1704 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1705 }
1706
1707 /* Set the default audio playback device as the device for the new AudioUnit. */
1708 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
1709 0, &pStreamOut->deviceID, sizeof(pStreamOut->deviceID));
1710 if (err != noErr)
1711 {
1712 LogRel(("CoreAudio: Failed to set current device for output stream (%RI32)\n", err));
1713 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1714 }
1715
1716 /*
1717 * CoreAudio will inform us on a second thread for new incoming audio data.
1718 * Therefor register a callback function which will process the new data.
1719 */
1720 AURenderCallbackStruct cb;
1721 RT_ZERO(cb);
1722 cb.inputProc = coreAudioPlaybackCb; /* pvUser */
1723 cb.inputProcRefCon = pStreamOut;
1724
1725 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1726 0, &cb, sizeof(cb));
1727 if (err != noErr)
1728 {
1729 LogRel(("CoreAudio: Failed to register output callback (%RI32)\n", err));
1730 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1731 }
1732
1733 /* Fetch the current stream format of the device. */
1734 uSize = sizeof(pStreamOut->deviceFormat);
1735 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1736 0, &pStreamOut->deviceFormat, &uSize);
1737 if (err != noErr)
1738 {
1739 LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
1740 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1741 }
1742
1743 /* Create an AudioStreamBasicDescription based on our required audio settings. */
1744 coreAudioStreamCfgToASBD(pCfgReq, &pStreamOut->streamFormat);
1745
1746 coreAudioPrintASBD("Playback device", &pStreamOut->deviceFormat);
1747 coreAudioPrintASBD("Playback format", &pStreamOut->streamFormat);
1748
1749 /* Set the new output format description for the stream. */
1750 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1751 0, &pStreamOut->streamFormat, sizeof(pStreamOut->streamFormat));
1752 if (err != noErr)
1753 {
1754 LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err));
1755 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1756 }
1757
1758 uSize = sizeof(pStreamOut->deviceFormat);
1759 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1760 0, &pStreamOut->deviceFormat, &uSize);
1761 if (err != noErr)
1762 {
1763 LogRel(("CoreAudio: Failed to retrieve device format for output stream (%RI32)\n", err));
1764 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1765 }
1766
1767 /*
1768 * Also set the frame buffer size off the device on our AudioUnit. This
1769 * should make sure that the frames count which we receive in the render
1770 * thread is as we like.
1771 */
1772 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1773 0, &cFrames, sizeof(cFrames));
1774 if (err != noErr)
1775 {
1776 LogRel(("CoreAudio: Failed to set maximum frame buffer size for output AudioUnit (%RI32)\n", err));
1777 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1778 }
1779
1780 /* Finally initialize the new AudioUnit. */
1781 err = AudioUnitInitialize(pStreamOut->audioUnit);
1782 if (err != noErr)
1783 {
1784 LogRel(("CoreAudio: Failed to initialize the output audio device (%RI32)\n", err));
1785 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1786 }
1787
1788 /*
1789 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
1790 * the frame buffer size set in the previous calls. So finally get the
1791 * frame buffer size after the AudioUnit was initialized.
1792 */
1793 uSize = sizeof(cFrames);
1794 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
1795 0, &cFrames, &uSize);
1796 if (err != noErr)
1797 {
1798 LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err));
1799
1800 AudioUnitUninitialize(pStreamOut->audioUnit);
1801 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);
1802 }
1803
1804 /*
1805 * Make sure that the ring buffer is big enough to hold the recording
1806 * data. Compare the maximum frames per slice value with the frames
1807 * necessary when using the converter where the sample rate could differ.
1808 * The result is always multiplied by the channels per frame to get the
1809 * samples count.
1810 */
1811 cSamples = cFrames * pStreamOut->streamFormat.mChannelsPerFrame;
1812 if (!cSamples)
1813 {
1814 LogRel(("CoreAudio: Failed to determine samples buffer count output stream\n"));
1815 CA_BREAK_STMT(rc = VERR_INVALID_PARAMETER);
1816 }
1817
1818 /* Destroy any former internal ring buffer. */
1819 if (pStreamOut->pCircBuf)
1820 {
1821 RTCircBufDestroy(pStreamOut->pCircBuf);
1822 pStreamOut->pCircBuf = NULL;
1823 }
1824
1825 PDMAUDIOPCMPROPS PCMProps;
1826 int rc2 = DrvAudioHlpStreamCfgToProps(pCfgAcq, &PCMProps);
1827 AssertRC(rc2);
1828
1829 rc = RTCircBufCreate(&pStreamOut->pCircBuf, cSamples << PCMProps.cShift);
1830 if (RT_FAILURE(rc))
1831 break;
1832
1833 /*
1834 * Register callbacks.
1835 */
1836#ifdef DEBUG
1837 propAdr.mSelector = kAudioDeviceProcessorOverload;
1838 propAdr.mScope = kAudioUnitScope_Global;
1839 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1840 coreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1841 if (err != noErr)
1842 LogRel(("CoreAudio: Failed to register processor overload listener for output stream (%RI32)\n", err));
1843#endif /* DEBUG */
1844
1845 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
1846 propAdr.mScope = kAudioUnitScope_Global;
1847 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
1848 coreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
1849 /* Not fatal. */
1850 if (err != noErr)
1851 LogRel(("CoreAudio: Failed to register sample rate changed listener for output stream (%RI32)\n", err));
1852
1853 } while (0);
1854
1855 if (RT_SUCCESS(rc))
1856 {
1857 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_INIT);
1858
1859 LogFunc(("cSamples=%RU32\n", cSamples));
1860
1861 if (pCfgAcq)
1862 pCfgAcq->cSampleBufferSize = cSamples;
1863 }
1864 else
1865 {
1866 AudioUnitUninitialize(pStreamOut->audioUnit);
1867
1868 if (pStreamOut->pCircBuf)
1869 {
1870 RTCircBufDestroy(pStreamOut->pCircBuf);
1871 pStreamOut->pCircBuf = NULL;
1872 }
1873
1874 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
1875 }
1876
1877 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
1878 return rc;
1879}
1880
1881
1882/**
1883 * Implements the OS X callback AudioObjectPropertyListenerProc for getting
1884 * notified when some of the properties of an audio device has changed.
1885 */
1886static OSStatus coreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID,
1887 UInt32 cAddresses,
1888 const AudioObjectPropertyAddress paProperties[],
1889 void *pvUser)
1890{
1891 RT_NOREF(propertyID, cAddresses, paProperties, pvUser)
1892
1893 switch (propertyID)
1894 {
1895#ifdef DEBUG
1896#endif /* DEBUG */
1897 default:
1898 break;
1899 }
1900
1901 return noErr;
1902}
1903
1904
1905/**
1906 * Implements the OS X callback AURenderCallback in order to feed the audio
1907 * output buffer.
1908 */
1909static OSStatus coreAudioPlaybackCb(void *pvUser,
1910 AudioUnitRenderActionFlags *pActionFlags,
1911 const AudioTimeStamp *pAudioTS,
1912 UInt32 uBusID,
1913 UInt32 cFrames,
1914 AudioBufferList *pBufData)
1915{
1916 RT_NOREF(pActionFlags, pAudioTS, uBusID, cFrames);
1917 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pvUser;
1918
1919 if (ASMAtomicReadU32(&pStreamOut->status) != CA_STATUS_INIT)
1920 {
1921 pBufData->mBuffers[0].mDataByteSize = 0;
1922 return noErr;
1923 }
1924
1925 /* How much space is used in the ring buffer? */
1926 size_t cbToRead = RT_MIN(RTCircBufUsed(pStreamOut->pCircBuf), pBufData->mBuffers[0].mDataByteSize);
1927 if (!cbToRead)
1928 {
1929 pBufData->mBuffers[0].mDataByteSize = 0;
1930 return noErr;
1931 }
1932
1933 uint8_t *pbSrc = NULL;
1934 size_t cbRead = 0;
1935
1936 size_t cbLeft = cbToRead;
1937 while (cbLeft)
1938 {
1939 /* Try to acquire the necessary block from the ring buffer. */
1940 RTCircBufAcquireReadBlock(pStreamOut->pCircBuf, cbLeft, (void **)&pbSrc, &cbToRead);
1941
1942 /* Break if nothing is used anymore. */
1943 if (!cbToRead)
1944 break;
1945
1946 /* Copy the data from our ring buffer to the core audio buffer. */
1947 memcpy((uint8_t *)pBufData->mBuffers[0].mData + cbRead, pbSrc, cbToRead);
1948
1949 /* Release the read buffer, so it could be used for new data. */
1950 RTCircBufReleaseReadBlock(pStreamOut->pCircBuf, cbToRead);
1951
1952 /* Move offset. */
1953 cbRead += cbToRead;
1954
1955 /* Check if we're lagging behind. */
1956 if (cbRead > pBufData->mBuffers[0].mDataByteSize)
1957 {
1958 LogRel2(("CoreAudio: Host output lagging behind, expect stuttering guest audio output\n"));
1959 cbRead = pBufData->mBuffers[0].mDataByteSize;
1960 break;
1961 }
1962
1963 Assert(cbToRead <= cbLeft);
1964 cbLeft -= cbToRead;
1965 }
1966
1967 /* Write the bytes to the core audio buffer which were really written. */
1968 Assert(pBufData->mBuffers[0].mDataByteSize >= cbRead);
1969 pBufData->mBuffers[0].mDataByteSize = cbRead;
1970
1971 Log3Func(("Read %zu / %zu bytes\n", cbRead, cbToRead));
1972
1973 return noErr;
1974}
1975
1976
1977/**
1978 * @interface_method_impl{PDMIHOSTAUDIO, pfnInit}
1979 *
1980 * @todo Please put me next to the shutdown function, because then it would be
1981 * clear why I'm empty. While at it, it would be nice if you also
1982 * reordered my PDMIHOSTAUDIO sibilings according to the interface
1983 * (doesn't matter if it's reversed (like all other PDM drivers and
1984 * devices) or not).
1985 */
1986static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)
1987{
1988 RT_NOREF(pInterface);
1989
1990 LogFlowFuncEnter();
1991
1992 return VINF_SUCCESS;
1993}
1994
1995/**
1996 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamCapture}
1997 */
1998static DECLCALLBACK(int) drvHostCoreAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1999 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2000{
2001 RT_NOREF(pvBuf, cbBuf); /** @todo r=bird: this looks totally weird at first glance! */
2002
2003 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2004 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2005 /* pcbRead is optional. */
2006
2007 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2008
2009/* unused, make you wonder why...
2010 size_t csReads = 0;
2011 char *pcSrc;
2012 PPDMAUDIOSAMPLE psDst; */
2013
2014 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
2015 {
2016 if (pcbRead)
2017 *pcbRead = 0;
2018 return VINF_SUCCESS;
2019 }
2020
2021 int rc = VINF_SUCCESS;
2022 uint32_t cbWrittenTotal = 0;
2023
2024 do
2025 {
2026 size_t cbMixBuf = AudioMixBufSizeBytes(&pStream->MixBuf);
2027 size_t cbToWrite = RT_MIN(cbMixBuf, RTCircBufUsed(pStreamIn->pCircBuf));
2028
2029 uint32_t cWritten, cbWritten;
2030 uint8_t *puBuf;
2031 size_t cbToRead;
2032
2033 Log3Func(("cbMixBuf=%zu, cbToWrite=%zu/%zu\n", cbMixBuf, cbToWrite, RTCircBufSize(pStreamIn->pCircBuf)));
2034
2035 while (cbToWrite)
2036 {
2037 /* Try to acquire the necessary block from the ring buffer. */
2038 RTCircBufAcquireReadBlock(pStreamIn->pCircBuf, cbToWrite, (void **)&puBuf, &cbToRead);
2039 if (!cbToRead)
2040 {
2041 RTCircBufReleaseReadBlock(pStreamIn->pCircBuf, cbToRead);
2042 break;
2043 }
2044
2045#ifdef DEBUG_DUMP_PCM_DATA
2046 RTFILE fh;
2047 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-capture.pcm",
2048 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
2049 if (RT_SUCCESS(rc))
2050 {
2051 RTFileWrite(fh, puBuf + cbWrittenTotal, cbToRead, NULL);
2052 RTFileClose(fh);
2053 }
2054 else
2055 AssertFailed();
2056#endif
2057 rc = AudioMixBufWriteCirc(&pStream->MixBuf, puBuf, cbToRead, &cWritten);
2058
2059 /* Release the read buffer, so it could be used for new data. */
2060 RTCircBufReleaseReadBlock(pStreamIn->pCircBuf, cbToRead);
2061
2062 if ( RT_FAILURE(rc)
2063 || !cWritten)
2064 {
2065 RTCircBufReleaseReadBlock(pStreamIn->pCircBuf, cbToRead);
2066 break;
2067 }
2068
2069 cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
2070
2071 /* Release the read buffer, so it could be used for new data. */
2072 RTCircBufReleaseReadBlock(pStreamIn->pCircBuf, cbWritten);
2073
2074 Assert(cbToWrite >= cbWritten);
2075 cbToWrite -= cbWritten;
2076 cbWrittenTotal += cbWritten;
2077 }
2078
2079 Log3Func(("cbToWrite=%zu, cbToRead=%zu, cbWrittenTotal=%RU32, rc=%Rrc\n", cbToWrite, cbToRead, cbWrittenTotal, rc));
2080 }
2081 while (0);
2082
2083 if (RT_SUCCESS(rc))
2084 {
2085 uint32_t cCaptured = 0;
2086 uint32_t cWrittenTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbWrittenTotal);
2087 if (cWrittenTotal)
2088 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal, &cCaptured);
2089
2090 Log3Func(("cWrittenTotal=%RU32 (%RU32 bytes), cCaptured=%RU32, rc=%Rrc\n", cWrittenTotal, cbWrittenTotal, cCaptured, rc));
2091
2092 if (cCaptured)
2093 LogFlowFunc(("%RU32 samples captured\n", cCaptured));
2094
2095 if (pcbRead)
2096 *pcbRead = cCaptured;
2097 }
2098
2099 if (RT_FAILURE(rc))
2100 LogFunc(("Failed with rc=%Rrc\n", rc));
2101
2102 return rc;
2103}
2104
2105/**
2106 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamPlay}
2107 */
2108static DECLCALLBACK(int) drvHostCoreAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
2109 PPDMAUDIOSTREAM pStream,
2110 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2111{
2112 RT_NOREF(pvBuf, cbBuf); /** @todo r=bird: this looks weird at first glance... */
2113
2114 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2115 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2116 /* pcbWritten is optional. */
2117
2118 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2119
2120 int rc = VINF_SUCCESS;
2121
2122 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
2123 if (!cLive) /* Not live samples to play? Bail out. */
2124 {
2125 if (pcbWritten)
2126 *pcbWritten = 0;
2127 return VINF_SUCCESS;
2128 }
2129
2130 size_t cbLive = AUDIOMIXBUF_S2B(&pStream->MixBuf, cLive);
2131
2132 uint32_t cbReadTotal = 0;
2133
2134 size_t cbToRead = RT_MIN(cbLive, RTCircBufFree(pStreamOut->pCircBuf));
2135 Log3Func(("cbToRead=%zu\n", cbToRead));
2136
2137 while (cbToRead)
2138 {
2139 uint32_t cRead, cbRead;
2140 uint8_t *puBuf;
2141 size_t cbCopy;
2142
2143 /* Try to acquire the necessary space from the ring buffer. */
2144 RTCircBufAcquireWriteBlock(pStreamOut->pCircBuf, cbToRead, (void **)&puBuf, &cbCopy);
2145 if (!cbCopy)
2146 {
2147 RTCircBufReleaseWriteBlock(pStreamOut->pCircBuf, cbCopy);
2148 break;
2149 }
2150
2151 Assert(cbCopy <= cbToRead);
2152
2153 rc = AudioMixBufReadCirc(&pStream->MixBuf,
2154 puBuf, cbCopy, &cRead);
2155
2156 if ( RT_FAILURE(rc)
2157 || !cRead)
2158 {
2159 RTCircBufReleaseWriteBlock(pStreamOut->pCircBuf, 0);
2160 break;
2161 }
2162
2163 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
2164
2165 /* Release the ring buffer, so the read thread could start reading this data. */
2166 RTCircBufReleaseWriteBlock(pStreamOut->pCircBuf, cbRead);
2167
2168 Assert(cbToRead >= cbRead);
2169 cbToRead -= cbRead;
2170 cbReadTotal += cbRead;
2171 }
2172
2173 if (RT_SUCCESS(rc))
2174 {
2175 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
2176 if (cReadTotal)
2177 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
2178
2179 Log3Func(("cReadTotal=%RU32 (%RU32 bytes)\n", cReadTotal, cbReadTotal));
2180
2181 if (pcbWritten)
2182 *pcbWritten = cReadTotal;
2183 }
2184
2185 return rc;
2186}
2187
2188static int coreAudioControlStreamOut(PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2189{
2190 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2191
2192 LogFlowFunc(("enmStreamCmd=%RU32\n", enmStreamCmd));
2193
2194 uint32_t uStatus = ASMAtomicReadU32(&pStreamOut->status);
2195 if (!( uStatus == CA_STATUS_INIT
2196 || uStatus == CA_STATUS_REINIT))
2197 {
2198 return VINF_SUCCESS;
2199 }
2200
2201 int rc = VINF_SUCCESS;
2202
2203 OSStatus err;
2204 switch (enmStreamCmd)
2205 {
2206 case PDMAUDIOSTREAMCMD_ENABLE:
2207 case PDMAUDIOSTREAMCMD_RESUME:
2208 {
2209 /* Only start the device if it is actually stopped */
2210 if (!coreAudioIsRunning(pStreamOut->deviceID))
2211 {
2212 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
2213 if (err != noErr)
2214 {
2215 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
2216 /* Keep going. */
2217 }
2218 RTCircBufReset(pStreamOut->pCircBuf);
2219
2220 err = AudioOutputUnitStart(pStreamOut->audioUnit);
2221 if (err != noErr)
2222 {
2223 LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));
2224 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
2225 }
2226 }
2227 break;
2228 }
2229
2230 case PDMAUDIOSTREAMCMD_DISABLE:
2231 case PDMAUDIOSTREAMCMD_PAUSE:
2232 {
2233 /* Only stop the device if it is actually running */
2234 if (coreAudioIsRunning(pStreamOut->deviceID))
2235 {
2236 err = AudioOutputUnitStop(pStreamOut->audioUnit);
2237 if (err != noErr)
2238 {
2239 LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
2240 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
2241 break;
2242 }
2243
2244 err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
2245 if (err != noErr)
2246 {
2247 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
2248 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
2249 }
2250 }
2251 break;
2252 }
2253
2254 default:
2255 rc = VERR_NOT_SUPPORTED;
2256 break;
2257 }
2258
2259 LogFlowFuncLeaveRC(rc);
2260 return rc;
2261}
2262
2263static int coreAudioControlStreamIn(PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2264{
2265 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2266
2267 LogFlowFunc(("enmStreamCmd=%RU32\n", enmStreamCmd));
2268
2269 uint32_t uStatus = ASMAtomicReadU32(&pStreamIn->status);
2270 if (!( uStatus == CA_STATUS_INIT
2271 || uStatus == CA_STATUS_REINIT))
2272 {
2273 return VINF_SUCCESS;
2274 }
2275
2276 int rc = VINF_SUCCESS;
2277
2278 OSStatus err;
2279 switch (enmStreamCmd)
2280 {
2281 case PDMAUDIOSTREAMCMD_ENABLE:
2282 case PDMAUDIOSTREAMCMD_RESUME:
2283 {
2284 /* Only start the device if it is actually stopped */
2285 if (!coreAudioIsRunning(pStreamIn->deviceID))
2286 {
2287 RTCircBufReset(pStreamIn->pCircBuf);
2288 err = AudioOutputUnitStart(pStreamIn->audioUnit);
2289 if (err != noErr)
2290 {
2291 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
2292 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
2293 break;
2294 }
2295 }
2296
2297 break;
2298 }
2299
2300 case PDMAUDIOSTREAMCMD_DISABLE:
2301 case PDMAUDIOSTREAMCMD_PAUSE:
2302 {
2303 /* Only stop the device if it is actually running */
2304 if (coreAudioIsRunning(pStreamIn->deviceID))
2305 {
2306 err = AudioOutputUnitStop(pStreamIn->audioUnit);
2307 if (err != noErr)
2308 {
2309 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
2310 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
2311 break;
2312 }
2313
2314 err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);
2315 if (err != noErr)
2316 {
2317 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
2318 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
2319 break;
2320 }
2321 }
2322 break;
2323 }
2324
2325 default:
2326 rc = VERR_NOT_SUPPORTED;
2327 break;
2328 }
2329
2330 LogFlowFuncLeaveRC(rc);
2331 return rc;
2332}
2333
2334static int coreAudioDestroyStreamIn(PPDMAUDIOSTREAM pStream)
2335{
2336 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2337
2338 LogFlowFuncEnter();
2339
2340 uint32_t status = ASMAtomicReadU32(&pStreamIn->status);
2341 if (!( status == CA_STATUS_INIT
2342 || status == CA_STATUS_REINIT))
2343 {
2344 return VINF_SUCCESS;
2345 }
2346
2347 OSStatus err = noErr;
2348
2349 int rc = coreAudioControlStreamIn(&pStreamIn->Stream, PDMAUDIOSTREAMCMD_DISABLE);
2350 if (RT_SUCCESS(rc))
2351 {
2352 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);
2353
2354 /*
2355 * Unregister recording device callbacks.
2356 */
2357 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
2358 kAudioObjectPropertyElementMaster };
2359#ifdef DEBUG
2360 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
2361 coreAudioRecordingAudioDevicePropertyChanged, &pStreamIn->cbCtx);
2362 if ( err != noErr
2363 && err != kAudioHardwareBadObjectError)
2364 {
2365 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err));
2366 }
2367#endif /* DEBUG */
2368
2369 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
2370 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
2371 coreAudioRecordingAudioDevicePropertyChanged, &pStreamIn->cbCtx);
2372 if ( err != noErr
2373 && err != kAudioHardwareBadObjectError)
2374 {
2375 LogRel(("CoreAudio: Failed to remove the recording sample rate changed listener (%RI32)\n", err));
2376 }
2377
2378 if (pStreamIn->fDefDevChgListReg)
2379 {
2380 propAdr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
2381 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
2382 coreAudioDefaultDeviceChanged, &pStreamIn->cbCtx);
2383 if ( err != noErr
2384 && err != kAudioHardwareBadObjectError)
2385 {
2386 LogRel(("CoreAudio: Failed to remove the default recording device changed listener (%RI32)\n", err));
2387 }
2388
2389 pStreamIn->fDefDevChgListReg = false;
2390 }
2391
2392 if (pStreamIn->fDevStateChgListReg)
2393 {
2394 Assert(pStreamIn->deviceID != kAudioDeviceUnknown);
2395
2396 AudioObjectPropertyAddress propAdr2 = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2397 kAudioObjectPropertyElementMaster };
2398 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr2,
2399 drvHostCoreAudioDeviceStateChanged, &pStreamIn->cbCtx);
2400 if ( err != noErr
2401 && err != kAudioHardwareBadObjectError)
2402 {
2403 LogRel(("CoreAudio: Failed to remove the recording device state changed listener (%RI32)\n", err));
2404 }
2405
2406 pStreamIn->fDevStateChgListReg = false;
2407 }
2408
2409 if (pStreamIn->pConverter)
2410 {
2411 AudioConverterDispose(pStreamIn->pConverter);
2412 pStreamIn->pConverter = NULL;
2413 }
2414
2415 err = AudioUnitUninitialize(pStreamIn->audioUnit);
2416 if (err == noErr)
2417 err = AudioComponentInstanceDispose(pStreamIn->audioUnit);
2418
2419 if ( err != noErr
2420 && err != kAudioHardwareBadObjectError)
2421 {
2422 LogRel(("CoreAudio: Failed to uninit the recording device (%RI32)\n", err));
2423 }
2424
2425 pStreamIn->deviceID = kAudioDeviceUnknown;
2426 pStreamIn->audioUnit = NULL;
2427 pStreamIn->sampleRatio = 1;
2428
2429 coreAudioUninitConvCbCtx(&pStreamIn->convCbCtx);
2430
2431 if (pStreamIn->pCircBuf)
2432 {
2433 RTCircBufDestroy(pStreamIn->pCircBuf);
2434 pStreamIn->pCircBuf = NULL;
2435 }
2436
2437 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
2438 }
2439 else
2440 {
2441 LogRel(("CoreAudio: Failed to stop recording on uninit (%RI32)\n", err));
2442 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
2443 }
2444
2445 LogFlowFuncLeaveRC(rc);
2446 return rc;
2447}
2448
2449static int coreAudioDestroyStreamOut(PPDMAUDIOSTREAM pStream)
2450{
2451 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2452
2453 LogFlowFuncEnter();
2454
2455 uint32_t status = ASMAtomicReadU32(&pStreamOut->status);
2456 if (!( status == CA_STATUS_INIT
2457 || status == CA_STATUS_REINIT))
2458 {
2459 return VINF_SUCCESS;
2460 }
2461
2462 int rc = coreAudioControlStreamOut(&pStreamOut->Stream, PDMAUDIOSTREAMCMD_DISABLE);
2463 if (RT_SUCCESS(rc))
2464 {
2465 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_UNINIT);
2466
2467 OSStatus err;
2468
2469 /*
2470 * Unregister playback device callbacks.
2471 */
2472 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
2473 kAudioObjectPropertyElementMaster };
2474#ifdef DEBUG
2475 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
2476 coreAudioPlaybackAudioDevicePropertyChanged, &pStreamOut->cbCtx);
2477 if ( err != noErr
2478 && err != kAudioHardwareBadObjectError)
2479 {
2480 LogRel(("CoreAudio: Failed to remove the playback processor overload listener (%RI32)\n", err));
2481 }
2482#endif /* DEBUG */
2483
2484 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
2485 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
2486 coreAudioPlaybackAudioDevicePropertyChanged, &pStreamOut->cbCtx);
2487 if ( err != noErr
2488 && err != kAudioHardwareBadObjectError)
2489 {
2490 LogRel(("CoreAudio: Failed to remove the playback sample rate changed listener (%RI32)\n", err));
2491 }
2492
2493 if (pStreamOut->fDefDevChgListReg)
2494 {
2495 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
2496 propAdr.mScope = kAudioObjectPropertyScopeGlobal;
2497 propAdr.mElement = kAudioObjectPropertyElementMaster;
2498 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
2499 coreAudioDefaultDeviceChanged, &pStreamOut->cbCtx);
2500 if ( err != noErr
2501 && err != kAudioHardwareBadObjectError)
2502 {
2503 LogRel(("CoreAudio: Failed to remove the default playback device changed listener (%RI32)\n", err));
2504 }
2505
2506 pStreamOut->fDefDevChgListReg = false;
2507 }
2508
2509 if (pStreamOut->fDevStateChgListReg)
2510 {
2511 Assert(pStreamOut->deviceID != kAudioDeviceUnknown);
2512
2513 AudioObjectPropertyAddress propAdr2 = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2514 kAudioObjectPropertyElementMaster };
2515 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr2,
2516 drvHostCoreAudioDeviceStateChanged, &pStreamOut->cbCtx);
2517 if ( err != noErr
2518 && err != kAudioHardwareBadObjectError)
2519 {
2520 LogRel(("CoreAudio: Failed to remove the playback device state changed listener (%RI32)\n", err));
2521 }
2522
2523 pStreamOut->fDevStateChgListReg = false;
2524 }
2525
2526 err = AudioUnitUninitialize(pStreamOut->audioUnit);
2527 if (err == noErr)
2528 err = AudioComponentInstanceDispose(pStreamOut->audioUnit);
2529
2530 if ( err != noErr
2531 && err != kAudioHardwareBadObjectError)
2532 {
2533 LogRel(("CoreAudio: Failed to uninit the playback device (%RI32)\n", err));
2534 }
2535
2536 pStreamOut->deviceID = kAudioDeviceUnknown;
2537 pStreamOut->audioUnit = NULL;
2538 if (pStreamOut->pCircBuf)
2539 {
2540 RTCircBufDestroy(pStreamOut->pCircBuf);
2541 pStreamOut->pCircBuf = NULL;
2542 }
2543
2544 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
2545 }
2546 else
2547 LogRel(("CoreAudio: Failed to stop playback on uninit, rc=%Rrc\n", rc));
2548
2549 LogFlowFuncLeaveRC(rc);
2550 return rc;
2551}
2552
2553static int coreAudioCreateStreamIn(PDRVHOSTCOREAUDIO pThis,
2554 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2555{
2556 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2557 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2558 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2559 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2560
2561 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2562
2563 LogFlowFunc(("enmRecSource=%RU32\n", pCfgReq->DestSource.Source));
2564
2565 pStreamIn->deviceID = kAudioDeviceUnknown;
2566 pStreamIn->audioUnit = NULL;
2567 pStreamIn->pConverter = NULL;
2568 pStreamIn->sampleRatio = 1;
2569 pStreamIn->pCircBuf = NULL;
2570 pStreamIn->status = CA_STATUS_UNINIT;
2571 pStreamIn->fDefDevChgListReg = false;
2572 pStreamIn->fDevStateChgListReg = false;
2573
2574 /* Set callback context. */
2575 pStreamIn->cbCtx.pThis = pThis;
2576 pStreamIn->cbCtx.enmDir = PDMAUDIODIR_IN;
2577 pStreamIn->cbCtx.pIn = pStreamIn;
2578
2579 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
2580
2581#if 0
2582 /* Try to find the audio device set by the user */
2583 if (DeviceUID.pszInputDeviceUID)
2584 {
2585 pStreamIn->deviceID = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszInputDeviceUID);
2586 /* Not fatal */
2587 if (pStreamIn->deviceID == kAudioDeviceUnknown)
2588 LogRel(("CoreAudio: Unable to find recording device %s. Falling back to the default audio device. \n", DeviceUID.pszInputDeviceUID));
2589 else
2590 fDeviceByUser = true;
2591 }
2592#endif
2593 int rc = coreAudioInitIn(pThis, &pStreamIn->Stream, pCfgReq, pCfgAcq);
2594 if (RT_SUCCESS(rc))
2595 {
2596 OSStatus err;
2597
2598 /* When the devices isn't forced by the user, we want default device change notifications. */
2599 if (!fDeviceByUser)
2600 {
2601 if (!pStreamIn->fDefDevChgListReg)
2602 {
2603 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
2604 kAudioObjectPropertyElementMaster };
2605 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2606 coreAudioDefaultDeviceChanged, &pStreamIn->cbCtx);
2607 if ( err == noErr
2608 || err == kAudioHardwareIllegalOperationError)
2609 {
2610 pStreamIn->fDefDevChgListReg = true;
2611 }
2612 else
2613 LogRel(("CoreAudio: Failed to add the default recording device changed listener (%RI32)\n", err));
2614 }
2615 }
2616
2617 if ( !pStreamIn->fDevStateChgListReg
2618 && (pStreamIn->deviceID != kAudioDeviceUnknown))
2619 {
2620 /* Register callback for being notified if the device stops being alive. */
2621 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2622 kAudioObjectPropertyElementMaster };
2623 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr, drvHostCoreAudioDeviceStateChanged,
2624 &pStreamIn->cbCtx);
2625 if (err == noErr)
2626 {
2627 pStreamIn->fDevStateChgListReg = true;
2628 }
2629 else
2630 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err));
2631 }
2632 }
2633
2634 LogFlowFuncLeaveRC(rc);
2635 return rc;
2636}
2637
2638static int coreAudioCreateStreamOut(PDRVHOSTCOREAUDIO pThis,
2639 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2640{
2641 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2642 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2643 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2644 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2645
2646 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2647
2648 LogFlowFuncEnter();
2649
2650 pStreamOut->deviceID = kAudioDeviceUnknown;
2651 pStreamOut->audioUnit = NULL;
2652 pStreamOut->pCircBuf = NULL;
2653 pStreamOut->status = CA_STATUS_UNINIT;
2654 pStreamOut->fDefDevChgListReg = false;
2655 pStreamOut->fDevStateChgListReg = false;
2656
2657 /* Set callback context. */
2658 pStreamOut->cbCtx.pThis = pThis;
2659 pStreamOut->cbCtx.enmDir = PDMAUDIODIR_OUT;
2660 pStreamOut->cbCtx.pOut = pStreamOut;
2661
2662 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
2663
2664#if 0
2665 /* Try to find the audio device set by the user. Use
2666 * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0
2667 * to set it. */
2668 if (DeviceUID.pszOutputDeviceUID)
2669 {
2670 pStreamOut->audioDeviceId = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszOutputDeviceUID);
2671 /* Not fatal */
2672 if (pStreamOut->audioDeviceId == kAudioDeviceUnknown)
2673 LogRel(("CoreAudio: Unable to find playback device %s. Falling back to the default audio device. \n", DeviceUID.pszOutputDeviceUID));
2674 else
2675 fDeviceByUser = true;
2676 }
2677#endif
2678 int rc = coreAudioInitOut(pThis, pStream, pCfgReq, pCfgAcq);
2679 if (RT_SUCCESS(rc))
2680 {
2681 OSStatus err;
2682
2683 /* When the devices isn't forced by the user, we want default device change notifications. */
2684 if (!fDeviceByUser)
2685 {
2686 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
2687 kAudioObjectPropertyElementMaster };
2688 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
2689 coreAudioDefaultDeviceChanged, &pStreamOut->cbCtx);
2690 if (err == noErr)
2691 {
2692 pStreamOut->fDefDevChgListReg = true;
2693 }
2694 else
2695 LogRel(("CoreAudio: Failed to add the default playback device changed listener (%RI32)\n", err));
2696 }
2697
2698 if ( !pStreamOut->fDevStateChgListReg
2699 && (pStreamOut->deviceID != kAudioDeviceUnknown))
2700 {
2701 /* Register callback for being notified if the device stops being alive. */
2702 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
2703 kAudioObjectPropertyElementMaster };
2704 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr, drvHostCoreAudioDeviceStateChanged,
2705 (void *)&pStreamOut->cbCtx);
2706 if (err == noErr)
2707 {
2708 pStreamOut->fDevStateChgListReg = true;
2709 }
2710 else
2711 LogRel(("CoreAudio: Failed to add the playback device state changed listener (%RI32)\n", err));
2712 }
2713 }
2714
2715 LogFlowFuncLeaveRC(rc);
2716 return rc;
2717}
2718
2719
2720/**
2721 * @interface_method_impl{PDMIHOSTAUDIO, pfnGetConfig}
2722 */
2723static DECLCALLBACK(int) drvHostCoreAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
2724{
2725 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2726 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
2727
2728 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2729
2730 return coreAudioUpdateStatusInternalEx(pThis, pBackendCfg, 0 /* fEnum */);
2731}
2732
2733
2734/**
2735 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamGetStatus}
2736 */
2737static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
2738{
2739 RT_NOREF(enmDir);
2740 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
2741
2742 return PDMAUDIOBACKENDSTS_RUNNING;
2743}
2744
2745
2746/**
2747 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamCreate}
2748 */
2749static DECLCALLBACK(int) drvHostCoreAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
2750 PPDMAUDIOSTREAM pStream,
2751 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
2752{
2753 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2754 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2755 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
2756 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
2757
2758 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);
2759
2760 int rc;
2761 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
2762 rc = coreAudioCreateStreamIn(pThis, pStream, pCfgReq, pCfgAcq);
2763 else
2764 rc = coreAudioCreateStreamOut(pThis, pStream, pCfgReq, pCfgAcq);
2765
2766 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
2767 return rc;
2768}
2769
2770
2771/**
2772 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamDestroy}
2773 */
2774static DECLCALLBACK(int) drvHostCoreAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2775{
2776 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2777 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2778
2779 int rc;
2780 if (pStream->enmDir == PDMAUDIODIR_IN)
2781 rc = coreAudioDestroyStreamIn(pStream);
2782 else
2783 rc = coreAudioDestroyStreamOut(pStream);
2784
2785 return rc;
2786}
2787
2788
2789/**
2790 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamControl}
2791 */
2792static DECLCALLBACK(int) drvHostCoreAudioStreamControl(PPDMIHOSTAUDIO pInterface,
2793 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
2794{
2795 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2796 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2797
2798 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
2799
2800 int rc;
2801 if (pStream->enmDir == PDMAUDIODIR_IN)
2802 rc = coreAudioControlStreamIn(pStream, enmStreamCmd);
2803 else
2804 rc = coreAudioControlStreamOut(pStream, enmStreamCmd);
2805
2806 return rc;
2807}
2808
2809
2810/**
2811 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamGetStatus}
2812 */
2813static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostCoreAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2814{
2815 RT_NOREF(pInterface);
2816 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2817
2818 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
2819
2820 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE;
2821
2822 if (pStream->enmDir == PDMAUDIODIR_IN)
2823 {
2824 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;
2825
2826 if (ASMAtomicReadU32(&pStreamIn->status) == CA_STATUS_INIT)
2827 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED;
2828
2829 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED)
2830 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_READABLE;
2831 }
2832 else if (pStream->enmDir == PDMAUDIODIR_OUT)
2833 {
2834 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;
2835
2836 if (ASMAtomicReadU32(&pStreamOut->status) == CA_STATUS_INIT)
2837 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED;
2838
2839 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED)
2840 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
2841 }
2842 else
2843 AssertFailed();
2844
2845 return strmSts;
2846}
2847
2848
2849/**
2850 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamIterate}
2851 */
2852static DECLCALLBACK(int) drvHostCoreAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
2853{
2854 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
2855 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
2856
2857 /* Nothing to do here for Core Audio. */
2858 return VINF_SUCCESS;
2859}
2860
2861
2862/**
2863 * @interface_method_impl{PDMIHOSTAUDIO, pfnShutdown}
2864 */
2865static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
2866{
2867 RT_NOREF(pInterface);
2868}
2869
2870
2871/**
2872 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2873 */
2874static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2875{
2876 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2877 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2878
2879 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2880 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
2881
2882 return NULL;
2883}
2884
2885/**
2886 * @callback_method_impl{FNPDMDRVCONSTRUCT,
2887 * Construct a DirectSound Audio driver instance.}
2888 */
2889static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2890{
2891 RT_NOREF(pCfg, fFlags);
2892 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2893 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
2894 LogRel(("Audio: Initializing Core Audio driver\n"));
2895
2896 /*
2897 * Init the static parts.
2898 */
2899 pThis->pDrvIns = pDrvIns;
2900 /* IBase */
2901 pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
2902 /* IHostAudio */
2903 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
2904
2905 return VINF_SUCCESS;
2906}
2907
2908/**
2909 * Char driver registration record.
2910 */
2911const PDMDRVREG g_DrvHostCoreAudio =
2912{
2913 /* u32Version */
2914 PDM_DRVREG_VERSION,
2915 /* szName */
2916 "CoreAudio",
2917 /* szRCMod */
2918 "",
2919 /* szR0Mod */
2920 "",
2921 /* pszDescription */
2922 "Core Audio host driver",
2923 /* fFlags */
2924 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2925 /* fClass. */
2926 PDM_DRVREG_CLASS_AUDIO,
2927 /* cMaxInstances */
2928 ~0U,
2929 /* cbInstance */
2930 sizeof(DRVHOSTCOREAUDIO),
2931 /* pfnConstruct */
2932 drvHostCoreAudioConstruct,
2933 /* pfnDestruct */
2934 NULL,
2935 /* pfnRelocate */
2936 NULL,
2937 /* pfnIOCtl */
2938 NULL,
2939 /* pfnPowerOn */
2940 NULL,
2941 /* pfnReset */
2942 NULL,
2943 /* pfnSuspend */
2944 NULL,
2945 /* pfnResume */
2946 NULL,
2947 /* pfnAttach */
2948 NULL,
2949 /* pfnDetach */
2950 NULL,
2951 /* pfnPowerOff */
2952 NULL,
2953 /* pfnSoftReset */
2954 NULL,
2955 /* u32EndVersion */
2956 PDM_DRVREG_VERSION
2957};
2958
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