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.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
31 #include <CoreServices/CoreServices.h>
33 #include <AudioUnit/AudioUnit.h>
34 #include <AudioToolbox/AudioToolbox.h>
41 ALdouble sampleRateRatio
; // Ratio of hardware sample rate / requested sample rate
42 AudioStreamBasicDescription format
; // This is the OpenAL format as a CoreAudio ASBD
44 AudioConverterRef audioConverter
; // Sample rate converter if needed
45 AudioBufferList
*bufferList
; // Buffer for data coming from the input device
46 ALCvoid
*resampleBuffer
; // Buffer for returned RingBuffer data when resampling
51 static const ALCchar ca_device
[] = "CoreAudio Default";
54 static void destroy_buffer_list(AudioBufferList
* list
)
59 for(i
= 0;i
< list
->mNumberBuffers
;i
++)
60 free(list
->mBuffers
[i
].mData
);
65 static AudioBufferList
* allocate_buffer_list(UInt32 channelCount
, UInt32 byteSize
)
67 AudioBufferList
*list
;
69 list
= calloc(1, sizeof(AudioBufferList
) + sizeof(AudioBuffer
));
72 list
->mNumberBuffers
= 1;
74 list
->mBuffers
[0].mNumberChannels
= channelCount
;
75 list
->mBuffers
[0].mDataByteSize
= byteSize
;
76 list
->mBuffers
[0].mData
= malloc(byteSize
);
77 if(list
->mBuffers
[0].mData
== NULL
)
86 static OSStatus
ca_callback(void *inRefCon
, AudioUnitRenderActionFlags
*ioActionFlags
, const AudioTimeStamp
*inTimeStamp
,
87 UInt32 inBusNumber
, UInt32 inNumberFrames
, AudioBufferList
*ioData
)
89 ALCdevice
*device
= (ALCdevice
*)inRefCon
;
90 ca_data
*data
= (ca_data
*)device
->ExtraData
;
92 aluMixData(device
, ioData
->mBuffers
[0].mData
,
93 ioData
->mBuffers
[0].mDataByteSize
/ data
->frameSize
);
98 static OSStatus
ca_capture_conversion_callback(AudioConverterRef inAudioConverter
, UInt32
*ioNumberDataPackets
,
99 AudioBufferList
*ioData
, AudioStreamPacketDescription
**outDataPacketDescription
, void* inUserData
)
101 ALCdevice
*device
= (ALCdevice
*)inUserData
;
102 ca_data
*data
= (ca_data
*)device
->ExtraData
;
104 // Read from the ring buffer and store temporarily in a large buffer
105 ReadRingBuffer(data
->ring
, data
->resampleBuffer
, (ALsizei
)(*ioNumberDataPackets
));
107 // Set the input data
108 ioData
->mNumberBuffers
= 1;
109 ioData
->mBuffers
[0].mNumberChannels
= data
->format
.mChannelsPerFrame
;
110 ioData
->mBuffers
[0].mData
= data
->resampleBuffer
;
111 ioData
->mBuffers
[0].mDataByteSize
= (*ioNumberDataPackets
) * data
->format
.mBytesPerFrame
;
116 static OSStatus
ca_capture_callback(void *inRefCon
, AudioUnitRenderActionFlags
*ioActionFlags
,
117 const AudioTimeStamp
*inTimeStamp
, UInt32 inBusNumber
,
118 UInt32 inNumberFrames
, AudioBufferList
*ioData
)
120 ALCdevice
*device
= (ALCdevice
*)inRefCon
;
121 ca_data
*data
= (ca_data
*)device
->ExtraData
;
122 AudioUnitRenderActionFlags flags
= 0;
125 // fill the bufferList with data from the input device
126 err
= AudioUnitRender(data
->audioUnit
, &flags
, inTimeStamp
, 1, inNumberFrames
, data
->bufferList
);
129 ERR("AudioUnitRender error: %d\n", err
);
133 WriteRingBuffer(data
->ring
, data
->bufferList
->mBuffers
[0].mData
, inNumberFrames
);
138 static ALCenum
ca_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
140 ComponentDescription desc
;
146 deviceName
= ca_device
;
147 else if(strcmp(deviceName
, ca_device
) != 0)
148 return ALC_INVALID_VALUE
;
150 /* open the default output unit */
151 desc
.componentType
= kAudioUnitType_Output
;
152 desc
.componentSubType
= kAudioUnitSubType_DefaultOutput
;
153 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
154 desc
.componentFlags
= 0;
155 desc
.componentFlagsMask
= 0;
157 comp
= FindNextComponent(NULL
, &desc
);
160 ERR("FindNextComponent failed\n");
161 return ALC_INVALID_VALUE
;
164 data
= calloc(1, sizeof(*data
));
166 err
= OpenAComponent(comp
, &data
->audioUnit
);
169 ERR("OpenAComponent failed\n");
171 return ALC_INVALID_VALUE
;
174 /* init and start the default audio unit... */
175 err
= AudioUnitInitialize(data
->audioUnit
);
178 ERR("AudioUnitInitialize failed\n");
179 CloseComponent(data
->audioUnit
);
181 return ALC_INVALID_VALUE
;
184 al_string_copy_cstr(&device
->DeviceName
, deviceName
);
185 device
->ExtraData
= data
;
189 static void ca_close_playback(ALCdevice
*device
)
191 ca_data
*data
= (ca_data
*)device
->ExtraData
;
193 AudioUnitUninitialize(data
->audioUnit
);
194 CloseComponent(data
->audioUnit
);
197 device
->ExtraData
= NULL
;
200 static ALCboolean
ca_reset_playback(ALCdevice
*device
)
202 ca_data
*data
= (ca_data
*)device
->ExtraData
;
203 AudioStreamBasicDescription streamFormat
;
204 AURenderCallbackStruct input
;
208 err
= AudioUnitUninitialize(data
->audioUnit
);
210 ERR("-- AudioUnitUninitialize failed.\n");
212 /* retrieve default output unit's properties (output side) */
213 size
= sizeof(AudioStreamBasicDescription
);
214 err
= AudioUnitGetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 0, &streamFormat
, &size
);
215 if(err
!= noErr
|| size
!= sizeof(AudioStreamBasicDescription
))
217 ERR("AudioUnitGetProperty failed\n");
222 TRACE("Output streamFormat of default output unit -\n");
223 TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat
.mFramesPerPacket
);
224 TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat
.mChannelsPerFrame
);
225 TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat
.mBitsPerChannel
);
226 TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat
.mBytesPerPacket
);
227 TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat
.mBytesPerFrame
);
228 TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat
.mSampleRate
);
231 /* set default output unit's input side to match output side */
232 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &streamFormat
, size
);
235 ERR("AudioUnitSetProperty failed\n");
239 if(device
->Frequency
!= streamFormat
.mSampleRate
)
241 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
242 streamFormat
.mSampleRate
/
244 device
->Frequency
= streamFormat
.mSampleRate
;
247 /* FIXME: How to tell what channels are what in the output device, and how
248 * to specify what we're giving? eg, 6.0 vs 5.1 */
249 switch(streamFormat
.mChannelsPerFrame
)
252 device
->FmtChans
= DevFmtMono
;
255 device
->FmtChans
= DevFmtStereo
;
258 device
->FmtChans
= DevFmtQuad
;
261 device
->FmtChans
= DevFmtX51
;
264 device
->FmtChans
= DevFmtX61
;
267 device
->FmtChans
= DevFmtX71
;
270 ERR("Unhandled channel count (%d), using Stereo\n", streamFormat
.mChannelsPerFrame
);
271 device
->FmtChans
= DevFmtStereo
;
272 streamFormat
.mChannelsPerFrame
= 2;
275 SetDefaultWFXChannelOrder(device
);
277 /* use channel count and sample rate from the default output unit's current
278 * parameters, but reset everything else */
279 streamFormat
.mFramesPerPacket
= 1;
280 streamFormat
.mFormatFlags
= 0;
281 switch(device
->FmtType
)
284 device
->FmtType
= DevFmtByte
;
287 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
288 streamFormat
.mBitsPerChannel
= 8;
291 device
->FmtType
= DevFmtShort
;
294 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
295 streamFormat
.mBitsPerChannel
= 16;
298 device
->FmtType
= DevFmtInt
;
301 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
;
302 streamFormat
.mBitsPerChannel
= 32;
305 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsFloat
;
306 streamFormat
.mBitsPerChannel
= 32;
309 streamFormat
.mBytesPerFrame
= streamFormat
.mChannelsPerFrame
*
310 streamFormat
.mBitsPerChannel
/ 8;
311 streamFormat
.mBytesPerPacket
= streamFormat
.mBytesPerFrame
;
312 streamFormat
.mFormatID
= kAudioFormatLinearPCM
;
313 streamFormat
.mFormatFlags
|= kAudioFormatFlagsNativeEndian
|
314 kLinearPCMFormatFlagIsPacked
;
316 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &streamFormat
, sizeof(AudioStreamBasicDescription
));
319 ERR("AudioUnitSetProperty failed\n");
324 data
->frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
325 input
.inputProc
= ca_callback
;
326 input
.inputProcRefCon
= device
;
328 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
, 0, &input
, sizeof(AURenderCallbackStruct
));
331 ERR("AudioUnitSetProperty failed\n");
335 /* init the default audio unit... */
336 err
= AudioUnitInitialize(data
->audioUnit
);
339 ERR("AudioUnitInitialize failed\n");
346 static ALCboolean
ca_start_playback(ALCdevice
*device
)
348 ca_data
*data
= (ca_data
*)device
->ExtraData
;
351 err
= AudioOutputUnitStart(data
->audioUnit
);
354 ERR("AudioOutputUnitStart failed\n");
361 static void ca_stop_playback(ALCdevice
*device
)
363 ca_data
*data
= (ca_data
*)device
->ExtraData
;
366 err
= AudioOutputUnitStop(data
->audioUnit
);
368 ERR("AudioOutputUnitStop failed\n");
371 static ALCenum
ca_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
)
373 AudioStreamBasicDescription requestedFormat
; // The application requested format
374 AudioStreamBasicDescription hardwareFormat
; // The hardware format
375 AudioStreamBasicDescription outputFormat
; // The AudioUnit output format
376 AURenderCallbackStruct input
;
377 ComponentDescription desc
;
378 AudioDeviceID inputDevice
;
379 UInt32 outputFrameCount
;
387 deviceName
= ca_device
;
388 else if(strcmp(deviceName
, ca_device
) != 0)
389 return ALC_INVALID_VALUE
;
391 desc
.componentType
= kAudioUnitType_Output
;
392 desc
.componentSubType
= kAudioUnitSubType_HALOutput
;
393 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
394 desc
.componentFlags
= 0;
395 desc
.componentFlagsMask
= 0;
397 // Search for component with given description
398 comp
= FindNextComponent(NULL
, &desc
);
401 ERR("FindNextComponent failed\n");
402 return ALC_INVALID_VALUE
;
405 data
= calloc(1, sizeof(*data
));
406 device
->ExtraData
= data
;
408 // Open the component
409 err
= OpenAComponent(comp
, &data
->audioUnit
);
412 ERR("OpenAComponent failed\n");
416 // Turn off AudioUnit output
418 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Output
, 0, &enableIO
, sizeof(ALuint
));
421 ERR("AudioUnitSetProperty failed\n");
425 // Turn on AudioUnit input
427 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Input
, 1, &enableIO
, sizeof(ALuint
));
430 ERR("AudioUnitSetProperty failed\n");
434 // Get the default input device
435 propertySize
= sizeof(AudioDeviceID
);
436 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, &propertySize
, &inputDevice
);
439 ERR("AudioHardwareGetProperty failed\n");
443 if(inputDevice
== kAudioDeviceUnknown
)
445 ERR("No input device found\n");
449 // Track the input device
450 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_CurrentDevice
, kAudioUnitScope_Global
, 0, &inputDevice
, sizeof(AudioDeviceID
));
453 ERR("AudioUnitSetProperty failed\n");
457 // set capture callback
458 input
.inputProc
= ca_capture_callback
;
459 input
.inputProcRefCon
= device
;
461 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_SetInputCallback
, kAudioUnitScope_Global
, 0, &input
, sizeof(AURenderCallbackStruct
));
464 ERR("AudioUnitSetProperty failed\n");
468 // Initialize the device
469 err
= AudioUnitInitialize(data
->audioUnit
);
472 ERR("AudioUnitInitialize failed\n");
476 // Get the hardware format
477 propertySize
= sizeof(AudioStreamBasicDescription
);
478 err
= AudioUnitGetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 1, &hardwareFormat
, &propertySize
);
479 if(err
!= noErr
|| propertySize
!= sizeof(AudioStreamBasicDescription
))
481 ERR("AudioUnitGetProperty failed\n");
485 // Set up the requested format description
486 switch(device
->FmtType
)
489 requestedFormat
.mBitsPerChannel
= 8;
490 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
493 requestedFormat
.mBitsPerChannel
= 16;
494 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked
;
497 requestedFormat
.mBitsPerChannel
= 32;
498 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked
;
501 requestedFormat
.mBitsPerChannel
= 32;
502 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
507 ERR("%s samples not supported\n", DevFmtTypeString(device
->FmtType
));
511 switch(device
->FmtChans
)
514 requestedFormat
.mChannelsPerFrame
= 1;
517 requestedFormat
.mChannelsPerFrame
= 2;
525 ERR("%s not supported\n", DevFmtChannelsString(device
->FmtChans
));
529 requestedFormat
.mBytesPerFrame
= requestedFormat
.mChannelsPerFrame
* requestedFormat
.mBitsPerChannel
/ 8;
530 requestedFormat
.mBytesPerPacket
= requestedFormat
.mBytesPerFrame
;
531 requestedFormat
.mSampleRate
= device
->Frequency
;
532 requestedFormat
.mFormatID
= kAudioFormatLinearPCM
;
533 requestedFormat
.mReserved
= 0;
534 requestedFormat
.mFramesPerPacket
= 1;
536 // save requested format description for later use
537 data
->format
= requestedFormat
;
538 data
->frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
540 // Use intermediate format for sample rate conversion (outputFormat)
541 // Set sample rate to the same as hardware for resampling later
542 outputFormat
= requestedFormat
;
543 outputFormat
.mSampleRate
= hardwareFormat
.mSampleRate
;
545 // Determine sample rate ratio for resampling
546 data
->sampleRateRatio
= outputFormat
.mSampleRate
/ device
->Frequency
;
548 // The output format should be the requested format, but using the hardware sample rate
549 // This is because the AudioUnit will automatically scale other properties, except for sample rate
550 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 1, (void *)&outputFormat
, sizeof(outputFormat
));
553 ERR("AudioUnitSetProperty failed\n");
557 // Set the AudioUnit output format frame count
558 outputFrameCount
= device
->UpdateSize
* data
->sampleRateRatio
;
559 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Output
, 0, &outputFrameCount
, sizeof(outputFrameCount
));
562 ERR("AudioUnitSetProperty failed: %d\n", err
);
566 // Set up sample converter
567 err
= AudioConverterNew(&outputFormat
, &requestedFormat
, &data
->audioConverter
);
570 ERR("AudioConverterNew failed: %d\n", err
);
574 // Create a buffer for use in the resample callback
575 data
->resampleBuffer
= malloc(device
->UpdateSize
* data
->frameSize
* data
->sampleRateRatio
);
577 // Allocate buffer for the AudioUnit output
578 data
->bufferList
= allocate_buffer_list(outputFormat
.mChannelsPerFrame
, device
->UpdateSize
* data
->frameSize
* data
->sampleRateRatio
);
579 if(data
->bufferList
== NULL
)
582 data
->ring
= CreateRingBuffer(data
->frameSize
, (device
->UpdateSize
* data
->sampleRateRatio
) * device
->NumUpdates
);
583 if(data
->ring
== NULL
)
586 al_string_copy_cstr(&device
->DeviceName
, deviceName
);
591 DestroyRingBuffer(data
->ring
);
592 free(data
->resampleBuffer
);
593 destroy_buffer_list(data
->bufferList
);
595 if(data
->audioConverter
)
596 AudioConverterDispose(data
->audioConverter
);
598 CloseComponent(data
->audioUnit
);
601 device
->ExtraData
= NULL
;
603 return ALC_INVALID_VALUE
;
606 static void ca_close_capture(ALCdevice
*device
)
608 ca_data
*data
= (ca_data
*)device
->ExtraData
;
610 DestroyRingBuffer(data
->ring
);
611 free(data
->resampleBuffer
);
612 destroy_buffer_list(data
->bufferList
);
614 AudioConverterDispose(data
->audioConverter
);
615 CloseComponent(data
->audioUnit
);
618 device
->ExtraData
= NULL
;
621 static void ca_start_capture(ALCdevice
*device
)
623 ca_data
*data
= (ca_data
*)device
->ExtraData
;
624 OSStatus err
= AudioOutputUnitStart(data
->audioUnit
);
626 ERR("AudioOutputUnitStart failed\n");
629 static void ca_stop_capture(ALCdevice
*device
)
631 ca_data
*data
= (ca_data
*)device
->ExtraData
;
632 OSStatus err
= AudioOutputUnitStop(data
->audioUnit
);
634 ERR("AudioOutputUnitStop failed\n");
637 static ALCenum
ca_capture_samples(ALCdevice
*device
, ALCvoid
*buffer
, ALCuint samples
)
639 ca_data
*data
= (ca_data
*)device
->ExtraData
;
640 AudioBufferList
*list
;
644 // If no samples are requested, just return
648 // Allocate a temporary AudioBufferList to use as the return resamples data
649 list
= alloca(sizeof(AudioBufferList
) + sizeof(AudioBuffer
));
651 // Point the resampling buffer to the capture buffer
652 list
->mNumberBuffers
= 1;
653 list
->mBuffers
[0].mNumberChannels
= data
->format
.mChannelsPerFrame
;
654 list
->mBuffers
[0].mDataByteSize
= samples
* data
->frameSize
;
655 list
->mBuffers
[0].mData
= buffer
;
657 // Resample into another AudioBufferList
658 frameCount
= samples
;
659 err
= AudioConverterFillComplexBuffer(data
->audioConverter
, ca_capture_conversion_callback
,
660 device
, &frameCount
, list
, NULL
);
663 ERR("AudioConverterFillComplexBuffer error: %d\n", err
);
664 return ALC_INVALID_VALUE
;
669 static ALCuint
ca_available_samples(ALCdevice
*device
)
671 ca_data
*data
= device
->ExtraData
;
672 return RingBufferSize(data
->ring
) / data
->sampleRateRatio
;
676 static const BackendFuncs ca_funcs
= {
690 ALCboolean
alc_ca_init(BackendFuncs
*func_list
)
692 *func_list
= ca_funcs
;
696 void alc_ca_deinit(void)
700 void alc_ca_probe(enum DevProbe type
)
704 case ALL_DEVICE_PROBE
:
705 AppendAllDevicesList(ca_device
);
707 case CAPTURE_DEVICE_PROBE
:
708 AppendCaptureDeviceList(ca_device
);