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
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
)
58 for(UInt32 i
= 0;i
< list
->mNumberBuffers
;i
++)
59 free(list
->mBuffers
[i
].mData
);
64 static AudioBufferList
* allocate_buffer_list(UInt32 channelCount
, UInt32 byteSize
)
66 AudioBufferList
*list
;
68 list
= calloc(1, sizeof(AudioBufferList
) + sizeof(AudioBuffer
));
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
)
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
);
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
;
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;
124 // fill the bufferList with data from the input device
125 err
= AudioUnitRender(data
->audioUnit
, &flags
, inTimeStamp
, 1, inNumberFrames
, data
->bufferList
);
128 ERROR("AudioUnitRender error: %d\n", err
);
132 WriteRingBuffer(data
->ring
, data
->bufferList
->mBuffers
[0].mData
, inNumberFrames
);
137 static ALCboolean
ca_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
139 ComponentDescription desc
;
145 deviceName
= ca_device
;
146 else if(strcmp(deviceName
, ca_device
) != 0)
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
);
159 ERROR("FindNextComponent failed\n");
163 data
= calloc(1, sizeof(*data
));
164 device
->ExtraData
= data
;
166 err
= OpenAComponent(comp
, &data
->audioUnit
);
169 ERROR("OpenAComponent failed\n");
171 device
->ExtraData
= NULL
;
178 static void ca_close_playback(ALCdevice
*device
)
180 ca_data
*data
= (ca_data
*)device
->ExtraData
;
182 CloseComponent(data
->audioUnit
);
185 device
->ExtraData
= NULL
;
188 static ALCboolean
ca_reset_playback(ALCdevice
*device
)
190 ca_data
*data
= (ca_data
*)device
->ExtraData
;
191 AudioStreamBasicDescription streamFormat
;
192 AURenderCallbackStruct input
;
196 /* init and start the default audio unit... */
197 err
= AudioUnitInitialize(data
->audioUnit
);
200 ERROR("AudioUnitInitialize failed\n");
204 err
= AudioOutputUnitStart(data
->audioUnit
);
207 ERROR("AudioOutputUnitStart 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 ERROR("AudioUnitGetProperty failed\n");
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
);
230 /* set default output unit's input side to match output side */
231 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &streamFormat
, size
);
234 ERROR("AudioUnitSetProperty failed\n");
238 if(device
->Frequency
!= streamFormat
.mSampleRate
)
240 if((device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
241 ERROR("CoreAudio does not support changing sample rates (wanted %dhz, got %dhz)\n", device
->Frequency
, streamFormat
.mSampleRate
);
242 device
->Flags
&= ~DEVICE_FREQUENCY_REQUEST
;
244 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
245 streamFormat
.mSampleRate
/
247 device
->Frequency
= streamFormat
.mSampleRate
;
250 /* FIXME: How to tell what channels are what in the output device, and how
251 * to specify what we're giving? eg, 6.0 vs 5.1 */
252 switch(streamFormat
.mChannelsPerFrame
)
255 if((device
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
256 device
->FmtChans
!= DevFmtMono
)
258 ERROR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device
->FmtChans
));
259 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
261 device
->FmtChans
= DevFmtMono
;
264 if((device
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
265 device
->FmtChans
!= DevFmtStereo
)
267 ERROR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device
->FmtChans
));
268 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
270 device
->FmtChans
= DevFmtStereo
;
273 if((device
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
274 device
->FmtChans
!= DevFmtQuad
)
276 ERROR("Failed to set %s, got Quad instead\n", DevFmtChannelsString(device
->FmtChans
));
277 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
279 device
->FmtChans
= DevFmtQuad
;
282 if((device
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
283 device
->FmtChans
!= DevFmtX51
)
285 ERROR("Failed to set %s, got 5.1 Surround instead\n", DevFmtChannelsString(device
->FmtChans
));
286 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
288 device
->FmtChans
= DevFmtX51
;
291 if((device
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
292 device
->FmtChans
!= DevFmtX61
)
294 ERROR("Failed to set %s, got 6.1 Surround instead\n", DevFmtChannelsString(device
->FmtChans
));
295 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
297 device
->FmtChans
= DevFmtX61
;
300 if((device
->Flags
&DEVICE_CHANNELS_REQUEST
) &&
301 device
->FmtChans
!= DevFmtX71
)
303 ERROR("Failed to set %s, got 7.1 Surround instead\n", DevFmtChannelsString(device
->FmtChans
));
304 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
306 device
->FmtChans
= DevFmtX71
;
309 ERROR("Unhandled channel count (%d), using Stereo\n", streamFormat
.mChannelsPerFrame
);
310 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
311 device
->FmtChans
= DevFmtStereo
;
312 streamFormat
.mChannelsPerFrame
= 2;
315 SetDefaultWFXChannelOrder(device
);
317 /* use channel count and sample rate from the default output unit's current
318 * parameters, but reset everything else */
319 streamFormat
.mFramesPerPacket
= 1;
320 switch(device
->FmtType
)
323 device
->FmtType
= DevFmtByte
;
326 streamFormat
.mBitsPerChannel
= 8;
327 streamFormat
.mBytesPerPacket
= streamFormat
.mChannelsPerFrame
;
328 streamFormat
.mBytesPerFrame
= streamFormat
.mChannelsPerFrame
;
332 device
->FmtType
= DevFmtShort
;
335 streamFormat
.mBitsPerChannel
= 16;
336 streamFormat
.mBytesPerPacket
= 2 * streamFormat
.mChannelsPerFrame
;
337 streamFormat
.mBytesPerFrame
= 2 * streamFormat
.mChannelsPerFrame
;
340 streamFormat
.mFormatID
= kAudioFormatLinearPCM
;
341 streamFormat
.mFormatFlags
= kLinearPCMFormatFlagIsSignedInteger
|
342 kAudioFormatFlagsNativeEndian
|
343 kLinearPCMFormatFlagIsPacked
;
345 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &streamFormat
, sizeof(AudioStreamBasicDescription
));
348 ERROR("AudioUnitSetProperty failed\n");
353 data
->frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
354 input
.inputProc
= ca_callback
;
355 input
.inputProcRefCon
= device
;
357 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
, 0, &input
, sizeof(AURenderCallbackStruct
));
360 ERROR("AudioUnitSetProperty failed\n");
367 static void ca_stop_playback(ALCdevice
*device
)
369 ca_data
*data
= (ca_data
*)device
->ExtraData
;
372 AudioOutputUnitStop(data
->audioUnit
);
373 err
= AudioUnitUninitialize(data
->audioUnit
);
375 ERROR("-- AudioUnitUninitialize failed.\n");
378 static ALCboolean
ca_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
)
380 AudioStreamBasicDescription requestedFormat
; // The application requested format
381 AudioStreamBasicDescription hardwareFormat
; // The hardware format
382 AudioStreamBasicDescription outputFormat
; // The AudioUnit output format
383 AURenderCallbackStruct input
;
384 ComponentDescription desc
;
385 AudioDeviceID inputDevice
;
386 UInt32 outputFrameCount
;
393 desc
.componentType
= kAudioUnitType_Output
;
394 desc
.componentSubType
= kAudioUnitSubType_HALOutput
;
395 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
396 desc
.componentFlags
= 0;
397 desc
.componentFlagsMask
= 0;
399 // Search for component with given description
400 comp
= FindNextComponent(NULL
, &desc
);
403 ERROR("FindNextComponent failed\n");
407 data
= calloc(1, sizeof(*data
));
408 device
->ExtraData
= data
;
410 // Open the component
411 err
= OpenAComponent(comp
, &data
->audioUnit
);
414 ERROR("OpenAComponent failed\n");
418 // Turn off AudioUnit output
420 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Output
, 0, &enableIO
, sizeof(ALuint
));
423 ERROR("AudioUnitSetProperty failed\n");
427 // Turn on AudioUnit input
429 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Input
, 1, &enableIO
, sizeof(ALuint
));
432 ERROR("AudioUnitSetProperty failed\n");
436 // Get the default input device
437 propertySize
= sizeof(AudioDeviceID
);
438 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, &propertySize
, &inputDevice
);
441 ERROR("AudioHardwareGetProperty failed\n");
445 if(inputDevice
== kAudioDeviceUnknown
)
447 ERROR("No input device found\n");
451 // Track the input device
452 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_CurrentDevice
, kAudioUnitScope_Global
, 0, &inputDevice
, sizeof(AudioDeviceID
));
455 ERROR("AudioUnitSetProperty failed\n");
459 // set capture callback
460 input
.inputProc
= ca_capture_callback
;
461 input
.inputProcRefCon
= device
;
463 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioOutputUnitProperty_SetInputCallback
, kAudioUnitScope_Global
, 0, &input
, sizeof(AURenderCallbackStruct
));
466 ERROR("AudioUnitSetProperty failed\n");
470 // Initialize the device
471 err
= AudioUnitInitialize(data
->audioUnit
);
474 ERROR("AudioUnitInitialize failed\n");
478 // Get the hardware format
479 propertySize
= sizeof(AudioStreamBasicDescription
);
480 err
= AudioUnitGetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 1, &hardwareFormat
, &propertySize
);
481 if(err
!= noErr
|| propertySize
!= sizeof(AudioStreamBasicDescription
))
483 ERROR("AudioUnitGetProperty failed\n");
487 // Set up the requested format description
488 switch(device
->FmtType
)
491 requestedFormat
.mBitsPerChannel
= 8;
492 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
495 requestedFormat
.mBitsPerChannel
= 16;
496 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked
;
499 requestedFormat
.mBitsPerChannel
= 32;
500 requestedFormat
.mFormatFlags
= kAudioFormatFlagIsPacked
;
504 ERROR("%s samples not supported\n", DevFmtTypeString(device
->FmtType
));
508 switch(device
->FmtChans
)
511 requestedFormat
.mChannelsPerFrame
= 1;
514 requestedFormat
.mChannelsPerFrame
= 2;
522 ERROR("%s not supported\n", DevFmtChannelsString(device
->FmtChans
));
526 requestedFormat
.mBytesPerFrame
= requestedFormat
.mChannelsPerFrame
* requestedFormat
.mBitsPerChannel
/ 8;
527 requestedFormat
.mBytesPerPacket
= requestedFormat
.mBytesPerFrame
;
528 requestedFormat
.mSampleRate
= device
->Frequency
;
529 requestedFormat
.mFormatID
= kAudioFormatLinearPCM
;
530 requestedFormat
.mReserved
= 0;
531 requestedFormat
.mFramesPerPacket
= 1;
533 // save requested format description for later use
534 data
->format
= requestedFormat
;
535 data
->frameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
537 // Use intermediate format for sample rate conversion (outputFormat)
538 // Set sample rate to the same as hardware for resampling later
539 outputFormat
= requestedFormat
;
540 outputFormat
.mSampleRate
= hardwareFormat
.mSampleRate
;
542 // Determine sample rate ratio for resampling
543 data
->sampleRateRatio
= outputFormat
.mSampleRate
/ device
->Frequency
;
545 // The output format should be the requested format, but using the hardware sample rate
546 // This is because the AudioUnit will automatically scale other properties, except for sample rate
547 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 1, (void *)&outputFormat
, sizeof(outputFormat
));
550 ERROR("AudioUnitSetProperty failed\n");
554 // Set the AudioUnit output format frame count
555 outputFrameCount
= device
->UpdateSize
* data
->sampleRateRatio
;
556 err
= AudioUnitSetProperty(data
->audioUnit
, kAudioUnitProperty_MaximumFramesPerSlice
, kAudioUnitScope_Output
, 0, &outputFrameCount
, sizeof(outputFrameCount
));
559 ERROR("AudioUnitSetProperty failed: %d\n", err
);
563 // Set up sample converter
564 err
= AudioConverterNew(&outputFormat
, &requestedFormat
, &data
->audioConverter
);
567 ERROR("AudioConverterNew failed: %d\n", err
);
571 // Create a buffer for use in the resample callback
572 data
->resampleBuffer
= malloc(device
->UpdateSize
* data
->frameSize
* data
->sampleRateRatio
);
574 // Allocate buffer for the AudioUnit output
575 data
->bufferList
= allocate_buffer_list(outputFormat
.mChannelsPerFrame
, device
->UpdateSize
* data
->frameSize
* data
->sampleRateRatio
);
576 if(data
->bufferList
== NULL
)
578 alcSetError(device
, ALC_OUT_OF_MEMORY
);
582 data
->ring
= CreateRingBuffer(data
->frameSize
, (device
->UpdateSize
* data
->sampleRateRatio
) * device
->NumUpdates
);
583 if(data
->ring
== NULL
)
585 alcSetError(device
, ALC_OUT_OF_MEMORY
);
592 DestroyRingBuffer(data
->ring
);
593 free(data
->resampleBuffer
);
594 destroy_buffer_list(data
->bufferList
);
596 if(data
->audioConverter
)
597 AudioConverterDispose(data
->audioConverter
);
599 CloseComponent(data
->audioUnit
);
602 device
->ExtraData
= NULL
;
607 static void ca_close_capture(ALCdevice
*device
)
609 ca_data
*data
= (ca_data
*)device
->ExtraData
;
611 DestroyRingBuffer(data
->ring
);
612 free(data
->resampleBuffer
);
613 destroy_buffer_list(data
->bufferList
);
615 AudioConverterDispose(data
->audioConverter
);
616 CloseComponent(data
->audioUnit
);
619 device
->ExtraData
= NULL
;
622 static void ca_start_capture(ALCdevice
*device
)
624 ca_data
*data
= (ca_data
*)device
->ExtraData
;
625 OSStatus err
= AudioOutputUnitStart(data
->audioUnit
);
627 ERROR("AudioOutputUnitStart failed\n");
630 static void ca_stop_capture(ALCdevice
*device
)
632 ca_data
*data
= (ca_data
*)device
->ExtraData
;
633 OSStatus err
= AudioOutputUnitStop(data
->audioUnit
);
635 ERROR("AudioOutputUnitStop failed\n");
638 static ALCuint
ca_available_samples(ALCdevice
*device
)
640 ca_data
*data
= device
->ExtraData
;
641 return RingBufferSize(data
->ring
) / data
->sampleRateRatio
;
644 static void ca_capture_samples(ALCdevice
*device
, ALCvoid
*buffer
, ALCuint samples
)
646 ca_data
*data
= (ca_data
*)device
->ExtraData
;
648 if(samples
<= ca_available_samples(device
))
650 AudioBufferList
*list
;
654 // If no samples are requested, just return
658 // Allocate a temporary AudioBufferList to use as the return resamples data
659 list
= alloca(sizeof(AudioBufferList
) + sizeof(AudioBuffer
));
661 // Point the resampling buffer to the capture buffer
662 list
->mNumberBuffers
= 1;
663 list
->mBuffers
[0].mNumberChannels
= data
->format
.mChannelsPerFrame
;
664 list
->mBuffers
[0].mDataByteSize
= samples
* data
->frameSize
;
665 list
->mBuffers
[0].mData
= buffer
;
667 // Resample into another AudioBufferList
668 frameCount
= samples
;
669 err
= AudioConverterFillComplexBuffer(data
->audioConverter
, ca_capture_conversion_callback
, device
,
670 &frameCount
, list
, NULL
);
673 ERROR("AudioConverterFillComplexBuffer error: %d\n", err
);
674 alcSetError(device
, ALC_INVALID_VALUE
);
678 alcSetError(device
, ALC_INVALID_VALUE
);
681 static const BackendFuncs ca_funcs
= {
694 void alc_ca_init(BackendFuncs
*func_list
)
696 *func_list
= ca_funcs
;
699 void alc_ca_deinit(void)
703 void alc_ca_probe(enum DevProbe type
)
708 AppendDeviceList(ca_device
);
710 case ALL_DEVICE_PROBE
:
711 AppendAllDeviceList(ca_device
);
713 case CAPTURE_DEVICE_PROBE
:
714 AppendCaptureDeviceList(ca_device
);