Add support for float32 output to CoreAudio
[openal-soft.git] / Alc / backends / coreaudio.c
blob150f37ac2e1d73dc8eef696b497d23768be6c03a
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include "alMain.h"
28 #include "alu.h"
30 #include <CoreServices/CoreServices.h>
31 #include <unistd.h>
32 #include <AudioUnit/AudioUnit.h>
33 #include <AudioToolbox/AudioToolbox.h>
36 typedef struct {
37 AudioUnit audioUnit;
39 ALuint frameSize;
40 ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
41 AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
43 AudioConverterRef audioConverter; // Sample rate converter if needed
44 AudioBufferList *bufferList; // Buffer for data coming from the input device
45 ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
47 RingBuffer *ring;
48 } ca_data;
50 static const ALCchar ca_device[] = "CoreAudio Default";
53 static void destroy_buffer_list(AudioBufferList* list)
55 if(list)
57 UInt32 i;
58 for(i = 0;i < list->mNumberBuffers;i++)
59 free(list->mBuffers[i].mData);
60 free(list);
64 static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
66 AudioBufferList *list;
68 list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
69 if(list)
71 list->mNumberBuffers = 1;
73 list->mBuffers[0].mNumberChannels = channelCount;
74 list->mBuffers[0].mDataByteSize = byteSize;
75 list->mBuffers[0].mData = malloc(byteSize);
76 if(list->mBuffers[0].mData == NULL)
78 free(list);
79 list = NULL;
82 return list;
85 static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
86 UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
88 ALCdevice *device = (ALCdevice*)inRefCon;
89 ca_data *data = (ca_data*)device->ExtraData;
91 aluMixData(device, ioData->mBuffers[0].mData,
92 ioData->mBuffers[0].mDataByteSize / data->frameSize);
94 return noErr;
97 static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
98 AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
100 ALCdevice *device = (ALCdevice*)inUserData;
101 ca_data *data = (ca_data*)device->ExtraData;
103 // Read from the ring buffer and store temporarily in a large buffer
104 ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
106 // Set the input data
107 ioData->mNumberBuffers = 1;
108 ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
109 ioData->mBuffers[0].mData = data->resampleBuffer;
110 ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
112 return noErr;
115 static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
116 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
117 UInt32 inNumberFrames, AudioBufferList *ioData)
119 ALCdevice *device = (ALCdevice*)inRefCon;
120 ca_data *data = (ca_data*)device->ExtraData;
121 AudioUnitRenderActionFlags flags = 0;
122 OSStatus err;
124 // fill the bufferList with data from the input device
125 err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
126 if(err != noErr)
128 ERR("AudioUnitRender error: %d\n", err);
129 return err;
132 WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
134 return noErr;
137 static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
139 ComponentDescription desc;
140 Component comp;
141 ca_data *data;
142 OSStatus err;
144 if(!deviceName)
145 deviceName = ca_device;
146 else if(strcmp(deviceName, ca_device) != 0)
147 return ALC_INVALID_VALUE;
149 /* open the default output unit */
150 desc.componentType = kAudioUnitType_Output;
151 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
152 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
153 desc.componentFlags = 0;
154 desc.componentFlagsMask = 0;
156 comp = FindNextComponent(NULL, &desc);
157 if(comp == NULL)
159 ERR("FindNextComponent failed\n");
160 return ALC_INVALID_VALUE;
163 data = calloc(1, sizeof(*data));
165 err = OpenAComponent(comp, &data->audioUnit);
166 if(err != noErr)
168 ERR("OpenAComponent failed\n");
169 free(data);
170 return ALC_INVALID_VALUE;
173 /* init and start the default audio unit... */
174 err = AudioUnitInitialize(data->audioUnit);
175 if(err != noErr)
177 ERR("AudioUnitInitialize failed\n");
178 CloseComponent(data->audioUnit);
179 free(data);
180 return ALC_INVALID_VALUE;
183 device->DeviceName = strdup(deviceName);
184 device->ExtraData = data;
185 return ALC_NO_ERROR;
188 static void ca_close_playback(ALCdevice *device)
190 ca_data *data = (ca_data*)device->ExtraData;
192 AudioUnitUninitialize(data->audioUnit);
193 CloseComponent(data->audioUnit);
195 free(data);
196 device->ExtraData = NULL;
199 static ALCboolean ca_reset_playback(ALCdevice *device)
201 ca_data *data = (ca_data*)device->ExtraData;
202 AudioStreamBasicDescription streamFormat;
203 AURenderCallbackStruct input;
204 OSStatus err;
205 UInt32 size;
207 err = AudioUnitUninitialize(data->audioUnit);
208 if(err != noErr)
209 ERR("-- AudioUnitUninitialize failed.\n");
211 /* retrieve default output unit's properties (output side) */
212 size = sizeof(AudioStreamBasicDescription);
213 err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
214 if(err != noErr || size != sizeof(AudioStreamBasicDescription))
216 ERR("AudioUnitGetProperty failed\n");
217 return ALC_FALSE;
220 #if 0
221 TRACE("Output streamFormat of default output unit -\n");
222 TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
223 TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
224 TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
225 TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
226 TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
227 TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
228 #endif
230 /* set default output unit's input side to match output side */
231 err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
232 if(err != noErr)
234 ERR("AudioUnitSetProperty failed\n");
235 return ALC_FALSE;
238 if(device->Frequency != streamFormat.mSampleRate)
240 device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
241 streamFormat.mSampleRate /
242 device->Frequency);
243 device->Frequency = streamFormat.mSampleRate;
246 /* FIXME: How to tell what channels are what in the output device, and how
247 * to specify what we're giving? eg, 6.0 vs 5.1 */
248 switch(streamFormat.mChannelsPerFrame)
250 case 1:
251 device->FmtChans = DevFmtMono;
252 break;
253 case 2:
254 device->FmtChans = DevFmtStereo;
255 break;
256 case 4:
257 device->FmtChans = DevFmtQuad;
258 break;
259 case 6:
260 device->FmtChans = DevFmtX51;
261 break;
262 case 7:
263 device->FmtChans = DevFmtX61;
264 break;
265 case 8:
266 device->FmtChans = DevFmtX71;
267 break;
268 default:
269 ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
270 device->FmtChans = DevFmtStereo;
271 streamFormat.mChannelsPerFrame = 2;
272 break;
274 SetDefaultWFXChannelOrder(device);
276 /* use channel count and sample rate from the default output unit's current
277 * parameters, but reset everything else */
278 streamFormat.mFramesPerPacket = 1;
279 streamFormat.mFormatFlags = 0;
280 switch(device->FmtType)
282 case DevFmtUByte:
283 device->FmtType = DevFmtByte;
284 /* fall-through */
285 case DevFmtByte:
286 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
287 streamFormat.mBitsPerChannel = 8;
288 break;
289 case DevFmtUShort:
290 device->FmtType = DevFmtShort;
291 /* fall-through */
292 case DevFmtShort:
293 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
294 streamFormat.mBitsPerChannel = 16;
295 break;
296 case DevFmtUInt:
297 device->FmtType = DevFmtInt;
298 /* fall-through */
299 case DevFmtInt:
300 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
301 streamFormat.mBitsPerChannel = 32;
302 break;
303 case DevFmtFloat:
304 streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
305 streamFormat.mBitsPerChannel = 32;
306 break;
308 streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
309 streamFormat.mBitsPerChannel / 8;
310 streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
311 streamFormat.mFormatID = kAudioFormatLinearPCM;
312 streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
313 kLinearPCMFormatFlagIsPacked;
315 err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
316 if(err != noErr)
318 ERR("AudioUnitSetProperty failed\n");
319 return ALC_FALSE;
322 /* setup callback */
323 data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
324 input.inputProc = ca_callback;
325 input.inputProcRefCon = device;
327 err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
328 if(err != noErr)
330 ERR("AudioUnitSetProperty failed\n");
331 return ALC_FALSE;
334 /* init the default audio unit... */
335 err = AudioUnitInitialize(data->audioUnit);
336 if(err != noErr)
338 ERR("AudioUnitInitialize failed\n");
339 return ALC_FALSE;
342 return ALC_TRUE;
345 static ALCboolean ca_start_playback(ALCdevice *device)
347 ca_data *data = (ca_data*)device->ExtraData;
348 OSStatus err;
350 err = AudioOutputUnitStart(data->audioUnit);
351 if(err != noErr)
353 ERR("AudioOutputUnitStart failed\n");
354 return ALC_FALSE;
357 return ALC_TRUE;
360 static void ca_stop_playback(ALCdevice *device)
362 ca_data *data = (ca_data*)device->ExtraData;
363 OSStatus err;
365 err = AudioOutputUnitStop(data->audioUnit);
366 if(err != noErr)
367 ERR("AudioOutputUnitStop failed\n");
370 static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
372 AudioStreamBasicDescription requestedFormat; // The application requested format
373 AudioStreamBasicDescription hardwareFormat; // The hardware format
374 AudioStreamBasicDescription outputFormat; // The AudioUnit output format
375 AURenderCallbackStruct input;
376 ComponentDescription desc;
377 AudioDeviceID inputDevice;
378 UInt32 outputFrameCount;
379 UInt32 propertySize;
380 UInt32 enableIO;
381 Component comp;
382 ca_data *data;
383 OSStatus err;
385 desc.componentType = kAudioUnitType_Output;
386 desc.componentSubType = kAudioUnitSubType_HALOutput;
387 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
388 desc.componentFlags = 0;
389 desc.componentFlagsMask = 0;
391 // Search for component with given description
392 comp = FindNextComponent(NULL, &desc);
393 if(comp == NULL)
395 ERR("FindNextComponent failed\n");
396 return ALC_INVALID_VALUE;
399 data = calloc(1, sizeof(*data));
400 device->ExtraData = data;
402 // Open the component
403 err = OpenAComponent(comp, &data->audioUnit);
404 if(err != noErr)
406 ERR("OpenAComponent failed\n");
407 goto error;
410 // Turn off AudioUnit output
411 enableIO = 0;
412 err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
413 if(err != noErr)
415 ERR("AudioUnitSetProperty failed\n");
416 goto error;
419 // Turn on AudioUnit input
420 enableIO = 1;
421 err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
422 if(err != noErr)
424 ERR("AudioUnitSetProperty failed\n");
425 goto error;
428 // Get the default input device
429 propertySize = sizeof(AudioDeviceID);
430 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
431 if(err != noErr)
433 ERR("AudioHardwareGetProperty failed\n");
434 goto error;
437 if(inputDevice == kAudioDeviceUnknown)
439 ERR("No input device found\n");
440 goto error;
443 // Track the input device
444 err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
445 if(err != noErr)
447 ERR("AudioUnitSetProperty failed\n");
448 goto error;
451 // set capture callback
452 input.inputProc = ca_capture_callback;
453 input.inputProcRefCon = device;
455 err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
456 if(err != noErr)
458 ERR("AudioUnitSetProperty failed\n");
459 goto error;
462 // Initialize the device
463 err = AudioUnitInitialize(data->audioUnit);
464 if(err != noErr)
466 ERR("AudioUnitInitialize failed\n");
467 goto error;
470 // Get the hardware format
471 propertySize = sizeof(AudioStreamBasicDescription);
472 err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
473 if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
475 ERR("AudioUnitGetProperty failed\n");
476 goto error;
479 // Set up the requested format description
480 switch(device->FmtType)
482 case DevFmtUByte:
483 requestedFormat.mBitsPerChannel = 8;
484 requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
485 break;
486 case DevFmtShort:
487 requestedFormat.mBitsPerChannel = 16;
488 requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
489 break;
490 case DevFmtInt:
491 requestedFormat.mBitsPerChannel = 32;
492 requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
493 break;
494 case DevFmtFloat:
495 requestedFormat.mBitsPerChannel = 32;
496 requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
497 break;
498 case DevFmtByte:
499 case DevFmtUShort:
500 case DevFmtUInt:
501 ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
502 goto error;
505 switch(device->FmtChans)
507 case DevFmtMono:
508 requestedFormat.mChannelsPerFrame = 1;
509 break;
510 case DevFmtStereo:
511 requestedFormat.mChannelsPerFrame = 2;
512 break;
514 case DevFmtQuad:
515 case DevFmtX51:
516 case DevFmtX51Side:
517 case DevFmtX61:
518 case DevFmtX71:
519 ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
520 goto error;
523 requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
524 requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
525 requestedFormat.mSampleRate = device->Frequency;
526 requestedFormat.mFormatID = kAudioFormatLinearPCM;
527 requestedFormat.mReserved = 0;
528 requestedFormat.mFramesPerPacket = 1;
530 // save requested format description for later use
531 data->format = requestedFormat;
532 data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
534 // Use intermediate format for sample rate conversion (outputFormat)
535 // Set sample rate to the same as hardware for resampling later
536 outputFormat = requestedFormat;
537 outputFormat.mSampleRate = hardwareFormat.mSampleRate;
539 // Determine sample rate ratio for resampling
540 data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
542 // The output format should be the requested format, but using the hardware sample rate
543 // This is because the AudioUnit will automatically scale other properties, except for sample rate
544 err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
545 if(err != noErr)
547 ERR("AudioUnitSetProperty failed\n");
548 goto error;
551 // Set the AudioUnit output format frame count
552 outputFrameCount = device->UpdateSize * data->sampleRateRatio;
553 err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
554 if(err != noErr)
556 ERR("AudioUnitSetProperty failed: %d\n", err);
557 goto error;
560 // Set up sample converter
561 err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
562 if(err != noErr)
564 ERR("AudioConverterNew failed: %d\n", err);
565 goto error;
568 // Create a buffer for use in the resample callback
569 data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
571 // Allocate buffer for the AudioUnit output
572 data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
573 if(data->bufferList == NULL)
574 goto error;
576 data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
577 if(data->ring == NULL)
578 goto error;
580 return ALC_NO_ERROR;
582 error:
583 DestroyRingBuffer(data->ring);
584 free(data->resampleBuffer);
585 destroy_buffer_list(data->bufferList);
587 if(data->audioConverter)
588 AudioConverterDispose(data->audioConverter);
589 if(data->audioUnit)
590 CloseComponent(data->audioUnit);
592 free(data);
593 device->ExtraData = NULL;
595 return ALC_INVALID_VALUE;
598 static void ca_close_capture(ALCdevice *device)
600 ca_data *data = (ca_data*)device->ExtraData;
602 DestroyRingBuffer(data->ring);
603 free(data->resampleBuffer);
604 destroy_buffer_list(data->bufferList);
606 AudioConverterDispose(data->audioConverter);
607 CloseComponent(data->audioUnit);
609 free(data);
610 device->ExtraData = NULL;
613 static void ca_start_capture(ALCdevice *device)
615 ca_data *data = (ca_data*)device->ExtraData;
616 OSStatus err = AudioOutputUnitStart(data->audioUnit);
617 if(err != noErr)
618 ERR("AudioOutputUnitStart failed\n");
621 static void ca_stop_capture(ALCdevice *device)
623 ca_data *data = (ca_data*)device->ExtraData;
624 OSStatus err = AudioOutputUnitStop(data->audioUnit);
625 if(err != noErr)
626 ERR("AudioOutputUnitStop failed\n");
629 static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
631 ca_data *data = (ca_data*)device->ExtraData;
632 AudioBufferList *list;
633 UInt32 frameCount;
634 OSStatus err;
636 // If no samples are requested, just return
637 if(samples == 0)
638 return ALC_NO_ERROR;
640 // Allocate a temporary AudioBufferList to use as the return resamples data
641 list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
643 // Point the resampling buffer to the capture buffer
644 list->mNumberBuffers = 1;
645 list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
646 list->mBuffers[0].mDataByteSize = samples * data->frameSize;
647 list->mBuffers[0].mData = buffer;
649 // Resample into another AudioBufferList
650 frameCount = samples;
651 err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
652 device, &frameCount, list, NULL);
653 if(err != noErr)
655 ERR("AudioConverterFillComplexBuffer error: %d\n", err);
656 return ALC_INVALID_VALUE;
658 return ALC_NO_ERROR;
661 static ALCuint ca_available_samples(ALCdevice *device)
663 ca_data *data = device->ExtraData;
664 return RingBufferSize(data->ring) / data->sampleRateRatio;
668 static const BackendFuncs ca_funcs = {
669 ca_open_playback,
670 ca_close_playback,
671 ca_reset_playback,
672 ca_start_playback,
673 ca_stop_playback,
674 ca_open_capture,
675 ca_close_capture,
676 ca_start_capture,
677 ca_stop_capture,
678 ca_capture_samples,
679 ca_available_samples,
680 ALCdevice_LockDefault,
681 ALCdevice_UnlockDefault,
682 ALCdevice_GetLatencyDefault
685 ALCboolean alc_ca_init(BackendFuncs *func_list)
687 *func_list = ca_funcs;
688 return ALC_TRUE;
691 void alc_ca_deinit(void)
695 void alc_ca_probe(enum DevProbe type)
697 switch(type)
699 case ALL_DEVICE_PROBE:
700 AppendAllDevicesList(ca_device);
701 break;
702 case CAPTURE_DEVICE_PROBE:
703 AppendCaptureDeviceList(ca_device);
704 break;