VirtualBox

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

Last change on this file since 39103 was 39103, checked in by vboxsync, 13 years ago

Audio/coreaudio: more convenient memory allocation and release operations, initialization of variables.

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