VirtualBox

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

Last change on this file since 26831 was 26562, checked in by vboxsync, 15 years ago

*: Added svn:keywords where missing.

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