VirtualBox

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

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

Audio-OSX: Rewrite of the CoreAudio device. Added recording support.

  • Property svn:eol-style set to native
File size: 44.7 KB
Line 
1/* $Id$ */
2/** @file
3 * VBox audio devices: Mac OS X CoreAudio audio driver
4 */
5
6/*
7 * Copyright (c) 2005 Mike Kronenberg
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 */
27
28#define LOG_GROUP LOG_GROUP_DEV_AUDIO
29#include <VBox/log.h>
30#include <iprt/mem.h>
31#include <iprt/cdefs.h>
32
33#define AUDIO_CAP "coreaudio"
34#include "vl_vbox.h"
35#include "audio.h"
36#include "audio_int.h"
37
38#include <CoreAudio/CoreAudio.h>
39#include <AudioUnit/AudioUnit.h>
40
41/* todo:
42 * - checking for properties changes of the devices
43 * - set frame size (use config)
44 * - AudioUnit converter for changing the sample rate
45 * - maybe get rid of the extra input sample buffer
46 * - maybe make sure the thread is immediately stopped if playing/recording stops
47 */
48
49/* Most of this is based on:
50 * http://developer.apple.com/mac/library/technotes/tn2004/tn2097.html
51 * http://developer.apple.com/mac/library/technotes/tn2002/tn2091.html
52 * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
53 */
54
55/*******************************************************************************
56 *
57 * IO Ring Buffer section
58 *
59 ******************************************************************************/
60
61/* Implementation of a lock free ring buffer which could be used in a multi
62 * threaded environment. */
63typedef struct IORINGBUFFER
64{
65 /* The current read position in the buffer */
66 uint32_t uReadPos;
67 /* The current write position in the buffer */
68 uint32_t uWritePos;
69 /* How much space of the buffer is currently in use */
70 volatile uint32_t cBufferUsed;
71 /* How big is the buffer */
72 uint32_t cBufSize;
73 /* The buffer itself */
74 char *pBuffer;
75} IORINGBUFFER;
76/* Pointer to an ring buffer structure */
77typedef IORINGBUFFER* PIORINGBUFFER;
78
79
80static void IORingBufferCreate(PIORINGBUFFER *ppBuffer, uint32_t cSize)
81{
82 PIORINGBUFFER pTmpBuffer;
83
84 AssertPtr(ppBuffer);
85
86 *ppBuffer = NULL;
87 pTmpBuffer = RTMemAllocZ(sizeof(IORINGBUFFER));
88 if (pTmpBuffer)
89 {
90 pTmpBuffer->pBuffer = RTMemAlloc(cSize);
91 if(pTmpBuffer->pBuffer)
92 {
93 pTmpBuffer->cBufSize = cSize;
94 *ppBuffer = pTmpBuffer;
95 }
96 else
97 RTMemFree(pTmpBuffer);
98 }
99}
100
101static void IORingBufferDestroy(PIORINGBUFFER pBuffer)
102{
103 if (pBuffer)
104 {
105 if (pBuffer->pBuffer)
106 RTMemFree(pBuffer->pBuffer);
107 RTMemFree(pBuffer);
108 }
109}
110
111DECL_FORCE_INLINE(void) IORingBufferReset(PIORINGBUFFER pBuffer)
112{
113 AssertPtr(pBuffer);
114
115 pBuffer->uReadPos = 0;
116 pBuffer->uWritePos = 0;
117 pBuffer->cBufferUsed = 0;
118}
119
120DECL_FORCE_INLINE(uint32_t) IORingBufferFree(PIORINGBUFFER pBuffer)
121{
122 AssertPtr(pBuffer);
123 return pBuffer->cBufSize - pBuffer->cBufferUsed;
124}
125
126DECL_FORCE_INLINE(uint32_t) IORingBufferUsed(PIORINGBUFFER pBuffer)
127{
128 AssertPtr(pBuffer);
129 return pBuffer->cBufferUsed;
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 = ASMAtomicAddU32(&pBuffer->cBufferUsed, 0);
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((int32_t*)&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 - ASMAtomicAddU32(&pBuffer->cBufferUsed, 0);
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 caAudioSettingsToAudioStreamBasicDescription(const audsettings_t *pAS, AudioStreamBasicDescription *pStreamDesc)
247{
248 pStreamDesc->mFormatID = kAudioFormatLinearPCM;
249 pStreamDesc->mFormatFlags = kAudioFormatFlagIsPacked;
250 pStreamDesc->mFramesPerPacket = 1;
251 pStreamDesc->mSampleRate = (Float64)pAS->freq;
252 pStreamDesc->mChannelsPerFrame = pAS->nchannels;
253 switch (pAS->fmt)
254 {
255 case AUD_FMT_U8:
256 {
257 pStreamDesc->mBitsPerChannel = 8;
258 break;
259 }
260 case AUD_FMT_S8:
261 {
262 pStreamDesc->mBitsPerChannel = 8;
263 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
264 break;
265 }
266 case AUD_FMT_U16:
267 {
268 pStreamDesc->mBitsPerChannel = 16;
269 break;
270 }
271 case AUD_FMT_S16:
272 {
273 pStreamDesc->mBitsPerChannel = 16;
274 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
275 break;
276 }
277#ifdef PA_SAMPLE_S32LE
278 case AUD_FMT_U32:
279 {
280 pStreamDesc->mBitsPerChannel = 32;
281 break;
282 }
283 case AUD_FMT_S32:
284 {
285 pStreamDesc->mBitsPerChannel = 32;
286 pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
287 break;
288 }
289#endif
290 default:
291 break;
292 }
293 pStreamDesc->mBytesPerFrame = pStreamDesc->mChannelsPerFrame * (pStreamDesc->mBitsPerChannel / 8);
294 pStreamDesc->mBytesPerPacket = pStreamDesc->mFramesPerPacket * pStreamDesc->mBytesPerFrame;
295}
296
297DECL_FORCE_INLINE(bool) caIsRunning(AudioDeviceID deviceID)
298{
299 OSStatus err = noErr;
300 UInt32 uFlag = 0;
301 UInt32 uSize = sizeof(uFlag);
302 err = AudioDeviceGetProperty(deviceID,
303 0,
304 0,
305 kAudioDevicePropertyDeviceIsRunning,
306 &uSize,
307 &uFlag);
308 if (err != kAudioHardwareNoError)
309 LogRel(("CoreAudio: Could not determine whether the device is running (%RI32)\n", err));
310 return uFlag >= 1;
311}
312
313/*******************************************************************************
314 *
315 * Global structures section
316 *
317 ******************************************************************************/
318
319struct
320{
321 int cBufferFrames;
322} conf =
323{
324 INIT_FIELD(.cBufferFrames =) 512
325};
326
327typedef struct caVoiceOut
328{
329 /* HW voice output struture defined by VBox */
330 HWVoiceOut hw;
331 /* Stream description which is default on the device */
332 AudioStreamBasicDescription deviceFormat;
333 /* Stream description which is selected for using by VBox */
334 AudioStreamBasicDescription streamFormat;
335 /* The audio device ID of the currently used device */
336 AudioDeviceID audioDeviceId;
337 /* The AudioUnit used */
338 AudioUnit audioUnit;
339 /* A ring buffer for transfering data to the playback thread */
340 PIORINGBUFFER pBuf;
341} caVoiceOut;
342
343typedef struct caVoiceIn
344{
345 /* HW voice input struture defined by VBox */
346 HWVoiceIn hw;
347 /* Stream description which is default on the device */
348 AudioStreamBasicDescription deviceFormat;
349 /* Stream description which is selected for using by VBox */
350 AudioStreamBasicDescription streamFormat;
351 /* The audio device ID of the currently used device */
352 AudioDeviceID audioDeviceId;
353 /* The AudioUnit used */
354 AudioUnit audioUnit;
355 /* An extra buffer used for render the audio data in the recording thread */
356 AudioBufferList *pcaBufferList;
357 /* A ring buffer for transfering from to the recording thread */
358 PIORINGBUFFER pBuf;
359} caVoiceIn;
360
361
362/*******************************************************************************
363 *
364 * CoreAudio output section
365 *
366 ******************************************************************************/
367
368/* callback to feed audiooutput buffer */
369static OSStatus caPlaybackCallback(void* inRefCon,
370 AudioUnitRenderActionFlags* ioActionFlags,
371 const AudioTimeStamp* inTimeStamp,
372 UInt32 inBusNumber,
373 UInt32 inNumberFrames,
374 AudioBufferList* ioData)
375{
376 uint32_t csAvail = 0;
377 uint32_t cbToRead = 0;
378 uint32_t csToRead = 0;
379 uint32_t csReads = 0;
380 char *pcSrc = NULL;
381
382 caVoiceOut *caVoice = (caVoiceOut *) inRefCon;
383
384 /* How much space is used in the ring buffer? */
385 csAvail = IORingBufferUsed(caVoice->pBuf) >> caVoice->hw.info.shift; /* bytes -> samples */
386 /* How much space is available in the core audio buffer. Use the smaller
387 * size of the too. */
388 csAvail = RT_MIN(csAvail, ioData->mBuffers[0].mDataByteSize >> caVoice->hw.info.shift);
389
390 Log2(("CoreAudio: [Output] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
391
392 /* Iterate as long as data is available */
393 while(csReads < csAvail)
394 {
395 /* How much is left? */
396 csToRead = csAvail - csReads;
397 cbToRead = csToRead << caVoice->hw.info.shift; /* samples -> bytes */
398 Log2(("CoreAudio: [Output] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead));
399 /* Try to aquire the necessary block from the ring buffer. */
400 IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead);
401 /* How much to we get? */
402 csToRead = cbToRead >> caVoice->hw.info.shift; /* bytes -> samples */
403 Log2(("CoreAudio: [Output] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead));
404 /* Break if nothing is used anymore. */
405 if (RT_UNLIKELY(cbToRead == 0))
406 break;
407 /* Copy the data from our ring buffer to the core audio buffer. */
408 memcpy((char*)ioData->mBuffers[0].mData + (csReads << caVoice->hw.info.shift), pcSrc, cbToRead);
409 /* Release the read buffer, so it could be used for new data. */
410 IORingBufferReleaseReadBlock(caVoice->pBuf, cbToRead);
411 /* How much have we reads so far. */
412 csReads += csToRead;
413 }
414 /* Write the bytes to the core audio buffer which where really written. */
415 ioData->mBuffers[0].mDataByteSize = csReads << caVoice->hw.info.shift; /* samples -> bytes */
416
417 Log2(("CoreAudio: [Output] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));
418
419 return noErr;
420}
421
422static int coreaudio_run_out(HWVoiceOut *hw)
423{
424 uint32_t csAvail = 0;
425 uint32_t cbToWrite = 0;
426 uint32_t csToWrite = 0;
427 uint32_t csWritten = 0;
428 char *pcDst = NULL;
429 st_sample_t *psSrc = NULL;
430
431 caVoiceOut *caVoice = (caVoiceOut *) hw;
432
433 /* How much space is available in the ring buffer */
434 csAvail = IORingBufferFree(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */
435 /* How much data is availabe. Use the smaller size of the too. */
436 csAvail = RT_MIN(csAvail, (uint32_t)audio_pcm_hw_get_live_out(hw));
437
438 Log2(("CoreAudio: [Output] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << hw->info.shift));
439
440 /* Iterate as long as data is available */
441 while (csWritten < csAvail)
442 {
443 /* How much is left? Split request at the end of our samples buffer. */
444 csToWrite = RT_MIN(csAvail - csWritten, (uint32_t)(hw->samples - hw->rpos));
445 cbToWrite = csToWrite << hw->info.shift; /* samples -> bytes */
446 Log2(("CoreAudio: [Output] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));
447 /* Try to aquire the necessary space from the ring buffer. */
448 IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
449 /* How much to we get? */
450 csToWrite = cbToWrite >> hw->info.shift;
451 Log2(("CoreAudio: [Output] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));
452 /* Break if nothing is free anymore. */
453 if (RT_UNLIKELY(cbToWrite == 0))
454 break;
455 /* Copy the data from our mix buffer to the ring buffer. */
456 psSrc = hw->mix_buf + hw->rpos;
457 hw->clip((uint8_t*)pcDst, psSrc, csToWrite);
458 /* Release the ring buffer, so the read thread could start reading this data. */
459 IORingBufferReleaseWriteBlock(caVoice->pBuf, cbToWrite);
460 hw->rpos = (hw->rpos + csToWrite) % hw->samples;
461 /* How much have we written so far. */
462 csWritten += csToWrite;
463 }
464
465 Log2(("CoreAudio: [Output] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << hw->info.shift));
466
467 /* Return the count of samples we have processed. */
468 return csWritten;
469}
470
471static int coreaudio_write(SWVoiceOut *sw, void *buf, int len)
472{
473 return audio_pcm_sw_write (sw, buf, len);
474}
475
476static int coreaudio_init_out(HWVoiceOut *hw, audsettings_t *as)
477{
478 OSStatus err = noErr;
479 UInt32 uSize = 0; /* temporary size of properties */
480 UInt32 uFlag = 0; /* for setting flags */
481 CFStringRef name; /* for the temporary device name fetching */
482 const char *pszName;
483 ComponentDescription cd; /* description for an audio component */
484 Component cp; /* an audio component */
485 AURenderCallbackStruct cb; /* holds the callback structure */
486 UInt32 cFrames; /* default frame count */
487
488 caVoiceOut *caVoice = (caVoiceOut *) hw;
489
490 /* Initialize the hardware info section with the audio settings */
491 audio_pcm_init_info(&hw->info, as);
492
493 /* Fetch the default audio output device currently in use */
494 uSize = sizeof(caVoice->audioDeviceId);
495 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
496 &uSize,
497 &caVoice->audioDeviceId);
498 if (RT_UNLIKELY(err != noErr))
499 {
500 LogRel(("CoreAudio: [Output] Unable to find default output device (%RI32)\n", err));
501 return -1;
502 }
503
504 /* Try to get the name of the default output device and log it. It's not
505 * fatal if it fails. */
506 uSize = sizeof(CFStringRef);
507 err = AudioDeviceGetProperty(caVoice->audioDeviceId,
508 0,
509 0,
510 kAudioObjectPropertyName,
511 &uSize,
512 &name);
513 if (RT_LIKELY(err == noErr))
514 {
515 pszName = CFStringGetCStringPtr(name, kCFStringEncodingMacRoman);
516 if (pszName)
517 LogRel(("CoreAudio: Using default output device: %s\n", pszName));
518 CFRelease(name);
519 }
520 else
521 LogRel(("CoreAudio: [Output] Unable to get output device name (%RI32)\n", err));
522
523 cd.componentType = kAudioUnitType_Output;
524 cd.componentSubType = kAudioUnitSubType_HALOutput;
525 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
526 cd.componentFlags = 0;
527 cd.componentFlagsMask = 0;
528
529 /* Try to find the default HAL output component. */
530 cp = FindNextComponent(NULL, &cd);
531 if (RT_UNLIKELY(cp == 0))
532 {
533 LogRel(("CoreAudio: [Output] Failed to find HAL output component\n"));
534 return -1;
535 }
536
537 /* Open the default HAL output component. */
538 err = OpenAComponent(cp, &caVoice->audioUnit);
539 if (RT_UNLIKELY(err != noErr))
540 {
541 LogRel(("CoreAudio: [Output] Failed to open output component (%RI32)\n", err));
542 return -1;
543 }
544
545 /* Switch the I/O mode for output to on. */
546 uFlag = 1;
547 err = AudioUnitSetProperty(caVoice->audioUnit,
548 kAudioOutputUnitProperty_EnableIO,
549 kAudioUnitScope_Output,
550 0,
551 &uFlag,
552 sizeof(uFlag));
553 if (RT_UNLIKELY(err != noErr))
554 {
555 LogRel(("CoreAudio: [Output] Failed to set output I/O mode enabled (%RI32)\n", err));
556 return -1;
557 }
558
559 /* CoreAudio will inform us on a second thread when it needs more data for
560 * output. Therfor register an callback function which will provide the new
561 * data. */
562 cb.inputProc = caPlaybackCallback;
563 cb.inputProcRefCon = caVoice;
564
565 err = AudioUnitSetProperty(caVoice->audioUnit,
566 kAudioUnitProperty_SetRenderCallback,
567 kAudioUnitScope_Global,
568 0,
569 &cb,
570 sizeof(cb));
571 if (RT_UNLIKELY(err != noErr))
572 {
573 LogRel(("CoreAudio: [Output] Failed to set callback (%RI32)\n", err));
574 return -1;
575 }
576
577 /* Set the default audio output device as the device for the new AudioUnit. */
578 err = AudioUnitSetProperty(caVoice->audioUnit,
579 kAudioOutputUnitProperty_CurrentDevice,
580 kAudioUnitScope_Global,
581 0,
582 &caVoice->audioDeviceId,
583 sizeof(caVoice->audioDeviceId));
584 if (RT_UNLIKELY(err != noErr))
585 {
586 LogRel(("CoreAudio: [Output] Failed to set current device (%RI32)\n", err));
587 return -1;
588 }
589
590 /* Fetch the current stream format of the device. */
591 uSize = sizeof(caVoice->deviceFormat);
592 err = AudioUnitGetProperty(caVoice->audioUnit,
593 kAudioUnitProperty_StreamFormat,
594 kAudioUnitScope_Input,
595 0,
596 &caVoice->deviceFormat,
597 &uSize);
598 if (RT_UNLIKELY(err != noErr))
599 {
600 LogRel(("CoreAudio: [Output] Failed to get device format (%RI32)\n", err));
601 return -1;
602 }
603
604 /* Create an AudioStreamBasicDescription based on the audio settings of
605 * VirtualBox. */
606 caAudioSettingsToAudioStreamBasicDescription(as, &caVoice->streamFormat);
607
608#if DEBUG
609 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] device", &caVoice->deviceFormat);
610 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Output] output", &caVoice->streamFormat);
611#endif /* DEBUG */
612
613 /* Set the device format description for the stream. */
614 err = AudioUnitSetProperty(caVoice->audioUnit,
615 kAudioUnitProperty_StreamFormat,
616 kAudioUnitScope_Input,
617 0,
618 &caVoice->streamFormat,
619 sizeof(caVoice->streamFormat));
620 if (RT_UNLIKELY(err != noErr))
621 {
622 LogRel(("CoreAudio: [Output] Failed to set stream format (%RI32)\n", err));
623 return -1;
624 }
625
626 /* Get the default frames buffer size, so that we can setup our internal
627 * buffers. */
628 uSize = sizeof(cFrames);
629 err = AudioUnitGetProperty(caVoice->audioUnit,
630 kAudioDevicePropertyBufferFrameSize,
631 kAudioUnitScope_Output,
632 0,
633 &cFrames,
634 &uSize);
635 if (RT_UNLIKELY(err != noErr))
636 {
637 LogRel(("CoreAudio: [Output] Failed to get frame buffer size (%RI32)\n", err));
638 return -1;
639 }
640
641/* cFrames = 1024;*/
642/* err = AudioUnitSetProperty(caVoice->audioUnit,*/
643/* kAudioDevicePropertyBufferFrameSize,*/
644/* kAudioUnitScope_Output,*/
645/* 0,*/
646/* &cFrames,*/
647/* sizeof(cFrames));*/
648/* if (RT_UNLIKELY(err != noErr))*/
649/* {*/
650/* LogRel(("CoreAudio: [Output] Failed to set frame buffer size (%RI32)\n", err));*/
651/* return -1;*/
652/* }*/
653
654 /* Finaly init the new AudioUnit. */
655 err = AudioUnitInitialize(caVoice->audioUnit);
656 if (RT_UNLIKELY(err != noErr))
657 {
658 LogRel(("CoreAudio: [Output] Failed to initialize the AudioUnit (%RI32)\n", err));
659 return -1;
660 }
661
662 /* Create the internal ring buffer. */
663 hw->samples = cFrames * caVoice->streamFormat.mChannelsPerFrame;
664 IORingBufferCreate(&caVoice->pBuf, hw->samples << hw->info.shift);
665 if (!VALID_PTR(caVoice->pBuf))
666 {
667 LogRel(("CoreAudio: [Output] Failed to create internal ring buffer\n"));
668 AudioUnitUninitialize(caVoice->audioUnit);
669 return -1;
670 }
671
672 Log(("CoreAudio: [Output] HW samples: %d; Frame count: %RU32\n", hw->samples, cFrames));
673
674 return 0;
675}
676
677static void coreaudio_fini_out(HWVoiceOut *hw)
678{
679 OSStatus err = noErr;
680 caVoiceOut *caVoice = (caVoiceOut *) hw;
681
682 /* Only stop the device if it is actually running */
683 if (caIsRunning(caVoice->audioDeviceId))
684 err = AudioOutputUnitStop(caVoice->audioUnit);
685 if (RT_LIKELY(err == noErr))
686 {
687 err = AudioUnitUninitialize(caVoice->audioUnit);
688 if (RT_LIKELY(err == noErr))
689 {
690 caVoice->audioDeviceId = kAudioDeviceUnknown;
691 IORingBufferDestroy(caVoice->pBuf);
692 }
693 else
694 LogRel(("CoreAudio: [Output] Failed to uninitialize the AudioUnit (%RI32)\n", err));
695 }
696 else
697 LogRel(("CoreAudio: [Output] Failed to stop playback (%RI32)\n", err));
698}
699
700static int coreaudio_ctl_out(HWVoiceOut *hw, int cmd, ...)
701{
702 OSStatus err = noErr;
703 caVoiceOut *caVoice = (caVoiceOut *) hw;
704
705 switch (cmd)
706 {
707 case VOICE_ENABLE:
708 {
709 /* Only start the device if it is actually stopped */
710 if (!caIsRunning(caVoice->audioDeviceId))
711 {
712 IORingBufferReset(caVoice->pBuf);
713 err = AudioOutputUnitStart(caVoice->audioUnit);
714 }
715 if (RT_UNLIKELY(err != noErr))
716 {
717 LogRel(("CoreAudio: [Output] Failed to start playback (%RI32)\n", err));
718 return -1;
719 }
720 break;
721 }
722 case VOICE_DISABLE:
723 {
724 /* Only stop the device if it is actually running */
725 if (caIsRunning(caVoice->audioDeviceId))
726 err = AudioOutputUnitStop(caVoice->audioUnit);
727 if (RT_UNLIKELY(err != noErr))
728 {
729 LogRel(("CoreAudio: [Input] Failed to stop playback (%RI32)\n", err));
730 return -1;
731 }
732 break;
733 }
734 }
735 return 0;
736}
737
738/*******************************************************************************
739 *
740 * CoreAudio input section
741 *
742 ******************************************************************************/
743
744/* callback to feed audio input buffer */
745static OSStatus caRecordingCallback(void* inRefCon,
746 AudioUnitRenderActionFlags* ioActionFlags,
747 const AudioTimeStamp* inTimeStamp,
748 UInt32 inBusNumber,
749 UInt32 inNumberFrames,
750 AudioBufferList* ioData)
751{
752 OSStatus err = noErr;
753 uint32_t csAvail = 0;
754 uint32_t csToWrite = 0;
755 uint32_t cbToWrite = 0;
756 uint32_t csWritten = 0;
757 char *pcDst = NULL;
758
759 caVoiceIn *caVoice = (caVoiceIn *) inRefCon;
760
761 /* If nothing is pending return immediately. */
762 if (inNumberFrames == 0)
763 return noErr;
764
765 caVoice->pcaBufferList->mBuffers[0].mDataByteSize = caVoice->streamFormat.mBytesPerFrame * inNumberFrames;
766 caVoice->pcaBufferList->mBuffers[0].mData = RTMemAlloc(caVoice->pcaBufferList->mBuffers[0].mDataByteSize);
767
768 err = AudioUnitRender(caVoice->audioUnit,
769 ioActionFlags,
770 inTimeStamp,
771 inBusNumber,
772 inNumberFrames,
773 caVoice->pcaBufferList);
774 if(RT_UNLIKELY(err != noErr))
775 {
776 Log(("CoreAudio: [Input] Failed to render audio data (%RI32)\n", err));
777 RTMemFree(caVoice->pcaBufferList->mBuffers[0].mData);
778 return err;
779 }
780
781 /* How much space is free in the ring buffer? */
782 csAvail = IORingBufferFree(caVoice->pBuf) >> caVoice->hw.info.shift; /* bytes -> samples */
783 /* How much space is used in the core audio buffer. Use the smaller size of
784 * the too. */
785 csAvail = RT_MIN(csAvail, caVoice->pcaBufferList->mBuffers[0].mDataByteSize >> caVoice->hw.info.shift);
786
787 Log2(("CoreAudio: [Input] Start writing buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
788
789 /* Iterate as long as data is available */
790 while(csWritten < csAvail)
791 {
792 /* How much is left? */
793 csToWrite = csAvail - csWritten;
794 cbToWrite = csToWrite << caVoice->hw.info.shift;
795 Log2(("CoreAudio: [Input] Try writing %RU32 samples (%RU32 bytes)\n", csToWrite, cbToWrite));
796 /* Try to aquire the necessary space from the ring buffer. */
797 IORingBufferAquireWriteBlock(caVoice->pBuf, cbToWrite, &pcDst, &cbToWrite);
798 /* How much to we get? */
799 csToWrite = cbToWrite >> caVoice->hw.info.shift;
800 Log2(("CoreAudio: [Input] There is space for %RU32 samples (%RU32 bytes) available\n", csToWrite, cbToWrite));
801 /* Break if nothing is free anymore. */
802 if (RT_UNLIKELY(cbToWrite == 0))
803 break;
804 /* Copy the data from the core audio buffer to the ring buffer. */
805 memcpy(pcDst, (char*)caVoice->pcaBufferList->mBuffers[0].mData + (csWritten << caVoice->hw.info.shift), cbToWrite);
806 /* Release the ring buffer, so the main thread could start reading this data. */
807 IORingBufferReleaseWriteBlock(caVoice->pBuf, cbToWrite);
808 csWritten += csToWrite;
809 }
810 /* Cleanup */
811 RTMemFree(caVoice->pcaBufferList->mBuffers[0].mData);
812
813 Log2(("CoreAudio: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n", csWritten, csWritten << caVoice->hw.info.shift));
814
815 return noErr;
816}
817
818static int coreaudio_run_in(HWVoiceIn *hw)
819{
820 uint32_t csAvail = 0;
821 uint32_t cbToRead = 0;
822 uint32_t csToRead = 0;
823 uint32_t csReads = 0;
824 char *pcSrc;
825 st_sample_t *psDst;
826
827 caVoiceIn *caVoice = (caVoiceIn *) hw;
828
829 /* How much space is used in the ring buffer? */
830 csAvail = IORingBufferUsed(caVoice->pBuf) >> hw->info.shift; /* bytes -> samples */
831 /* How much space is available in the mix buffer. Use the smaller size of
832 * the too. */
833 csAvail = RT_MIN(csAvail, (uint32_t)(hw->samples - audio_pcm_hw_get_live_in (hw)));
834
835 Log2(("CoreAudio: [Input] Start reading buffer with %RU32 samples (%RU32 bytes)\n", csAvail, csAvail << caVoice->hw.info.shift));
836
837 /* Iterate as long as data is available */
838 while (csReads < csAvail)
839 {
840 /* How much is left? Split request at the end of our samples buffer. */
841 csToRead = RT_MIN(csAvail - csReads, (uint32_t)(hw->samples - hw->wpos));
842 cbToRead = csToRead << hw->info.shift;
843 Log2(("CoreAudio: [Input] Try reading %RU32 samples (%RU32 bytes)\n", csToRead, cbToRead));
844 /* Try to aquire the necessary block from the ring buffer. */
845 IORingBufferAquireReadBlock(caVoice->pBuf, cbToRead, &pcSrc, &cbToRead);
846 /* How much to we get? */
847 csToRead = cbToRead >> hw->info.shift;
848 Log2(("CoreAudio: [Input] There are %RU32 samples (%RU32 bytes) available\n", csToRead, cbToRead));
849 /* Break if nothing is used anymore. */
850 if (cbToRead == 0)
851 break;
852 /* Copy the data from our ring buffer to the mix buffer. */
853 psDst = hw->conv_buf + hw->wpos;
854 hw->conv(psDst, pcSrc, csToRead, &nominal_volume);
855 /* Release the read buffer, so it could be used for new data. */
856 IORingBufferReleaseReadBlock(caVoice->pBuf, cbToRead);
857 hw->wpos = (hw->wpos + csToRead) % hw->samples;
858 /* How much have we reads so far. */
859 csReads += csToRead;
860 }
861
862 Log2(("CoreAudio: [Input] Finished reading buffer with %RU32 samples (%RU32 bytes)\n", csReads, csReads << caVoice->hw.info.shift));
863
864 return csReads;
865}
866
867static int coreaudio_read(SWVoiceIn *sw, void *buf, int size)
868{
869 return audio_pcm_sw_read (sw, buf, size);
870}
871
872static int coreaudio_init_in(HWVoiceIn *hw, audsettings_t *as)
873{
874 OSStatus err = noErr;
875 int rc = -1;
876 UInt32 uSize = 0; /* temporary size of properties */
877 UInt32 uFlag = 0; /* for setting flags */
878 CFStringRef name; /* for the temporary device name fetching */
879 const char *pszName;
880 ComponentDescription cd; /* description for an audio component */
881 Component cp; /* an audio component */
882 AURenderCallbackStruct cb; /* holds the callback structure */
883 UInt32 cFrames; /* default frame count */
884
885 caVoiceIn *caVoice = (caVoiceIn *) hw;
886
887 /* Initialize the hardware info section with the audio settings */
888 audio_pcm_init_info(&hw->info, as);
889
890 /* Fetch the default audio input device currently in use */
891 uSize = sizeof(caVoice->audioDeviceId);
892 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
893 &uSize,
894 &caVoice->audioDeviceId);
895 if (RT_UNLIKELY(err != noErr))
896 {
897 LogRel(("CoreAudio: [Input] Unable to find default input device (%RI32)\n", err));
898 return -1;
899 }
900
901 /* Try to get the name of the default input device and log it. It's not
902 * fatal if it fails. */
903 uSize = sizeof(CFStringRef);
904 err = AudioDeviceGetProperty(caVoice->audioDeviceId,
905 0,
906 1,
907 kAudioObjectPropertyName,
908 &uSize,
909 &name);
910 if (RT_LIKELY(err == noErr))
911 {
912 pszName = CFStringGetCStringPtr(name, kCFStringEncodingMacRoman);
913 if (pszName)
914 LogRel(("CoreAudio: Using default input device: %s\n", pszName));
915 CFRelease(name);
916 }
917 else
918 LogRel(("CoreAudio: [Input] Unable to get input device name (%RI32)\n", err));
919
920 cd.componentType = kAudioUnitType_Output;
921 cd.componentSubType = kAudioUnitSubType_HALOutput;
922 cd.componentManufacturer = kAudioUnitManufacturer_Apple;
923 cd.componentFlags = 0;
924 cd.componentFlagsMask = 0;
925
926 /* Try to find the default HAL output component. */
927 cp = FindNextComponent(NULL, &cd);
928 if (RT_UNLIKELY(cp == 0))
929 {
930 LogRel(("CoreAudio: [Input] Failed to find HAL output component\n"));
931 return -1;
932 }
933
934 /* Open the default HAL output component. */
935 err = OpenAComponent(cp, &caVoice->audioUnit);
936 if (RT_UNLIKELY(err != noErr))
937 {
938 LogRel(("CoreAudio: [Input] Failed to open output component (%RI32)\n", err));
939 return -1;
940 }
941
942 /* Switch the I/O mode for input to on. */
943 uFlag = 1;
944 err = AudioUnitSetProperty(caVoice->audioUnit,
945 kAudioOutputUnitProperty_EnableIO,
946 kAudioUnitScope_Input,
947 1,
948 &uFlag,
949 sizeof(uFlag));
950 if (RT_UNLIKELY(err != noErr))
951 {
952 LogRel(("CoreAudio: [Input] Failed to set input I/O mode enabled (%RI32)\n", err));
953 return -1;
954 }
955
956 /* Switch the I/O mode for output to off. This is important, as this is a
957 * pure input stream. */
958 uFlag = 0;
959 err = AudioUnitSetProperty(caVoice->audioUnit,
960 kAudioOutputUnitProperty_EnableIO,
961 kAudioUnitScope_Output,
962 0,
963 &uFlag,
964 sizeof(uFlag));
965 if (RT_UNLIKELY(err != noErr))
966 {
967 LogRel(("CoreAudio: [Input] Failed to set output I/O mode disabled (%RI32)\n", err));
968 return -1;
969 }
970
971 /* CoreAudio will inform us on a second thread for new incoming audio data.
972 * Therfor register an callback function, which will process the new data.
973 * */
974 cb.inputProc = caRecordingCallback;
975 cb.inputProcRefCon = caVoice;
976
977 err = AudioUnitSetProperty(caVoice->audioUnit,
978 kAudioOutputUnitProperty_SetInputCallback,
979 kAudioUnitScope_Global,
980 0,
981 &cb,
982 sizeof(cb));
983 if (RT_UNLIKELY(err != noErr))
984 {
985 LogRel(("CoreAudio: [Input] Failed to set callback (%RI32)\n", err));
986 return -1;
987 }
988
989 /* Set the default audio input device as the device for the new AudioUnit. */
990 err = AudioUnitSetProperty(caVoice->audioUnit,
991 kAudioOutputUnitProperty_CurrentDevice,
992 kAudioUnitScope_Global,
993 0,
994 &caVoice->audioDeviceId,
995 sizeof(caVoice->audioDeviceId));
996 if (RT_UNLIKELY(err != noErr))
997 {
998 LogRel(("CoreAudio: [Input] Failed to set current device (%RI32)\n", err));
999 return -1;
1000 }
1001
1002/* UInt32 fpb = 1024;*/
1003/* if (AudioDeviceSetProperty(core->audioDeviceId,*/
1004/* NULL,*/
1005/* 0,*/
1006/* 1,*/
1007/* kAudioDevicePropertyBufferSize,*/
1008/* sizeof(fpb), &fpb) != noErr)*/
1009/* {*/
1010/* LogRel(("CoreAudio: [Input] Unable to set frame buffer size)\n"));*/
1011/* return -1;*/
1012/* }*/
1013
1014 /* Fetch the current stream format of the device. */
1015 uSize = sizeof(caVoice->deviceFormat);
1016 err = AudioUnitGetProperty(caVoice->audioUnit,
1017 kAudioUnitProperty_StreamFormat,
1018 kAudioUnitScope_Input,
1019 1,
1020 &caVoice->deviceFormat,
1021 &uSize);
1022 if (RT_UNLIKELY(err != noErr))
1023 {
1024 LogRel(("CoreAudio: [Input] Failed to get device format (%RI32)\n", err));
1025 return -1;
1026 }
1027
1028 /* Create an AudioStreamBasicDescription based on the audio settings of
1029 * VirtualBox. */
1030 caAudioSettingsToAudioStreamBasicDescription(as, &caVoice->streamFormat);
1031
1032#if DEBUG
1033 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] device", &caVoice->deviceFormat);
1034 caDebugOutputAudioStreamBasicDescription("CoreAudio: [Input] input", &caVoice->streamFormat);
1035#endif /* DEBUG */
1036
1037 /* If the frequency of the device is different from the requested one we
1038 * need a converter. */
1039 if (caVoice->deviceFormat.mSampleRate != caVoice->streamFormat.mSampleRate)
1040 {
1041 Log(("CoreAudio: [Input] Converter in use\n"));
1042 /* Set the device format description for the stream. */
1043 err = AudioUnitSetProperty(caVoice->audioUnit,
1044 kAudioUnitProperty_StreamFormat,
1045 kAudioUnitScope_Output,
1046 1,
1047 &caVoice->deviceFormat,
1048 sizeof(caVoice->deviceFormat));
1049 }
1050 else
1051 {
1052 /* Set the new format description for the stream. */
1053 err = AudioUnitSetProperty(caVoice->audioUnit,
1054 kAudioUnitProperty_StreamFormat,
1055 kAudioUnitScope_Output,
1056 1,
1057 &caVoice->streamFormat,
1058 sizeof(caVoice->streamFormat));
1059 }
1060 if (RT_UNLIKELY(err != noErr))
1061 {
1062 LogRel(("CoreAudio: [Input] Failed to set stream format (%RI32)\n", err));
1063 return -1;
1064 }
1065
1066 /* Get the default frames per slice size, so that we can setup our internal
1067 * buffers. */
1068 uSize = sizeof(cFrames);
1069 err = AudioUnitGetProperty(caVoice->audioUnit,
1070 kAudioDevicePropertyBufferFrameSize,
1071 kAudioUnitScope_Global,
1072 0,
1073 &cFrames,
1074 &uSize);
1075 if (RT_UNLIKELY(err != noErr))
1076 {
1077 LogRel(("CoreAudio: [Input] Failed to get maximum frame per slice (%RI32)\n", err));
1078 return -1;
1079 }
1080
1081 /* Finaly init the new AudioUnit. */
1082 err = AudioUnitInitialize(caVoice->audioUnit);
1083 if (RT_UNLIKELY(err != noErr))
1084 {
1085 LogRel(("CoreAudio: [Input] Failed to initialize the AudioUnit (%RI32)\n", err));
1086 return -1;
1087 }
1088
1089 /* Set to zero first */
1090 caVoice->pcaBufferList = NULL;
1091 caVoice->pBuf = NULL;
1092 /* Create the AudioBufferList structure with one buffer. */
1093 caVoice->pcaBufferList = RTMemAllocZ(sizeof(AudioBufferList) + sizeof(AudioBuffer));
1094 if (VALID_PTR(caVoice->pcaBufferList))
1095 {
1096 caVoice->pcaBufferList->mNumberBuffers = 1;
1097 /* Initialize the buffer to nothing. */
1098 caVoice->pcaBufferList->mBuffers[0].mNumberChannels = caVoice->streamFormat.mChannelsPerFrame;
1099 caVoice->pcaBufferList->mBuffers[0].mDataByteSize = 0;
1100 caVoice->pcaBufferList->mBuffers[0].mData = NULL;
1101
1102 /* Create the internal ring buffer. */
1103 hw->samples = cFrames * caVoice->streamFormat.mChannelsPerFrame;
1104 IORingBufferCreate(&caVoice->pBuf, hw->samples << hw->info.shift);
1105 if (VALID_PTR(caVoice->pBuf))
1106 rc = 0;
1107 else
1108 LogRel(("CoreAudio: [Input] Failed to create internal ring buffer\n"));
1109 }
1110 else
1111 LogRel(("CoreAudio: [Input] Failed to create the audio buffer list\n"));
1112
1113 if (rc != 0)
1114 {
1115 if (caVoice->pcaBufferList)
1116 RTMemFree(caVoice->pcaBufferList);
1117 if (caVoice->pBuf)
1118 IORingBufferDestroy(caVoice->pBuf);
1119 AudioUnitUninitialize(caVoice->audioUnit);
1120 }
1121
1122 Log(("CoreAudio: [Input] HW samples: %d; Frame count: %RU32\n", hw->samples, cFrames));
1123
1124 return 0;
1125}
1126
1127static void coreaudio_fini_in(HWVoiceIn *hw)
1128{
1129 OSStatus err = noErr;
1130 caVoiceIn *caVoice = (caVoiceIn *) hw;
1131
1132 /* Only stop the device if it is actually running */
1133 if (caIsRunning(caVoice->audioDeviceId))
1134 err = AudioOutputUnitStop(caVoice->audioUnit);
1135 if (RT_LIKELY(err == noErr))
1136 {
1137 err = AudioUnitUninitialize(caVoice->audioUnit);
1138 if (RT_LIKELY(err == noErr))
1139 {
1140 caVoice->audioDeviceId = kAudioDeviceUnknown;
1141 IORingBufferDestroy(caVoice->pBuf);
1142 RTMemFree(caVoice->pcaBufferList);
1143 }
1144 else
1145 LogRel(("CoreAudio: [Input] Failed to uninitialize the AudioUnit (%RI32)\n", err));
1146 }
1147 else
1148 LogRel(("CoreAudio: [Input] Failed to stop recording (%RI32)\n", err));
1149}
1150
1151static int coreaudio_ctl_in(HWVoiceIn *hw, int cmd, ...)
1152{
1153 OSStatus err = noErr;
1154 caVoiceIn *caVoice = (caVoiceIn *) hw;
1155
1156 switch (cmd)
1157 {
1158 case VOICE_ENABLE:
1159 {
1160 /* Only start the device if it is actually stopped */
1161 if (!caIsRunning(caVoice->audioDeviceId))
1162 {
1163 IORingBufferReset(caVoice->pBuf);
1164 err = AudioOutputUnitStart(caVoice->audioUnit);
1165 }
1166 if (RT_UNLIKELY(err != noErr))
1167 {
1168 LogRel(("CoreAudio: [Input] Failed to start recording (%RI32)\n", err));
1169 return -1;
1170 }
1171 break;
1172 }
1173 case VOICE_DISABLE:
1174 {
1175 /* Only stop the device if it is actually running */
1176 if (caIsRunning(caVoice->audioDeviceId))
1177 err = AudioOutputUnitStop(caVoice->audioUnit);
1178 if (RT_UNLIKELY(err != noErr))
1179 {
1180 LogRel(("CoreAudio: [Input] Failed to stop recording (%RI32)\n", err));
1181 return -1;
1182 }
1183 break;
1184 }
1185 }
1186 return 0;
1187}
1188
1189/*******************************************************************************
1190 *
1191 * CoreAudio global section
1192 *
1193 ******************************************************************************/
1194
1195static void *coreaudio_audio_init(void)
1196{
1197 return &conf;
1198}
1199
1200static void coreaudio_audio_fini(void *opaque)
1201{
1202 NOREF(opaque);
1203}
1204
1205static struct audio_option coreaudio_options[] =
1206{
1207 {"BUFFER_SIZE", AUD_OPT_INT, &conf.cBufferFrames,
1208 "Size of the buffer in frames", NULL, 0},
1209 {NULL, 0, NULL, NULL, NULL, 0}
1210};
1211
1212static struct audio_pcm_ops coreaudio_pcm_ops =
1213{
1214 coreaudio_init_out,
1215 coreaudio_fini_out,
1216 coreaudio_run_out,
1217 coreaudio_write,
1218 coreaudio_ctl_out,
1219
1220 coreaudio_init_in,
1221 coreaudio_fini_in,
1222 coreaudio_run_in,
1223 coreaudio_read,
1224 coreaudio_ctl_in
1225};
1226
1227struct audio_driver coreaudio_audio_driver =
1228{
1229 INIT_FIELD(name =) "coreaudio",
1230 INIT_FIELD(descr =)
1231 "CoreAudio http://developer.apple.com/audio/coreaudio.html",
1232 INIT_FIELD(options =) coreaudio_options,
1233 INIT_FIELD(init =) coreaudio_audio_init,
1234 INIT_FIELD(fini =) coreaudio_audio_fini,
1235 INIT_FIELD(pcm_ops =) &coreaudio_pcm_ops,
1236 INIT_FIELD(can_be_default =) 1,
1237 INIT_FIELD(max_voices_out =) 1,
1238 INIT_FIELD(max_voices_in =) 1,
1239 INIT_FIELD(voice_size_out =) sizeof(caVoiceOut),
1240 INIT_FIELD(voice_size_in =) sizeof(caVoiceIn)
1241};
1242
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