VirtualBox

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

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

don't include vl_vbox.h anymore

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