Add a CoreAudio backend
Code courtesy of Garin Hiebert <garinh@cheesetoast.net>
This commit is contained in:
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user