VirtualBox

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

Last change on this file since 29250 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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