VirtualBox

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

Last change on this file since 55998 was 54117, checked in by vboxsync, 10 years ago

don't check for VALID_PTR after RTMem*Alloc(), that's overkill

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