VirtualBox

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

Last change on this file since 62142 was 62117, checked in by vboxsync, 9 years ago

Audio: Bugfixes, logging.

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