Add a CoreAudio backend

Code courtesy of Garin Hiebert <garinh@cheesetoast.net>
This commit is contained in:
Chris Robinson
2011-03-15 04:58:56 -07:00
parent 4899674b6c
commit d6c8bb35b4
6 changed files with 315 additions and 1 deletions
+3
View File
@@ -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
+290
View File
@@ -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);
}
+15
View File
@@ -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)
+3
View File
@@ -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);
+1 -1
View File
@@ -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
+3
View File
@@ -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