VirtualBox

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

最後變更 在這個檔案從61706是 61706,由 vboxsync 提交於 9 年 前

Audio/DrvHostCoreAudio.cpp: Implemented device state changed listener, fixed a warning, logging changes.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette