AuroraOpenALSoft/Alc/ALc.c
Chris Robinson 0ac9e57d28 Store the requested mono/stereo source count and sends in the device
Although the properties are set as context attributes, they are queried from
the device. If multiple contexts per device are ever supported, it would not
be straight forward about where to read the values from. This way, the
attributes can be treated as device-specific attributes that are updated when
a new context is created.
2009-07-06 03:09:01 -07:00

1384 lines
37 KiB
C

/**
* 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 <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <ctype.h>
#include "alMain.h"
#include "alSource.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alThunk.h"
#include "alSource.h"
#include "alBuffer.h"
#include "alExtension.h"
#include "alAuxEffectSlot.h"
#include "bs2b.h"
#include "alu.h"
#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
static struct {
const char *name;
void (*Init)(BackendFuncs*);
BackendFuncs Funcs;
} BackendList[] = {
#ifdef HAVE_ALSA
{ "alsa", alc_alsa_init, EmptyFuncs },
#endif
#ifdef HAVE_OSS
{ "oss", alc_oss_init, EmptyFuncs },
#endif
#ifdef HAVE_SOLARIS
{ "solaris", alc_solaris_init, EmptyFuncs },
#endif
#ifdef HAVE_DSOUND
{ "dsound", alcDSoundInit, EmptyFuncs },
#endif
#ifdef HAVE_WINMM
{ "winmm", alcWinMMInit, EmptyFuncs },
#endif
#ifdef HAVE_PORTAUDIO
{ "port", alc_pa_init, EmptyFuncs },
#endif
#ifdef HAVE_PULSEAUDIO
{ "pulse", alc_pulse_init, EmptyFuncs },
#endif
{ "wave", alc_wave_init, EmptyFuncs },
{ NULL, NULL, EmptyFuncs }
};
#undef EmptyFuncs
///////////////////////////////////////////////////////
#define ALC_EFX_MAJOR_VERSION 0x20001
#define ALC_EFX_MINOR_VERSION 0x20002
#define ALC_MAX_AUXILIARY_SENDS 0x20003
///////////////////////////////////////////////////////
// STRING and EXTENSIONS
typedef struct ALCfunction_struct
{
ALCchar *funcName;
ALvoid *address;
} ALCfunction;
static ALCfunction alcFunctions[] = {
{ "alcCreateContext", (ALvoid *) alcCreateContext },
{ "alcMakeContextCurrent", (ALvoid *) alcMakeContextCurrent },
{ "alcProcessContext", (ALvoid *) alcProcessContext },
{ "alcSuspendContext", (ALvoid *) alcSuspendContext },
{ "alcDestroyContext", (ALvoid *) alcDestroyContext },
{ "alcGetCurrentContext", (ALvoid *) alcGetCurrentContext },
{ "alcGetContextsDevice", (ALvoid *) alcGetContextsDevice },
{ "alcOpenDevice", (ALvoid *) alcOpenDevice },
{ "alcCloseDevice", (ALvoid *) alcCloseDevice },
{ "alcGetError", (ALvoid *) alcGetError },
{ "alcIsExtensionPresent", (ALvoid *) alcIsExtensionPresent },
{ "alcGetProcAddress", (ALvoid *) alcGetProcAddress },
{ "alcGetEnumValue", (ALvoid *) alcGetEnumValue },
{ "alcGetString", (ALvoid *) alcGetString },
{ "alcGetIntegerv", (ALvoid *) alcGetIntegerv },
{ "alcCaptureOpenDevice", (ALvoid *) alcCaptureOpenDevice },
{ "alcCaptureCloseDevice", (ALvoid *) alcCaptureCloseDevice },
{ "alcCaptureStart", (ALvoid *) alcCaptureStart },
{ "alcCaptureStop", (ALvoid *) alcCaptureStop },
{ "alcCaptureSamples", (ALvoid *) alcCaptureSamples },
{ NULL, (ALvoid *) NULL }
};
static ALenums enumeration[]={
// Types
{ (ALchar *)"ALC_INVALID", ALC_INVALID },
{ (ALchar *)"ALC_FALSE", ALC_FALSE },
{ (ALchar *)"ALC_TRUE", ALC_TRUE },
// ALC Properties
{ (ALchar *)"ALC_MAJOR_VERSION", ALC_MAJOR_VERSION },
{ (ALchar *)"ALC_MINOR_VERSION", ALC_MINOR_VERSION },
{ (ALchar *)"ALC_ATTRIBUTES_SIZE", ALC_ATTRIBUTES_SIZE },
{ (ALchar *)"ALC_ALL_ATTRIBUTES", ALC_ALL_ATTRIBUTES },
{ (ALchar *)"ALC_DEFAULT_DEVICE_SPECIFIER", ALC_DEFAULT_DEVICE_SPECIFIER },
{ (ALchar *)"ALC_DEVICE_SPECIFIER", ALC_DEVICE_SPECIFIER },
{ (ALchar *)"ALC_ALL_DEVICES_SPECIFIER", ALC_ALL_DEVICES_SPECIFIER },
{ (ALchar *)"ALC_DEFAULT_ALL_DEVICES_SPECIFIER", ALC_DEFAULT_ALL_DEVICES_SPECIFIER },
{ (ALchar *)"ALC_EXTENSIONS", ALC_EXTENSIONS },
{ (ALchar *)"ALC_FREQUENCY", ALC_FREQUENCY },
{ (ALchar *)"ALC_REFRESH", ALC_REFRESH },
{ (ALchar *)"ALC_SYNC", ALC_SYNC },
{ (ALchar *)"ALC_MONO_SOURCES", ALC_MONO_SOURCES },
{ (ALchar *)"ALC_STEREO_SOURCES", ALC_STEREO_SOURCES },
{ (ALchar *)"ALC_CAPTURE_DEVICE_SPECIFIER", ALC_CAPTURE_DEVICE_SPECIFIER },
{ (ALchar *)"ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER", ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER},
{ (ALchar *)"ALC_CAPTURE_SAMPLES", ALC_CAPTURE_SAMPLES },
// EFX Properties
{ (ALchar *)"ALC_EFX_MAJOR_VERSION", ALC_EFX_MAJOR_VERSION },
{ (ALchar *)"ALC_EFX_MINOR_VERSION", ALC_EFX_MINOR_VERSION },
{ (ALchar *)"ALC_MAX_AUXILIARY_SENDS", ALC_MAX_AUXILIARY_SENDS },
// ALC Error Message
{ (ALchar *)"ALC_NO_ERROR", ALC_NO_ERROR },
{ (ALchar *)"ALC_INVALID_DEVICE", ALC_INVALID_DEVICE },
{ (ALchar *)"ALC_INVALID_CONTEXT", ALC_INVALID_CONTEXT },
{ (ALchar *)"ALC_INVALID_ENUM", ALC_INVALID_ENUM },
{ (ALchar *)"ALC_INVALID_VALUE", ALC_INVALID_VALUE },
{ (ALchar *)"ALC_OUT_OF_MEMORY", ALC_OUT_OF_MEMORY },
{ (ALchar *)NULL, (ALenum)0 }
};
// Error strings
static const ALCchar alcNoError[] = "No Error";
static const ALCchar alcErrInvalidDevice[] = "Invalid Device";
static const ALCchar alcErrInvalidContext[] = "Invalid Context";
static const ALCchar alcErrInvalidEnum[] = "Invalid Enum";
static const ALCchar alcErrInvalidValue[] = "Invalid Value";
static const ALCchar alcErrOutOfMemory[] = "Out of Memory";
// Context strings
static ALCchar alcDeviceList[2048];
static ALCchar alcAllDeviceList[2048];
static ALCchar alcCaptureDeviceList[2048];
// Default is always the first in the list
static ALCchar *alcDefaultDeviceSpecifier = alcDeviceList;
static ALCchar *alcDefaultAllDeviceSpecifier = alcAllDeviceList;
static ALCchar *alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceList;
static ALCchar alcExtensionList[] = "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE ALC_EXT_EFX";
static ALCint alcMajorVersion = 1;
static ALCint alcMinorVersion = 1;
static ALCint alcEFXMajorVersion = 1;
static ALCint alcEFXMinorVersion = 0;
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// Global Variables
static ALCdevice *g_pDeviceList = NULL;
static ALCuint g_ulDeviceCount = 0;
static CRITICAL_SECTION g_csMutex;
// Context List
static ALCcontext *g_pContextList = NULL;
static ALCuint g_ulContextCount = 0;
// Context Error
static ALCenum g_eLastContextError = ALC_NO_ERROR;
static ALboolean init_done = AL_FALSE;
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// ALC Related helper functions
#ifdef _WIN32
BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
(void)lpReserved;
// Perform actions based on the reason for calling.
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
break;
case DLL_PROCESS_DETACH:
if(!init_done)
break;
ReleaseALC();
ReleaseALBuffers();
ReleaseALEffects();
ReleaseALFilters();
FreeALConfig();
ALTHUNK_EXIT();
DeleteCriticalSection(&g_csMutex);
break;
}
return TRUE;
}
#else
#ifdef HAVE_GCC_DESTRUCTOR
static void my_deinit() __attribute__((destructor));
static void my_deinit()
{
static ALenum once = AL_FALSE;
if(once || !init_done) return;
once = AL_TRUE;
ReleaseALC();
ReleaseALBuffers();
ReleaseALEffects();
ReleaseALFilters();
FreeALConfig();
ALTHUNK_EXIT();
DeleteCriticalSection(&g_csMutex);
}
#endif
#endif
static void InitAL(void)
{
if(!init_done)
{
int i;
const char *devs, *str;
init_done = AL_TRUE;
InitializeCriticalSection(&g_csMutex);
ALTHUNK_INIT();
ReadALConfig();
devs = GetConfigValue(NULL, "drivers", "");
if(devs[0])
{
int n;
size_t len;
const char *next = devs;
i = 0;
do {
devs = next;
next = strchr(devs, ',');
if(!devs[0] || devs[0] == ',')
continue;
len = (next ? ((size_t)(next-devs)) : strlen(devs));
for(n = i;BackendList[n].Init;n++)
{
if(len == strlen(BackendList[n].name) &&
strncmp(BackendList[n].name, devs, len) == 0)
{
const char *name = BackendList[i].name;
void (*Init)(BackendFuncs*) = BackendList[i].Init;
BackendList[i].name = BackendList[n].name;
BackendList[i].Init = BackendList[n].Init;
BackendList[n].name = name;
BackendList[n].Init = Init;
i++;
}
}
} while(next++);
BackendList[i].name = NULL;
BackendList[i].Init = NULL;
}
for(i = 0;BackendList[i].Init;i++)
BackendList[i].Init(&BackendList[i].Funcs);
str = GetConfigValue(NULL, "stereodup", "false");
DuplicateStereo = (strcasecmp(str, "true") == 0 ||
strcasecmp(str, "yes") == 0 ||
strcasecmp(str, "on") == 0 ||
atoi(str) != 0);
str = GetConfigValue(NULL, "excludefx", "");
if(str[0])
{
const struct {
const char *name;
int type;
} EffectList[] = {
{ "eaxreverb", EAXREVERB },
{ "reverb", REVERB },
{ "echo", ECHO },
{ NULL, 0 }
};
int n;
size_t len;
const char *next = str;
do {
str = next;
next = strchr(str, ',');
if(!str[0] || next == str)
continue;
len = (next ? ((size_t)(next-str)) : strlen(str));
for(n = 0;EffectList[n].name;n++)
{
if(len == strlen(EffectList[n].name) &&
strncmp(EffectList[n].name, str, len) == 0)
DisabledEffects[EffectList[n].type] = AL_TRUE;
}
} while(next++);
}
}
}
ALCchar *AppendDeviceList(char *name)
{
static size_t pos;
ALCchar *ret = alcDeviceList+pos;
if(pos >= sizeof(alcDeviceList))
{
AL_PRINT("Not enough room to add %s!\n", name);
return alcDeviceList + sizeof(alcDeviceList) - 1;
}
pos += snprintf(alcDeviceList+pos, sizeof(alcDeviceList)-pos-1, "%s", name) + 1;
return ret;
}
ALCchar *AppendAllDeviceList(char *name)
{
static size_t pos;
ALCchar *ret = alcAllDeviceList+pos;
if(pos >= sizeof(alcAllDeviceList))
{
AL_PRINT("Not enough room to add %s!\n", name);
return alcAllDeviceList + sizeof(alcAllDeviceList) - 1;
}
pos += snprintf(alcAllDeviceList+pos, sizeof(alcAllDeviceList)-pos-1, "%s", name) + 1;
return ret;
}
ALCchar *AppendCaptureDeviceList(char *name)
{
static size_t pos;
ALCchar *ret = alcCaptureDeviceList+pos;
if(pos >= sizeof(alcCaptureDeviceList))
{
AL_PRINT("Not enough room to add %s!\n", name);
return alcCaptureDeviceList + sizeof(alcCaptureDeviceList) - 1;
}
pos += snprintf(alcCaptureDeviceList+pos, sizeof(alcCaptureDeviceList)-pos-1, "%s", name) + 1;
return ret;
}
/*
IsContext
Check pContext is a valid Context pointer
*/
static ALCboolean IsContext(ALCcontext *pContext)
{
ALCcontext *pTempContext;
pTempContext = g_pContextList;
while (pTempContext && pTempContext != pContext)
pTempContext = pTempContext->next;
return (pTempContext ? ALC_TRUE : ALC_FALSE);
}
/*
SetALCError
Store latest ALC Error
*/
ALCvoid SetALCError(ALenum errorCode)
{
g_eLastContextError = errorCode;
}
/*
SuspendContext
Thread-safe entry
*/
ALCvoid SuspendContext(ALCcontext *pContext)
{
(void)pContext;
EnterCriticalSection(&g_csMutex);
}
/*
ProcessContext
Thread-safe exit
*/
ALCvoid ProcessContext(ALCcontext *pContext)
{
(void)pContext;
LeaveCriticalSection(&g_csMutex);
}
/*
InitContext
Initialize Context variables
*/
static ALvoid InitContext(ALCcontext *pContext)
{
int level;
//Initialise listener
pContext->Listener.Gain = 1.0f;
pContext->Listener.MetersPerUnit = 1.0f;
pContext->Listener.Position[0] = 0.0f;
pContext->Listener.Position[1] = 0.0f;
pContext->Listener.Position[2] = 0.0f;
pContext->Listener.Velocity[0] = 0.0f;
pContext->Listener.Velocity[1] = 0.0f;
pContext->Listener.Velocity[2] = 0.0f;
pContext->Listener.Forward[0] = 0.0f;
pContext->Listener.Forward[1] = 0.0f;
pContext->Listener.Forward[2] = -1.0f;
pContext->Listener.Up[0] = 0.0f;
pContext->Listener.Up[1] = 1.0f;
pContext->Listener.Up[2] = 0.0f;
//Validate pContext
pContext->LastError = AL_NO_ERROR;
pContext->InUse = AL_FALSE;
//Set output format
pContext->Frequency = pContext->Device->Frequency;
//Set globals
pContext->DistanceModel = AL_INVERSE_DISTANCE_CLAMPED;
pContext->DopplerFactor = 1.0f;
pContext->DopplerVelocity = 1.0f;
pContext->flSpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
pContext->ExtensionList = "AL_EXTX_buffer_sub_data AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_OFFSET AL_EXTX_source_distance_model AL_LOKI_quadriphonic";
level = GetConfigValueInt(NULL, "cf_level", 0);
if(level > 0 && level <= 6)
{
pContext->bs2b = calloc(1, sizeof(*pContext->bs2b));
bs2b_set_srate(pContext->bs2b, pContext->Frequency);
bs2b_set_level(pContext->bs2b, level);
}
aluInitPanning(pContext);
}
/*
ExitContext
Clean up Context, destroy any remaining Sources
*/
static ALCvoid ExitContext(ALCcontext *pContext)
{
//Invalidate context
pContext->LastError = AL_NO_ERROR;
pContext->InUse = AL_FALSE;
free(pContext->bs2b);
pContext->bs2b = NULL;
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// ALC Functions calls
// This should probably move to another c file but for now ...
ALCAPI ALCdevice* ALCAPIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
{
ALCboolean DeviceFound = ALC_FALSE;
ALCdevice *pDevice = NULL;
ALCint i;
InitAL();
if(SampleSize <= 0)
{
SetALCError(ALC_INVALID_VALUE);
return NULL;
}
if(deviceName && !deviceName[0])
deviceName = NULL;
pDevice = malloc(sizeof(ALCdevice));
if (pDevice)
{
//Initialise device structure
memset(pDevice, 0, sizeof(ALCdevice));
//Validate device
pDevice->IsCaptureDevice = AL_TRUE;
pDevice->Frequency = frequency;
pDevice->Format = format;
for(i = 0;BackendList[i].Init;i++)
{
pDevice->Funcs = &BackendList[i].Funcs;
if(ALCdevice_OpenCapture(pDevice, deviceName, frequency, format, SampleSize))
{
SuspendContext(NULL);
pDevice->next = g_pDeviceList;
g_pDeviceList = pDevice;
g_ulDeviceCount++;
ProcessContext(NULL);
DeviceFound = ALC_TRUE;
break;
}
}
if(!DeviceFound)
{
SetALCError(ALC_INVALID_VALUE);
free(pDevice);
pDevice = NULL;
}
}
else
SetALCError(ALC_OUT_OF_MEMORY);
return pDevice;
}
ALCAPI ALCboolean ALCAPIENTRY alcCaptureCloseDevice(ALCdevice *pDevice)
{
ALCboolean bReturn = ALC_FALSE;
ALCdevice **list;
if ((pDevice)&&(pDevice->IsCaptureDevice))
{
SuspendContext(NULL);
list = &g_pDeviceList;
while(*list != pDevice)
list = &(*list)->next;
*list = (*list)->next;
g_ulDeviceCount--;
ProcessContext(NULL);
ALCdevice_CloseCapture(pDevice);
free(pDevice);
bReturn = ALC_TRUE;
}
else
SetALCError(ALC_INVALID_DEVICE);
return bReturn;
}
ALCAPI void ALCAPIENTRY alcCaptureStart(ALCdevice *pDevice)
{
if ((pDevice)&&(pDevice->IsCaptureDevice))
ALCdevice_StartCapture(pDevice);
else
SetALCError(ALC_INVALID_DEVICE);
}
ALCAPI void ALCAPIENTRY alcCaptureStop(ALCdevice *pDevice)
{
if ((pDevice)&&(pDevice->IsCaptureDevice))
ALCdevice_StopCapture(pDevice);
else
SetALCError(ALC_INVALID_DEVICE);
}
ALCAPI void ALCAPIENTRY alcCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCsizei lSamples)
{
if ((pDevice) && (pDevice->IsCaptureDevice))
ALCdevice_CaptureSamples(pDevice, pBuffer, lSamples);
else
SetALCError(ALC_INVALID_DEVICE);
}
/*
alcGetError
Return last ALC generated error code
*/
ALCAPI ALCenum ALCAPIENTRY alcGetError(ALCdevice *device)
{
ALCenum errorCode;
(void)device;
errorCode = g_eLastContextError;
g_eLastContextError = ALC_NO_ERROR;
return errorCode;
}
/*
alcSuspendContext
Not functional
*/
ALCAPI ALCvoid ALCAPIENTRY alcSuspendContext(ALCcontext *pContext)
{
// Not a lot happens here !
(void)pContext;
}
/*
alcProcessContext
Not functional
*/
ALCAPI ALCvoid ALCAPIENTRY alcProcessContext(ALCcontext *pContext)
{
// Not a lot happens here !
(void)pContext;
}
/*
alcGetString
Returns information about the Device, and error strings
*/
ALCAPI const ALCchar* ALCAPIENTRY alcGetString(ALCdevice *pDevice,ALCenum param)
{
const ALCchar *value = NULL;
InitAL();
switch (param)
{
case ALC_NO_ERROR:
value = alcNoError;
break;
case ALC_INVALID_ENUM:
value = alcErrInvalidEnum;
break;
case ALC_INVALID_VALUE:
value = alcErrInvalidValue;
break;
case ALC_INVALID_DEVICE:
value = alcErrInvalidDevice;
break;
case ALC_INVALID_CONTEXT:
value = alcErrInvalidContext;
break;
case ALC_OUT_OF_MEMORY:
value = alcErrOutOfMemory;
break;
case ALC_DEFAULT_DEVICE_SPECIFIER:
value = alcDefaultDeviceSpecifier;
break;
case ALC_DEVICE_SPECIFIER:
if (pDevice)
value = pDevice->szDeviceName;
else
value = alcDeviceList;
break;
case ALC_ALL_DEVICES_SPECIFIER:
value = alcAllDeviceList;
break;
case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
value = alcDefaultAllDeviceSpecifier;
break;
case ALC_CAPTURE_DEVICE_SPECIFIER:
if (pDevice)
value = pDevice->szDeviceName;
else
value = alcCaptureDeviceList;
break;
case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
value = alcCaptureDefaultDeviceSpecifier;
break;
case ALC_EXTENSIONS:
value = alcExtensionList;
break;
default:
SetALCError(ALC_INVALID_ENUM);
break;
}
return value;
}
/*
alcGetIntegerv
Returns information about the Device and the version of Open AL
*/
ALCAPI ALCvoid ALCAPIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsizei size,ALCint *data)
{
InitAL();
if ((device)&&(device->IsCaptureDevice))
{
SuspendContext(NULL);
// Capture device
switch (param)
{
case ALC_CAPTURE_SAMPLES:
if ((size) && (data))
*data = ALCdevice_AvailableSamples(device);
else
SetALCError(ALC_INVALID_VALUE);
break;
default:
SetALCError(ALC_INVALID_ENUM);
break;
}
ProcessContext(NULL);
}
else
{
if(data)
{
// Playback Device
switch (param)
{
case ALC_MAJOR_VERSION:
if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = alcMajorVersion;
break;
case ALC_MINOR_VERSION:
if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = alcMinorVersion;
break;
case ALC_EFX_MAJOR_VERSION:
if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = alcEFXMajorVersion;
break;
case ALC_EFX_MINOR_VERSION:
if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = alcEFXMinorVersion;
break;
case ALC_MAX_AUXILIARY_SENDS:
if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = (device?device->NumAuxSends:MAX_SENDS);
break;
case ALC_ATTRIBUTES_SIZE:
if(!device)
SetALCError(ALC_INVALID_DEVICE);
else if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = 13;
break;
case ALC_ALL_ATTRIBUTES:
if(!device)
SetALCError(ALC_INVALID_DEVICE);
else if (size < 13)
SetALCError(ALC_INVALID_VALUE);
else
{
int i = 0;
SuspendContext(NULL);
data[i++] = ALC_FREQUENCY;
data[i++] = device->Frequency;
data[i++] = ALC_REFRESH;
data[i++] = device->Frequency / device->UpdateSize;
data[i++] = ALC_SYNC;
data[i++] = ALC_FALSE;
data[i++] = ALC_MONO_SOURCES;
data[i++] = device->lNumMonoSources;
data[i++] = ALC_STEREO_SOURCES;
data[i++] = device->lNumStereoSources;
data[i++] = ALC_MAX_AUXILIARY_SENDS;
data[i++] = device->NumAuxSends;
data[i++] = 0;
ProcessContext(NULL);
}
break;
case ALC_FREQUENCY:
if(!device)
SetALCError(ALC_INVALID_DEVICE);
else if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = device->Frequency;
break;
case ALC_REFRESH:
if(!device)
SetALCError(ALC_INVALID_DEVICE);
else if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = device->Frequency / device->UpdateSize;
break;
case ALC_SYNC:
if(!device)
SetALCError(ALC_INVALID_DEVICE);
else if(!size)
SetALCError(ALC_INVALID_VALUE);
else
*data = ALC_FALSE;
break;
case ALC_MONO_SOURCES:
if(!device || !device->Context)
SetALCError(ALC_INVALID_DEVICE);
else if (size != 1)
SetALCError(ALC_INVALID_VALUE);
else
*data = device->lNumMonoSources;
break;
case ALC_STEREO_SOURCES:
if(!device || !device->Context)
SetALCError(ALC_INVALID_DEVICE);
else if (size != 1)
SetALCError(ALC_INVALID_VALUE);
else
*data = device->lNumStereoSources;
break;
default:
SetALCError(ALC_INVALID_ENUM);
break;
}
}
else if(size)
SetALCError(ALC_INVALID_VALUE);
}
return;
}
/*
alcIsExtensionPresent
Determines if there is support for a particular extension
*/
ALCAPI ALCboolean ALCAPIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName)
{
ALCboolean bResult = ALC_FALSE;
(void)device;
if (extName)
{
const char *ptr;
size_t len;
len = strlen(extName);
ptr = alcExtensionList;
while(ptr && *ptr)
{
if(strncasecmp(ptr, extName, len) == 0 &&
(ptr[len] == '\0' || isspace(ptr[len])))
{
bResult = ALC_TRUE;
break;
}
if((ptr=strchr(ptr, ' ')) != NULL)
{
do {
++ptr;
} while(isspace(*ptr));
}
}
}
else
SetALCError(ALC_INVALID_VALUE);
return bResult;
}
/*
alcGetProcAddress
Retrieves the function address for a particular extension function
*/
ALCAPI ALCvoid * ALCAPIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName)
{
ALCvoid *pFunction = NULL;
ALsizei i = 0;
(void)device;
if (funcName)
{
while(alcFunctions[i].funcName &&
strcmp(alcFunctions[i].funcName,funcName) != 0)
i++;
pFunction = alcFunctions[i].address;
}
else
SetALCError(ALC_INVALID_VALUE);
return pFunction;
}
/*
alcGetEnumValue
Get the value for a particular ALC Enumerated Value
*/
ALCAPI ALCenum ALCAPIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName)
{
ALsizei i = 0;
ALCenum val;
(void)device;
while ((enumeration[i].enumName)&&(strcmp(enumeration[i].enumName,enumName)))
i++;
val = enumeration[i].value;
if(!enumeration[i].enumName)
SetALCError(ALC_INVALID_VALUE);
return val;
}
/*
alcCreateContext
Create and attach a Context to a particular Device.
*/
ALCAPI ALCcontext* ALCAPIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
{
ALCcontext *ALContext = NULL;
ALuint ulAttributeIndex, ulRequestedStereoSources;
ALuint RequestedSends;
if ((device)&&(!device->IsCaptureDevice))
{
// Reset Context Last Error code
g_eLastContextError = ALC_NO_ERROR;
// Current implementation only allows one Context per Device
if(!device->Context)
{
ALContext = calloc(1, sizeof(ALCcontext));
if(!ALContext)
{
SetALCError(ALC_OUT_OF_MEMORY);
return NULL;
}
ALContext->Device = device;
InitContext(ALContext);
device->Context = ALContext;
SuspendContext(NULL);
ALContext->next = g_pContextList;
g_pContextList = ALContext;
g_ulContextCount++;
ProcessContext(NULL);
// Check for attributes
if (attrList)
{
ALCint numMono = ALContext->Device->lNumMonoSources;
ALCint numStereo = ALContext->Device->lNumStereoSources;
ALCuint numSends = ALContext->Device->NumAuxSends;
ulAttributeIndex = 0;
while ((ulAttributeIndex < 10) && (attrList[ulAttributeIndex]))
{
if (attrList[ulAttributeIndex] == ALC_STEREO_SOURCES)
{
ulRequestedStereoSources = attrList[ulAttributeIndex + 1];
if (ulRequestedStereoSources > ALContext->Device->MaxNoOfSources)
ulRequestedStereoSources = ALContext->Device->MaxNoOfSources;
numStereo = ulRequestedStereoSources;
numMono = ALContext->Device->MaxNoOfSources - numStereo;
}
if(attrList[ulAttributeIndex] == ALC_MAX_AUXILIARY_SENDS)
{
RequestedSends = attrList[ulAttributeIndex + 1];
if(RequestedSends > ALContext->Device->NumAuxSends)
RequestedSends = ALContext->Device->NumAuxSends;
numSends = RequestedSends;
}
ulAttributeIndex += 2;
}
ALContext->Device->lNumMonoSources = numMono;
ALContext->Device->lNumStereoSources = numStereo;
ALContext->Device->NumAuxSends = numSends;
}
}
else
{
SetALCError(ALC_INVALID_VALUE);
ALContext = NULL;
}
}
else
SetALCError(ALC_INVALID_DEVICE);
return ALContext;
}
/*
alcDestroyContext
Remove a Context
*/
ALCAPI ALCvoid ALCAPIENTRY alcDestroyContext(ALCcontext *context)
{
ALCcontext **list;
InitAL();
// Lock context list
SuspendContext(NULL);
if (IsContext(context))
{
// Lock context
SuspendContext(context);
ReleaseALSources(context);
ReleaseALAuxiliaryEffectSlots(context);
context->Device->Context = NULL;
list = &g_pContextList;
while(*list != context)
list = &(*list)->next;
*list = (*list)->next;
g_ulContextCount--;
// Unlock context
ProcessContext(context);
ExitContext(context);
// Free memory (MUST do this after ProcessContext)
memset(context, 0, sizeof(ALCcontext));
free(context);
}
else
SetALCError(ALC_INVALID_CONTEXT);
ProcessContext(NULL);
}
/*
alcGetCurrentContext
Returns the currently active Context
*/
ALCAPI ALCcontext * ALCAPIENTRY alcGetCurrentContext(ALCvoid)
{
ALCcontext *pContext = NULL;
InitAL();
SuspendContext(NULL);
pContext = g_pContextList;
while ((pContext) && (!pContext->InUse))
pContext = pContext->next;
ProcessContext(NULL);
return pContext;
}
/*
alcGetContextsDevice
Returns the Device that a particular Context is attached to
*/
ALCAPI ALCdevice* ALCAPIENTRY alcGetContextsDevice(ALCcontext *pContext)
{
ALCdevice *pDevice = NULL;
InitAL();
SuspendContext(NULL);
if (IsContext(pContext))
pDevice = pContext->Device;
else
SetALCError(ALC_INVALID_CONTEXT);
ProcessContext(NULL);
return pDevice;
}
/*
alcMakeContextCurrent
Makes the given Context the active Context
*/
ALCAPI ALCboolean ALCAPIENTRY alcMakeContextCurrent(ALCcontext *context)
{
ALCcontext *ALContext;
ALboolean bReturn = AL_TRUE;
InitAL();
SuspendContext(NULL);
// context must be a valid Context or NULL
if ((IsContext(context)) || (context == NULL))
{
if ((ALContext=alcGetCurrentContext()))
{
SuspendContext(ALContext);
ALContext->InUse=AL_FALSE;
ProcessContext(ALContext);
}
if ((ALContext=context) && (ALContext->Device))
{
SuspendContext(ALContext);
ALContext->InUse=AL_TRUE;
ProcessContext(ALContext);
}
}
else
{
SetALCError(ALC_INVALID_CONTEXT);
bReturn = AL_FALSE;
}
ProcessContext(NULL);
return bReturn;
}
/*
alcOpenDevice
Open the Device specified.
*/
ALCAPI ALCdevice* ALCAPIENTRY alcOpenDevice(const ALCchar *deviceName)
{
ALboolean bDeviceFound = AL_FALSE;
ALCdevice *device;
ALint i;
InitAL();
if(deviceName && !deviceName[0])
deviceName = NULL;
device = malloc(sizeof(ALCdevice));
if (device)
{
const char *fmt;
//Initialise device structure
memset(device, 0, sizeof(ALCdevice));
//Validate device
device->IsCaptureDevice = AL_FALSE;
//Set output format
device->Frequency = GetConfigValueInt(NULL, "frequency", SWMIXER_OUTPUT_RATE);
if((ALint)device->Frequency <= 0)
device->Frequency = SWMIXER_OUTPUT_RATE;
fmt = GetConfigValue(NULL, "format", "AL_FORMAT_STEREO16");
if(fmt[0])
device->Format = alGetEnumValue(fmt);
if(!aluChannelsFromFormat(device->Format))
device->Format = AL_FORMAT_STEREO16;
device->UpdateSize = GetConfigValueInt(NULL, "refresh", 4096);
if((ALint)device->UpdateSize <= 0)
device->UpdateSize = 4096;
device->MaxNoOfSources = GetConfigValueInt(NULL, "sources", 256);
if((ALint)device->MaxNoOfSources <= 0)
device->MaxNoOfSources = 256;
device->AuxiliaryEffectSlotMax = GetConfigValueInt(NULL, "slots", 4);
if((ALint)device->AuxiliaryEffectSlotMax <= 0)
device->AuxiliaryEffectSlotMax = 4;
device->lNumStereoSources = 1;
device->lNumMonoSources = device->MaxNoOfSources - device->lNumStereoSources;
device->NumAuxSends = GetConfigValueInt(NULL, "sends", MAX_SENDS);
if(device->NumAuxSends > MAX_SENDS)
device->NumAuxSends = MAX_SENDS;
// Find a playback device to open
SuspendContext(NULL);
for(i = 0;BackendList[i].Init;i++)
{
device->Funcs = &BackendList[i].Funcs;
if(ALCdevice_OpenPlayback(device, deviceName))
{
device->next = g_pDeviceList;
g_pDeviceList = device;
g_ulDeviceCount++;
bDeviceFound = AL_TRUE;
break;
}
}
ProcessContext(NULL);
if (!bDeviceFound)
{
// No suitable output device found
SetALCError(ALC_INVALID_VALUE);
free(device);
device = NULL;
}
}
else
SetALCError(ALC_OUT_OF_MEMORY);
return device;
}
/*
alcCloseDevice
Close the specified Device
*/
ALCAPI ALCboolean ALCAPIENTRY alcCloseDevice(ALCdevice *pDevice)
{
ALCboolean bReturn = ALC_FALSE;
ALCdevice **list;
if ((pDevice)&&(!pDevice->IsCaptureDevice))
{
SuspendContext(NULL);
list = &g_pDeviceList;
while(*list != pDevice)
list = &(*list)->next;
*list = (*list)->next;
g_ulDeviceCount--;
ProcessContext(NULL);
if(pDevice->Context)
{
#ifdef _DEBUG
AL_PRINT("alcCloseDevice(): destroying 1 Context\n");
#endif
alcDestroyContext(pDevice->Context);
}
ALCdevice_ClosePlayback(pDevice);
//Release device structure
memset(pDevice, 0, sizeof(ALCdevice));
free(pDevice);
bReturn = ALC_TRUE;
}
else
SetALCError(ALC_INVALID_DEVICE);
return bReturn;
}
ALCvoid ReleaseALC(ALCvoid)
{
#ifdef _DEBUG
if(g_ulDeviceCount > 0)
AL_PRINT("exit(): closing %u Device%s\n", g_ulDeviceCount, (g_ulDeviceCount>1)?"s":"");
#endif
while(g_pDeviceList)
{
if(g_pDeviceList->IsCaptureDevice)
alcCaptureCloseDevice(g_pDeviceList);
else
alcCloseDevice(g_pDeviceList);
}
}
///////////////////////////////////////////////////////