VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/coreaudio.c@ 33540

Last change on this file since 33540 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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