diff options
author | Chris Robinson <[email protected]> | 2011-03-15 04:58:56 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2011-03-15 04:58:56 -0700 |
commit | d6c8bb35b4f9af60cd1dd43c35d530937c95a3ef (patch) | |
tree | 3522bb5552597e42ab010c4d190fb14c27e14299 | |
parent | 4899674b6c765370f80f6e57aaddfd2985baf6e5 (diff) |
Add a CoreAudio backend
Code courtesy of Garin Hiebert <[email protected]>
-rw-r--r-- | Alc/ALc.c | 3 | ||||
-rw-r--r-- | Alc/coreaudio.c | 290 | ||||
-rw-r--r-- | CMakeLists.txt | 15 | ||||
-rw-r--r-- | OpenAL32/Include/alMain.h | 3 | ||||
-rw-r--r-- | alsoftrc.sample | 2 | ||||
-rw-r--r-- | config.h.in | 3 |
6 files changed, 315 insertions, 1 deletions
@@ -54,6 +54,9 @@ static BackendInfo BackendList[] = { #ifdef HAVE_ALSA { "alsa", alc_alsa_init, alc_alsa_deinit, alc_alsa_probe, EmptyFuncs }, #endif +#ifdef HAVE_COREAUDIO + { "core", alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs }, +#endif #ifdef HAVE_OSS { "oss", alc_oss_init, alc_oss_deinit, alc_oss_probe, EmptyFuncs }, #endif diff --git a/Alc/coreaudio.c b/Alc/coreaudio.c new file mode 100644 index 00000000..9fc2a3c4 --- /dev/null +++ b/Alc/coreaudio.c @@ -0,0 +1,290 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2007 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include <CoreServices/CoreServices.h> +#include <unistd.h> +#include <AudioUnit/AudioUnit.h> + +/* toggle verbose tty output among CoreAudio code */ +#define CA_VERBOSE 1 + +typedef struct { + AudioUnit OutputUnit; + ALuint FrameSize; +} ca_data; + +static const ALCchar ca_device[] = "CoreAudio Default"; + +static int ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + ALCdevice *device = (ALCdevice*)inRefCon; + ca_data *data = (ca_data*)device->ExtraData; + + aluMixData(device, ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize / data->FrameSize); + + return noErr; +} + +static ALCboolean ca_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + ComponentDescription desc; + Component comp; + ca_data *data; + OSStatus err; + + if(!deviceName) + deviceName = ca_device; + else if(strcmp(deviceName, ca_device) != 0) + return ALC_FALSE; + + /* open the default output unit */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + comp = FindNextComponent(NULL, &desc); + if(comp == NULL) + { + AL_PRINT("FindNextComponent failed\n"); + return ALC_FALSE; + } + + data = calloc(1, sizeof(*data)); + device->ExtraData = data; + + err = OpenAComponent(comp, &data->OutputUnit); + if(err != NoErr) + { + AL_PRINT("OpenAComponent failed\n"); + free(data); + device->ExtraData = NULL; + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ca_close_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + + CloseComponent(data->OutputUnit); + + free(data); + device->ExtraData = NULL; +} + +static ALCboolean ca_reset_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + AudioStreamBasicDescription streamFormat; + AURenderCallbackStruct input; + OSStatus err; + UInt32 size; + + /* init and start the default audio unit... */ + err = AudioUnitInitialize(data->OutputUnit); + if(err != NoErr) + { + AL_PRINT("AudioUnitInitialize failed\n"); + return ALC_FALSE; + } + + err = AudioOutputUnitStart(data->OutputUnit); + if(err != NoErr) + { + AL_PRINT("AudioOutputUnitStart failed\n"); + return ALC_FALSE; + } + + /* retrieve default output unit's properties (output side) */ + size = sizeof(AudioStreamBasicDescription); + err = AudioUnitGetProperty(data->OutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size); + if(err != NoErr || size != sizeof(AudioStreamBasicDescription)) + { + AL_PRINT("AudioUnitGetProperty failed\n"); + return ALC_FALSE; + } + +#if 0 + AL_PRINT("Output streamFormat of default output unit -\n"); + AL_PRINT(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket); + AL_PRINT(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame); + AL_PRINT(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel); + AL_PRINT(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket); + AL_PRINT(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame); + AL_PRINT(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate); +#endif + + /* set default output unit's input side to match output side */ + err = AudioUnitSetProperty(data->OutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size); + if(err != NoErr) + { + AL_PRINT("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + /* set AL device's sample rate */ + device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize * + streamFormat.mSampleRate / + device->Frequency); + device->Frequency = streamFormat.mSampleRate; + + /* FIXME: How to tell what channels are what in the output device, and how + * to specify what we're giving? eg, 6.0 vs 5.1 */ + switch(streamFormat.mChannelsPerFrame) + { + case 1: + device->FmtChans = DevFmtMono; + break; + case 2: + device->FmtChans = DevFmtStereo; + break; + case 4: + device->FmtChans = DevFmtQuad; + break; + case 6: + device->FmtChans = DevFmtX51; + break; + case 7: + device->FmtChans = DevFmtX61; + break; + case 8: + device->FmtChans = DevFmtX71; + break; + default: + AL_PRINT("Unhandled channel count (%d), using stereo\n", streamFormat.mChannelsPerFrame); + device->FmtChans = DevFmtStereo; + streamFormat.mChannelsPerFrame = 2; + break; + } + SetDefaultWFXChannelOrder(device); + + /* use channel count and sample rate from the default output unit's current + * parameters, but reset everything else */ + streamFormat.mFramesPerPacket = 1; + switch(device->FmtType) + { + case DevFmtUByte: + device->FmtType = DevFmtByte; + /* fall-through */ + case DevFmtByte: + streamFormat.mBitsPerChannel = 8; + streamFormat.mBytesPerPacket = streamFormat.mChannelsPerFrame; + streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame; + break; + case DevFmtUShort: + case DevFmtFloat: + device->FmtType = DevFmtShort; + /* fall-through */ + case DevFmtShort: + streamFormat.mBitsPerChannel = 16; + streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame; + streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame; + break; + } + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | + kAudioFormatFlagsNativeEndian | + kLinearPCMFormatFlagIsPacked; + + err = AudioUnitSetProperty(data->OutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription)); + if(err != NoErr) + { + AL_PRINT("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + /* setup callback */ + data->FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); + input.inputProc = ca_callback; + input.inputProcRefCon = device; + + err = AudioUnitSetProperty(data->OutputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct)); + if(err != NoErr) + { + AL_PRINT("AudioUnitSetProperty failed\n"); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void ca_stop_playback(ALCdevice *device) +{ + ca_data *data = (ca_data*)device->ExtraData; + OSStatus err; + + AudioOutputUnitStop(data->OutputUnit); + err = AudioUnitUninitialize(data->OutputUnit); + if(err != NoErr) + AL_PRINT("-- AudioUnitUninitialize failed.\n"); +} + +static ALCboolean ca_open_capture(ALCdevice *device, const ALCchar *deviceName) +{ + return ALC_FALSE; + (void)device; + (void)deviceName; +} + +static const BackendFuncs ca_funcs = { + ca_open_playback, + ca_close_playback, + ca_reset_playback, + ca_stop_playback, + ca_open_capture, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +void alc_ca_init(BackendFuncs *func_list) +{ + *func_list = ca_funcs; +} + +void alc_ca_deinit(void) +{ +} + +void alc_ca_probe(int type) +{ + if(type == DEVICE_PROBE) + AppendDeviceList(ca_device); + else if(type == ALL_DEVICE_PROBE) + AppendAllDeviceList(ca_device); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 9284a123..5631a3f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ OPTION(DSOUND "Check for DirectSound backend" ON) OPTION(WINMM "Check for Windows Multimedia backend" ON) OPTION(PORTAUDIO "Check for PortAudio backend" ON) OPTION(PULSEAUDIO "Check for PulseAudio backend" ON) +OPTION(COREAUDIO "Check for CoreAudio backend" ON) OPTION(WAVE "Enable Wave Writer backend" ON) OPTION(DLOPEN "Check for the dlopen API for loading optional libs" ON) @@ -340,6 +341,7 @@ SET(HAVE_DSOUND 0) SET(HAVE_WINMM 0) SET(HAVE_PORTAUDIO 0) SET(HAVE_PULSEAUDIO 0) +SET(HAVE_COREAUDIO 0) SET(HAVE_WAVE 0) # Check ALSA backend @@ -446,6 +448,19 @@ IF(PULSEAUDIO) ENDIF() ENDIF() +# Check CoreAudio backend +IF(COREAUDIO) + CHECK_INCLUDE_FILE(/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h HAVE_COREAUDIO_FRAMEWORK) + IF(HAVE_COREAUDIO_FRAMEWORK) + SET(HAVE_COREAUDIO 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/coreaudio.c) + SET(BACKENDS "${BACKENDS} CoreAudio,") + SET(EXTRA_LIBS /System/Library/Frameworks/CoreAudio.framework ${EXTRA_LIBS}) + SET(EXTRA_LIBS /System/Library/Frameworks/AudioUnit.framework ${EXTRA_LIBS}) + SET(EXTRA_LIBS /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS}) + ENDIF() +ENDIF() + # Optionally enable the Wave Writer backend IF(WAVE) SET(HAVE_WAVE 1) diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 74f50e8f..a7867e4d 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -322,6 +322,9 @@ void alc_wave_probe(int type); void alc_pulse_init(BackendFuncs *func_list); void alc_pulse_deinit(void); void alc_pulse_probe(int type); +void alc_ca_init(BackendFuncs *func_list); +void alc_ca_deinit(void); +void alc_ca_probe(int type); void alc_null_init(BackendFuncs *func_list); void alc_null_deinit(void); void alc_null_probe(int type); diff --git a/alsoftrc.sample b/alsoftrc.sample index c152ba59..87806a35 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -120,7 +120,7 @@ # followed by all other available backends, while 'oss' will list OSS only). # Backends prepended with - won't be available for use (eg. '-oss,' will allow # all available backends except OSS). An empty list means the default. -#drivers = pulse,alsa,oss,solaris,dsound,winmm,port,null,wave +#drivers = pulse,alsa,core,oss,solaris,dsound,winmm,port,null,wave ## excludefx: # Sets which effects to exclude, preventing apps from using them. This can diff --git a/config.h.in b/config.h.in index 3c1e7e67..c031ce45 100644 --- a/config.h.in +++ b/config.h.in @@ -25,6 +25,9 @@ /* Define if we have the PulseAudio backend */ #cmakedefine HAVE_PULSEAUDIO +/* Define if we have the CoreAudio backend */ +#cmakedefine HAVE_COREAUDIO + /* Define if we have the Wave Writer backend */ #cmakedefine HAVE_WAVE |