Chris Robinson 5736bbc3e4 Add a source property to skip channel virtualization for multi-channel buffers
With virtualization disabled, channels are mapped directly from input to output
and any input channel that is missing an output is dropped. Any virtualization
effects (such as HRTF filters) for positional cues are also skipped.

The idea is to allow applications a way to play pre-filtered audio that channel
vitualization effects could interfere with.
2011-06-29 23:18:49 -07:00

2898 lines
81 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"
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
#define INITGUID
#include <windows.h>
#ifdef HAVE_GUIDDEF_H
#include <guiddef.h>
#else
#include <initguid.h>
#endif
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
#endif
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <ctype.h>
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#include "alMain.h"
#include "alSource.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alThunk.h"
#include "alSource.h"
#include "alBuffer.h"
#include "alAuxEffectSlot.h"
#include "bs2b.h"
#include "alu.h"
#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
typedef struct BackendInfo {
const char *name;
void (*Init)(BackendFuncs*);
void (*Deinit)(void);
void (*Probe)(enum DevProbe);
BackendFuncs Funcs;
} BackendInfo;
static BackendInfo BackendList[] = {
#ifdef HAVE_PULSEAUDIO
{ "pulse", alc_pulse_init, alc_pulse_deinit, alc_pulse_probe, EmptyFuncs },
#endif
#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
#ifdef HAVE_SOLARIS
{ "solaris", alc_solaris_init, alc_solaris_deinit, alc_solaris_probe, EmptyFuncs },
#endif
#ifdef HAVE_SNDIO
{ "sndio", alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs },
#endif
#ifdef HAVE_MMDEVAPI
{ "mmdevapi", alcMMDevApiInit, alcMMDevApiDeinit, alcMMDevApiProbe, EmptyFuncs },
#endif
#ifdef HAVE_DSOUND
{ "dsound", alcDSoundInit, alcDSoundDeinit, alcDSoundProbe, EmptyFuncs },
#endif
#ifdef HAVE_WINMM
{ "winmm", alcWinMMInit, alcWinMMDeinit, alcWinMMProbe, EmptyFuncs },
#endif
#ifdef HAVE_PORTAUDIO
{ "port", alc_pa_init, alc_pa_deinit, alc_pa_probe, EmptyFuncs },
#endif
#ifdef HAVE_OPENSL
{ "opensl", alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs },
#endif
{ "null", alc_null_init, alc_null_deinit, alc_null_probe, EmptyFuncs },
#ifdef HAVE_WAVE
{ "wave", alc_wave_init, alc_wave_deinit, alc_wave_probe, EmptyFuncs },
#endif
{ NULL, NULL, NULL, NULL, EmptyFuncs }
};
static BackendInfo BackendLoopback = {
"loopback", alc_loopback_init, alc_loopback_deinit, alc_loopback_probe, EmptyFuncs
};
#undef EmptyFuncs
///////////////////////////////////////////////////////
// STRING and EXTENSIONS
typedef struct ALCfunction {
const ALCchar *funcName;
ALCvoid *address;
} ALCfunction;
typedef struct ALCenums {
const ALCchar *enumName;
ALCenum value;
} ALCenums;
static const ALCfunction alcFunctions[] = {
{ "alcCreateContext", (ALCvoid *) alcCreateContext },
{ "alcMakeContextCurrent", (ALCvoid *) alcMakeContextCurrent },
{ "alcProcessContext", (ALCvoid *) alcProcessContext },
{ "alcSuspendContext", (ALCvoid *) alcSuspendContext },
{ "alcDestroyContext", (ALCvoid *) alcDestroyContext },
{ "alcGetCurrentContext", (ALCvoid *) alcGetCurrentContext },
{ "alcGetContextsDevice", (ALCvoid *) alcGetContextsDevice },
{ "alcOpenDevice", (ALCvoid *) alcOpenDevice },
{ "alcCloseDevice", (ALCvoid *) alcCloseDevice },
{ "alcGetError", (ALCvoid *) alcGetError },
{ "alcIsExtensionPresent", (ALCvoid *) alcIsExtensionPresent },
{ "alcGetProcAddress", (ALCvoid *) alcGetProcAddress },
{ "alcGetEnumValue", (ALCvoid *) alcGetEnumValue },
{ "alcGetString", (ALCvoid *) alcGetString },
{ "alcGetIntegerv", (ALCvoid *) alcGetIntegerv },
{ "alcCaptureOpenDevice", (ALCvoid *) alcCaptureOpenDevice },
{ "alcCaptureCloseDevice", (ALCvoid *) alcCaptureCloseDevice },
{ "alcCaptureStart", (ALCvoid *) alcCaptureStart },
{ "alcCaptureStop", (ALCvoid *) alcCaptureStop },
{ "alcCaptureSamples", (ALCvoid *) alcCaptureSamples },
{ "alcSetThreadContext", (ALCvoid *) alcSetThreadContext },
{ "alcGetThreadContext", (ALCvoid *) alcGetThreadContext },
{ "alcLoopbackOpenDeviceSOFT", (ALCvoid *) alcLoopbackOpenDeviceSOFT},
{ "alcIsRenderFormatSupportedSOFT",(ALCvoid *) alcIsRenderFormatSupportedSOFT},
{ "alcRenderSamplesSOFT", (ALCvoid *) alcRenderSamplesSOFT },
{ "alEnable", (ALCvoid *) alEnable },
{ "alDisable", (ALCvoid *) alDisable },
{ "alIsEnabled", (ALCvoid *) alIsEnabled },
{ "alGetString", (ALCvoid *) alGetString },
{ "alGetBooleanv", (ALCvoid *) alGetBooleanv },
{ "alGetIntegerv", (ALCvoid *) alGetIntegerv },
{ "alGetFloatv", (ALCvoid *) alGetFloatv },
{ "alGetDoublev", (ALCvoid *) alGetDoublev },
{ "alGetBoolean", (ALCvoid *) alGetBoolean },
{ "alGetInteger", (ALCvoid *) alGetInteger },
{ "alGetFloat", (ALCvoid *) alGetFloat },
{ "alGetDouble", (ALCvoid *) alGetDouble },
{ "alGetError", (ALCvoid *) alGetError },
{ "alIsExtensionPresent", (ALCvoid *) alIsExtensionPresent },
{ "alGetProcAddress", (ALCvoid *) alGetProcAddress },
{ "alGetEnumValue", (ALCvoid *) alGetEnumValue },
{ "alListenerf", (ALCvoid *) alListenerf },
{ "alListener3f", (ALCvoid *) alListener3f },
{ "alListenerfv", (ALCvoid *) alListenerfv },
{ "alListeneri", (ALCvoid *) alListeneri },
{ "alListener3i", (ALCvoid *) alListener3i },
{ "alListeneriv", (ALCvoid *) alListeneriv },
{ "alGetListenerf", (ALCvoid *) alGetListenerf },
{ "alGetListener3f", (ALCvoid *) alGetListener3f },
{ "alGetListenerfv", (ALCvoid *) alGetListenerfv },
{ "alGetListeneri", (ALCvoid *) alGetListeneri },
{ "alGetListener3i", (ALCvoid *) alGetListener3i },
{ "alGetListeneriv", (ALCvoid *) alGetListeneriv },
{ "alGenSources", (ALCvoid *) alGenSources },
{ "alDeleteSources", (ALCvoid *) alDeleteSources },
{ "alIsSource", (ALCvoid *) alIsSource },
{ "alSourcef", (ALCvoid *) alSourcef },
{ "alSource3f", (ALCvoid *) alSource3f },
{ "alSourcefv", (ALCvoid *) alSourcefv },
{ "alSourcei", (ALCvoid *) alSourcei },
{ "alSource3i", (ALCvoid *) alSource3i },
{ "alSourceiv", (ALCvoid *) alSourceiv },
{ "alGetSourcef", (ALCvoid *) alGetSourcef },
{ "alGetSource3f", (ALCvoid *) alGetSource3f },
{ "alGetSourcefv", (ALCvoid *) alGetSourcefv },
{ "alGetSourcei", (ALCvoid *) alGetSourcei },
{ "alGetSource3i", (ALCvoid *) alGetSource3i },
{ "alGetSourceiv", (ALCvoid *) alGetSourceiv },
{ "alSourcePlayv", (ALCvoid *) alSourcePlayv },
{ "alSourceStopv", (ALCvoid *) alSourceStopv },
{ "alSourceRewindv", (ALCvoid *) alSourceRewindv },
{ "alSourcePausev", (ALCvoid *) alSourcePausev },
{ "alSourcePlay", (ALCvoid *) alSourcePlay },
{ "alSourceStop", (ALCvoid *) alSourceStop },
{ "alSourceRewind", (ALCvoid *) alSourceRewind },
{ "alSourcePause", (ALCvoid *) alSourcePause },
{ "alSourceQueueBuffers", (ALCvoid *) alSourceQueueBuffers },
{ "alSourceUnqueueBuffers", (ALCvoid *) alSourceUnqueueBuffers },
{ "alGenBuffers", (ALCvoid *) alGenBuffers },
{ "alDeleteBuffers", (ALCvoid *) alDeleteBuffers },
{ "alIsBuffer", (ALCvoid *) alIsBuffer },
{ "alBufferData", (ALCvoid *) alBufferData },
{ "alBufferf", (ALCvoid *) alBufferf },
{ "alBuffer3f", (ALCvoid *) alBuffer3f },
{ "alBufferfv", (ALCvoid *) alBufferfv },
{ "alBufferi", (ALCvoid *) alBufferi },
{ "alBuffer3i", (ALCvoid *) alBuffer3i },
{ "alBufferiv", (ALCvoid *) alBufferiv },
{ "alGetBufferf", (ALCvoid *) alGetBufferf },
{ "alGetBuffer3f", (ALCvoid *) alGetBuffer3f },
{ "alGetBufferfv", (ALCvoid *) alGetBufferfv },
{ "alGetBufferi", (ALCvoid *) alGetBufferi },
{ "alGetBuffer3i", (ALCvoid *) alGetBuffer3i },
{ "alGetBufferiv", (ALCvoid *) alGetBufferiv },
{ "alDopplerFactor", (ALCvoid *) alDopplerFactor },
{ "alDopplerVelocity", (ALCvoid *) alDopplerVelocity },
{ "alSpeedOfSound", (ALCvoid *) alSpeedOfSound },
{ "alDistanceModel", (ALCvoid *) alDistanceModel },
{ "alGenFilters", (ALCvoid *) alGenFilters },
{ "alDeleteFilters", (ALCvoid *) alDeleteFilters },
{ "alIsFilter", (ALCvoid *) alIsFilter },
{ "alFilteri", (ALCvoid *) alFilteri },
{ "alFilteriv", (ALCvoid *) alFilteriv },
{ "alFilterf", (ALCvoid *) alFilterf },
{ "alFilterfv", (ALCvoid *) alFilterfv },
{ "alGetFilteri", (ALCvoid *) alGetFilteri },
{ "alGetFilteriv", (ALCvoid *) alGetFilteriv },
{ "alGetFilterf", (ALCvoid *) alGetFilterf },
{ "alGetFilterfv", (ALCvoid *) alGetFilterfv },
{ "alGenEffects", (ALCvoid *) alGenEffects },
{ "alDeleteEffects", (ALCvoid *) alDeleteEffects },
{ "alIsEffect", (ALCvoid *) alIsEffect },
{ "alEffecti", (ALCvoid *) alEffecti },
{ "alEffectiv", (ALCvoid *) alEffectiv },
{ "alEffectf", (ALCvoid *) alEffectf },
{ "alEffectfv", (ALCvoid *) alEffectfv },
{ "alGetEffecti", (ALCvoid *) alGetEffecti },
{ "alGetEffectiv", (ALCvoid *) alGetEffectiv },
{ "alGetEffectf", (ALCvoid *) alGetEffectf },
{ "alGetEffectfv", (ALCvoid *) alGetEffectfv },
{ "alGenAuxiliaryEffectSlots", (ALCvoid *) alGenAuxiliaryEffectSlots},
{ "alDeleteAuxiliaryEffectSlots",(ALCvoid *) alDeleteAuxiliaryEffectSlots},
{ "alIsAuxiliaryEffectSlot", (ALCvoid *) alIsAuxiliaryEffectSlot },
{ "alAuxiliaryEffectSloti", (ALCvoid *) alAuxiliaryEffectSloti },
{ "alAuxiliaryEffectSlotiv", (ALCvoid *) alAuxiliaryEffectSlotiv },
{ "alAuxiliaryEffectSlotf", (ALCvoid *) alAuxiliaryEffectSlotf },
{ "alAuxiliaryEffectSlotfv", (ALCvoid *) alAuxiliaryEffectSlotfv },
{ "alGetAuxiliaryEffectSloti", (ALCvoid *) alGetAuxiliaryEffectSloti},
{ "alGetAuxiliaryEffectSlotiv", (ALCvoid *) alGetAuxiliaryEffectSlotiv},
{ "alGetAuxiliaryEffectSlotf", (ALCvoid *) alGetAuxiliaryEffectSlotf},
{ "alGetAuxiliaryEffectSlotfv", (ALCvoid *) alGetAuxiliaryEffectSlotfv},
{ "alBufferSubDataSOFT", (ALCvoid *) alBufferSubDataSOFT },
{ "alBufferSamplesSOFT", (ALCvoid *) alBufferSamplesSOFT },
{ "alBufferSubSamplesSOFT", (ALCvoid *) alBufferSubSamplesSOFT },
{ "alGetBufferSamplesSOFT", (ALCvoid *) alGetBufferSamplesSOFT },
{ "alIsBufferFormatSupportedSOFT",(ALCvoid *) alIsBufferFormatSupportedSOFT},
{ NULL, (ALCvoid *) NULL }
};
static const ALCenums enumeration[] = {
// Types
{ "ALC_INVALID", ALC_INVALID },
{ "ALC_FALSE", ALC_FALSE },
{ "ALC_TRUE", ALC_TRUE },
// ALC Properties
{ "ALC_MAJOR_VERSION", ALC_MAJOR_VERSION },
{ "ALC_MINOR_VERSION", ALC_MINOR_VERSION },
{ "ALC_ATTRIBUTES_SIZE", ALC_ATTRIBUTES_SIZE },
{ "ALC_ALL_ATTRIBUTES", ALC_ALL_ATTRIBUTES },
{ "ALC_DEFAULT_DEVICE_SPECIFIER", ALC_DEFAULT_DEVICE_SPECIFIER },
{ "ALC_DEVICE_SPECIFIER", ALC_DEVICE_SPECIFIER },
{ "ALC_ALL_DEVICES_SPECIFIER", ALC_ALL_DEVICES_SPECIFIER },
{ "ALC_DEFAULT_ALL_DEVICES_SPECIFIER", ALC_DEFAULT_ALL_DEVICES_SPECIFIER },
{ "ALC_EXTENSIONS", ALC_EXTENSIONS },
{ "ALC_FREQUENCY", ALC_FREQUENCY },
{ "ALC_REFRESH", ALC_REFRESH },
{ "ALC_SYNC", ALC_SYNC },
{ "ALC_MONO_SOURCES", ALC_MONO_SOURCES },
{ "ALC_STEREO_SOURCES", ALC_STEREO_SOURCES },
{ "ALC_CAPTURE_DEVICE_SPECIFIER", ALC_CAPTURE_DEVICE_SPECIFIER },
{ "ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER", ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER},
{ "ALC_CAPTURE_SAMPLES", ALC_CAPTURE_SAMPLES },
{ "ALC_CONNECTED", ALC_CONNECTED },
// EFX Properties
{ "ALC_EFX_MAJOR_VERSION", ALC_EFX_MAJOR_VERSION },
{ "ALC_EFX_MINOR_VERSION", ALC_EFX_MINOR_VERSION },
{ "ALC_MAX_AUXILIARY_SENDS", ALC_MAX_AUXILIARY_SENDS },
// Loopback device Properties
{ "ALC_FORMAT_CHANNELS_SOFT", ALC_FORMAT_CHANNELS_SOFT },
{ "ALC_FORMAT_TYPE_SOFT", ALC_FORMAT_TYPE_SOFT },
// ALC Error Message
{ "ALC_NO_ERROR", ALC_NO_ERROR },
{ "ALC_INVALID_DEVICE", ALC_INVALID_DEVICE },
{ "ALC_INVALID_CONTEXT", ALC_INVALID_CONTEXT },
{ "ALC_INVALID_ENUM", ALC_INVALID_ENUM },
{ "ALC_INVALID_VALUE", ALC_INVALID_VALUE },
{ "ALC_OUT_OF_MEMORY", ALC_OUT_OF_MEMORY },
{ NULL, (ALCenum)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";
/* Device lists. Sizes only include the first ending null character, not the
* second */
static ALCchar *alcDeviceList;
static size_t alcDeviceListSize;
static ALCchar *alcAllDeviceList;
static size_t alcAllDeviceListSize;
static ALCchar *alcCaptureDeviceList;
static size_t alcCaptureDeviceListSize;
// Default is always the first in the list
static ALCchar *alcDefaultDeviceSpecifier;
static ALCchar *alcDefaultAllDeviceSpecifier;
static ALCchar *alcCaptureDefaultDeviceSpecifier;
static const ALCchar alcNoDeviceExtList[] =
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
"ALC_EXT_thread_local_context ALC_SOFTX_loopback_device";
static const ALCchar alcExtensionList[] =
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
"ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX "
"ALC_EXT_thread_local_context ALC_SOFTX_loopback_device";
static const ALCint alcMajorVersion = 1;
static const ALCint alcMinorVersion = 1;
static const ALCint alcEFXMajorVersion = 1;
static const ALCint alcEFXMinorVersion = 0;
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// Global Variables
static CRITICAL_SECTION g_csMutex;
static CRITICAL_SECTION ListLock;
/* Device List */
static ALCdevice *g_pDeviceList = NULL;
static ALCuint g_ulDeviceCount = 0;
// Context List
static ALCcontext *g_pContextList = NULL;
static ALCuint g_ulContextCount = 0;
// Thread-local current context
static tls_type LocalContext;
// Process-wide current context
static ALCcontext *GlobalContext;
// Context Error
static ALCenum g_eLastNullDeviceError = ALC_NO_ERROR;
// Default context extensions
static const ALchar alExtList[] =
"AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 "
"AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW "
"AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model "
"AL_LOKI_quadriphonic AL_SOFTX_buffer_samples AL_SOFT_buffer_sub_data "
"AL_SOFT_loop_points AL_SOFTX_non_virtual_channels";
// Mixing Priority Level
static ALint RTPrioLevel;
// Output Log File
static FILE *LogFile;
// Cone scalar
ALdouble ConeScale = 0.5;
// Localized Z scalar for mono sources
ALdouble ZScale = 1.0;
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// ALC Related helper functions
static void ReleaseALC(void);
static void alc_initconfig(void);
#if defined(_WIN32)
static void alc_init(void);
static void alc_deinit(void);
#ifndef AL_LIBTYPE_STATIC
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);
alc_init();
break;
case DLL_PROCESS_DETACH:
alc_deinit();
break;
}
return TRUE;
}
#elif defined(_MSC_VER)
#pragma section(".CRT$XCU",read)
static void alc_constructor(void);
static void alc_destructor(void);
__declspec(allocate(".CRT$XCU")) void (__cdecl* alc_constructor_)(void) = alc_constructor;
static void alc_constructor(void)
{
atexit(alc_destructor);
alc_init();
}
static void alc_destructor(void)
{
alc_deinit();
}
#elif defined(HAVE_GCC_DESTRUCTOR)
static void alc_init(void) __attribute__((constructor));
static void alc_deinit(void) __attribute__((destructor));
#else
#error "No static initialization available on this platform!"
#endif
#elif defined(HAVE_GCC_DESTRUCTOR)
static void alc_init(void) __attribute__((constructor));
static void alc_deinit(void) __attribute__((destructor));
#else
#error "No global initialization available on this platform!"
#endif
static void alc_init(void)
{
const char *str;
LogFile = stderr;
str = getenv("__ALSOFT_HALF_ANGLE_CONES");
if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
ConeScale = 1.0;
str = getenv("__ALSOFT_REVERSE_Z");
if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
ZScale = -1.0;
tls_create(&LocalContext);
InitializeCriticalSection(&g_csMutex);
InitializeCriticalSection(&ListLock);
ALTHUNK_INIT();
#ifdef _WIN32
alc_initconfig();
#endif
}
static void alc_deinit(void)
{
int i;
ReleaseALC();
for(i = 0;BackendList[i].Deinit;i++)
BackendList[i].Deinit();
BackendLoopback.Deinit();
FreeALConfig();
ALTHUNK_EXIT();
DeleteCriticalSection(&ListLock);
DeleteCriticalSection(&g_csMutex);
tls_delete(LocalContext);
if(LogFile != stderr)
fclose(LogFile);
LogFile = NULL;
}
static void alc_initconfig(void)
{
int i;
const char *devs, *str;
str = getenv("ALSOFT_LOGFILE");
if(str && str[0])
{
FILE *logfile = fopen(str, "wat");
if(logfile) LogFile = logfile;
else AL_PRINT("Failed to open log file '%s'\n", str);
}
ReadALConfig();
InitHrtf();
RTPrioLevel = GetConfigValueInt(NULL, "rt-prio", 0);
DefaultResampler = GetConfigValueInt(NULL, "resampler", RESAMPLER_DEFAULT);
if(DefaultResampler >= RESAMPLER_MAX || DefaultResampler <= RESAMPLER_MIN)
DefaultResampler = RESAMPLER_DEFAULT;
devs = GetConfigValue(NULL, "drivers", "");
if(devs[0])
{
int n;
size_t len;
const char *next = devs;
int endlist, delitem;
i = 0;
do {
devs = next;
next = strchr(devs, ',');
delitem = (devs[0] == '-');
if(devs[0] == '-') devs++;
if(!devs[0] || devs[0] == ',')
{
endlist = 0;
continue;
}
endlist = 1;
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)
{
if(delitem)
{
do {
BackendList[n] = BackendList[n+1];
++n;
} while(BackendList[n].Init);
}
else
{
BackendInfo Bkp = BackendList[n];
while(n > i)
{
BackendList[n] = BackendList[n-1];
--n;
}
BackendList[n] = Bkp;
i++;
}
break;
}
}
} while(next++);
if(endlist)
{
BackendList[i].name = NULL;
BackendList[i].Init = NULL;
BackendList[i].Deinit = NULL;
BackendList[i].Probe = NULL;
}
}
for(i = 0;BackendList[i].Init;i++)
BackendList[i].Init(&BackendList[i].Funcs);
BackendLoopback.Init(&BackendLoopback.Funcs);
str = GetConfigValue(NULL, "excludefx", "");
if(str[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++);
}
}
#ifndef _WIN32
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
#define DO_INITCONFIG() pthread_once(&once_control, alc_initconfig)
#else
#define DO_INITCONFIG()
#endif
static void ProbeList(ALCchar **list, size_t *listsize, int type)
{
ALint i;
free(*list);
*list = NULL;
*listsize = 0;
DO_INITCONFIG();
for(i = 0;BackendList[i].Probe;i++)
BackendList[i].Probe(type);
}
static void ProbeDeviceList()
{ ProbeList(&alcDeviceList, &alcDeviceListSize, DEVICE_PROBE); }
static void ProbeAllDeviceList()
{ ProbeList(&alcAllDeviceList, &alcAllDeviceListSize, ALL_DEVICE_PROBE); }
static void ProbeCaptureDeviceList()
{ ProbeList(&alcCaptureDeviceList, &alcCaptureDeviceListSize, CAPTURE_DEVICE_PROBE); }
static void AppendList(const ALCchar *name, ALCchar **List, size_t *ListSize)
{
size_t len = strlen(name);
void *temp;
if(len == 0)
return;
temp = realloc(*List, (*ListSize) + len + 2);
if(!temp)
{
AL_PRINT("Realloc failed to add %s!\n", name);
return;
}
*List = temp;
memcpy((*List)+(*ListSize), name, len+1);
*ListSize += len+1;
(*List)[*ListSize] = 0;
}
#define DECL_APPEND_LIST_FUNC(type) \
void Append##type##List(const ALCchar *name) \
{ AppendList(name, &alc##type##List, &alc##type##ListSize); }
DECL_APPEND_LIST_FUNC(Device)
DECL_APPEND_LIST_FUNC(AllDevice)
DECL_APPEND_LIST_FUNC(CaptureDevice)
#undef DECL_APPEND_LIST_FUNC
void al_print(const char *fname, unsigned int line, const char *fmt, ...)
{
const char *fn;
char str[256];
int i;
fn = strrchr(fname, '/');
if(!fn) fn = strrchr(fname, '\\');
if(!fn) fn = fname;
else fn += 1;
i = snprintf(str, sizeof(str), "AL lib: %s:%d: ", fn, line);
if(i < (int)sizeof(str) && i > 0)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(str+i, sizeof(str)-i, fmt, ap);
va_end(ap);
}
str[sizeof(str)-1] = 0;
fprintf(LogFile, "%s", str);
fflush(LogFile);
}
void SetRTPriority(void)
{
ALboolean failed;
#ifdef _WIN32
if(RTPrioLevel > 0)
failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
else
failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
#elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
struct sched_param param;
if(RTPrioLevel > 0)
{
/* Use the minimum real-time priority possible for now (on Linux this
* should be 1 for SCHED_RR) */
param.sched_priority = sched_get_priority_min(SCHED_RR);
failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, &param);
}
else
{
param.sched_priority = 0;
failed = !!pthread_setschedparam(pthread_self(), SCHED_OTHER, &param);
}
#else
/* Real-time priority not available */
failed = (RTPrioLevel>0);
#endif
if(failed)
AL_PRINT("Failed to set priority level for thread\n");
}
void InitUIntMap(UIntMap *map)
{
map->array = NULL;
map->size = 0;
map->maxsize = 0;
}
void ResetUIntMap(UIntMap *map)
{
free(map->array);
map->array = NULL;
map->size = 0;
map->maxsize = 0;
}
ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value)
{
ALsizei pos = 0;
if(map->size > 0)
{
ALsizei low = 0;
ALsizei high = map->size - 1;
while(low < high)
{
ALsizei mid = low + (high-low)/2;
if(map->array[mid].key < key)
low = mid + 1;
else
high = mid;
}
if(map->array[low].key < key)
low++;
pos = low;
}
if(pos == map->size || map->array[pos].key != key)
{
if(map->size == map->maxsize)
{
ALvoid *temp;
ALsizei newsize;
newsize = (map->maxsize ? (map->maxsize<<1) : 4);
if(newsize < map->maxsize)
return AL_OUT_OF_MEMORY;
temp = realloc(map->array, newsize*sizeof(map->array[0]));
if(!temp) return AL_OUT_OF_MEMORY;
map->array = temp;
map->maxsize = newsize;
}
map->size++;
if(pos < map->size-1)
memmove(&map->array[pos+1], &map->array[pos],
(map->size-1-pos)*sizeof(map->array[0]));
}
map->array[pos].key = key;
map->array[pos].value = value;
return AL_NO_ERROR;
}
void RemoveUIntMapKey(UIntMap *map, ALuint key)
{
if(map->size > 0)
{
ALsizei low = 0;
ALsizei high = map->size - 1;
while(low < high)
{
ALsizei mid = low + (high-low)/2;
if(map->array[mid].key < key)
low = mid + 1;
else
high = mid;
}
if(map->array[low].key == key)
{
if(low < map->size-1)
memmove(&map->array[low], &map->array[low+1],
(map->size-1-low)*sizeof(map->array[0]));
map->size--;
}
}
}
ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key)
{
if(map->size > 0)
{
ALsizei low = 0;
ALsizei high = map->size - 1;
while(low < high)
{
ALsizei mid = low + (high-low)/2;
if(map->array[mid].key < key)
low = mid + 1;
else
high = mid;
}
if(map->array[low].key == key)
return map->array[low].value;
}
return NULL;
}
const ALCchar *DevFmtTypeString(enum DevFmtType type)
{
switch(type)
{
case DevFmtByte: return "Signed Byte";
case DevFmtUByte: return "Unsigned Byte";
case DevFmtShort: return "Signed Short";
case DevFmtUShort: return "Unsigned Short";
case DevFmtFloat: return "Float";
}
return "(unknown type)";
}
const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans)
{
switch(chans)
{
case DevFmtMono: return "Mono";
case DevFmtStereo: return "Stereo";
case DevFmtQuad: return "Quadraphonic";
case DevFmtX51: return "5.1 Surround";
case DevFmtX51Side: return "5.1 Side";
case DevFmtX61: return "6.1 Surround";
case DevFmtX71: return "7.1 Surround";
}
return "(unknown channels)";
}
ALuint BytesFromDevFmt(enum DevFmtType type)
{
switch(type)
{
case DevFmtByte: return sizeof(ALbyte);
case DevFmtUByte: return sizeof(ALubyte);
case DevFmtShort: return sizeof(ALshort);
case DevFmtUShort: return sizeof(ALushort);
case DevFmtFloat: return sizeof(ALfloat);
}
return 0;
}
ALuint ChannelsFromDevFmt(enum DevFmtChannels chans)
{
switch(chans)
{
case DevFmtMono: return 1;
case DevFmtStereo: return 2;
case DevFmtQuad: return 4;
case DevFmtX51: return 6;
case DevFmtX51Side: return 6;
case DevFmtX61: return 7;
case DevFmtX71: return 8;
}
return 0;
}
ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans,
enum DevFmtType *type)
{
switch(format)
{
case AL_FORMAT_MONO8:
*chans = DevFmtMono;
*type = DevFmtUByte;
return AL_TRUE;
case AL_FORMAT_MONO16:
*chans = DevFmtMono;
*type = DevFmtShort;
return AL_TRUE;
case AL_FORMAT_MONO_FLOAT32:
*chans = DevFmtMono;
*type = DevFmtFloat;
return AL_TRUE;
case AL_FORMAT_STEREO8:
*chans = DevFmtStereo;
*type = DevFmtUByte;
return AL_TRUE;
case AL_FORMAT_STEREO16:
*chans = DevFmtStereo;
*type = DevFmtShort;
return AL_TRUE;
case AL_FORMAT_STEREO_FLOAT32:
*chans = DevFmtStereo;
*type = DevFmtFloat;
return AL_TRUE;
case AL_FORMAT_QUAD8:
*chans = DevFmtQuad;
*type = DevFmtUByte;
return AL_TRUE;
case AL_FORMAT_QUAD16:
*chans = DevFmtQuad;
*type = DevFmtShort;
return AL_TRUE;
case AL_FORMAT_QUAD32:
*chans = DevFmtQuad;
*type = DevFmtFloat;
return AL_TRUE;
case AL_FORMAT_51CHN8:
*chans = DevFmtX51;
*type = DevFmtUByte;
return AL_TRUE;
case AL_FORMAT_51CHN16:
*chans = DevFmtX51;
*type = DevFmtShort;
return AL_TRUE;
case AL_FORMAT_51CHN32:
*chans = DevFmtX51;
*type = DevFmtFloat;
return AL_TRUE;
case AL_FORMAT_61CHN8:
*chans = DevFmtX61;
*type = DevFmtUByte;
return AL_TRUE;
case AL_FORMAT_61CHN16:
*chans = DevFmtX61;
*type = DevFmtShort;
return AL_TRUE;
case AL_FORMAT_61CHN32:
*chans = DevFmtX61;
*type = DevFmtFloat;
return AL_TRUE;
case AL_FORMAT_71CHN8:
*chans = DevFmtX71;
*type = DevFmtUByte;
return AL_TRUE;
case AL_FORMAT_71CHN16:
*chans = DevFmtX71;
*type = DevFmtShort;
return AL_TRUE;
case AL_FORMAT_71CHN32:
*chans = DevFmtX71;
*type = DevFmtFloat;
return AL_TRUE;
}
return AL_FALSE;
}
ALboolean IsValidType(ALenum type)
{
switch(type)
{
case AL_BYTE:
case AL_UNSIGNED_BYTE:
case AL_SHORT:
case AL_UNSIGNED_SHORT:
case AL_INT:
case AL_UNSIGNED_INT:
case AL_FLOAT:
case AL_DOUBLE:
case AL_MULAW:
case AL_IMA4:
case AL_BYTE3:
case AL_UNSIGNED_BYTE3:
return AL_TRUE;
}
return AL_FALSE;
}
ALboolean IsValidChannels(ALenum channels)
{
switch(channels)
{
case AL_MONO:
case AL_STEREO:
case AL_REAR:
case AL_QUAD:
case AL_5POINT1:
case AL_6POINT1:
case AL_7POINT1:
return AL_TRUE;
}
return AL_FALSE;
}
#ifndef _WIN32
void InitializeCriticalSection(CRITICAL_SECTION *cs)
{
pthread_mutexattr_t attrib;
int ret;
ret = pthread_mutexattr_init(&attrib);
assert(ret == 0);
ret = pthread_mutexattr_settype(&attrib, PTHREAD_MUTEX_RECURSIVE);
#ifdef HAVE_PTHREAD_NP_H
if(ret != 0)
ret = pthread_mutexattr_setkind_np(&attrib, PTHREAD_MUTEX_RECURSIVE);
#endif
assert(ret == 0);
ret = pthread_mutex_init(cs, &attrib);
assert(ret == 0);
pthread_mutexattr_destroy(&attrib);
}
void DeleteCriticalSection(CRITICAL_SECTION *cs)
{
int ret;
ret = pthread_mutex_destroy(cs);
assert(ret == 0);
}
void EnterCriticalSection(CRITICAL_SECTION *cs)
{
int ret;
ret = pthread_mutex_lock(cs);
assert(ret == 0);
}
void LeaveCriticalSection(CRITICAL_SECTION *cs)
{
int ret;
ret = pthread_mutex_unlock(cs);
assert(ret == 0);
}
/* NOTE: This wrapper isn't quite accurate as it returns an ALuint, as opposed
* to the expected DWORD. Both are defined as unsigned 32-bit types, however.
* Additionally, Win32 is supposed to measure the time since Windows started,
* as opposed to the actual time. */
ALuint timeGetTime(void)
{
#if _POSIX_TIMERS > 0
struct timespec ts;
int ret = -1;
#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0)
#if _POSIX_MONOTONIC_CLOCK == 0
static int hasmono = 0;
if(hasmono > 0 || (hasmono == 0 &&
(hasmono=sysconf(_SC_MONOTONIC_CLOCK)) > 0))
#endif
ret = clock_gettime(CLOCK_MONOTONIC, &ts);
#endif
if(ret != 0)
ret = clock_gettime(CLOCK_REALTIME, &ts);
assert(ret == 0);
return ts.tv_nsec/1000000 + ts.tv_sec*1000;
#else
struct timeval tv;
int ret;
ret = gettimeofday(&tv, NULL);
assert(ret == 0);
return tv.tv_usec/1000 + tv.tv_sec*1000;
#endif
}
#endif
#if defined(_WIN32)
void *LoadLib(const char *name)
{ return LoadLibraryA(name); }
void CloseLib(void *handle)
{ FreeLibrary((HANDLE)handle); }
void *GetSymbol(void *handle, const char *name)
{
void *ret;
ret = (void*)GetProcAddress((HANDLE)handle, name);
if(ret == NULL)
AL_PRINT("Failed to load %s\n", name);
return ret;
}
#elif defined(HAVE_DLFCN_H)
void *LoadLib(const char *name)
{
const char *err;
void *handle;
dlerror();
handle = dlopen(name, RTLD_NOW);
if((err=dlerror()) != NULL)
handle = NULL;
return handle;
}
void CloseLib(void *handle)
{ dlclose(handle); }
void *GetSymbol(void *handle, const char *name)
{
const char *err;
void *sym;
dlerror();
sym = dlsym(handle, name);
if((err=dlerror()) != NULL)
{
AL_PRINT("Failed to load %s: %s\n", name, err);
sym = NULL;
}
return sym;
}
#endif
static void LockLists(void)
{
EnterCriticalSection(&ListLock);
}
static void UnlockLists(void)
{
LeaveCriticalSection(&ListLock);
}
/*
IsDevice
Check pDevice is a valid Device pointer
*/
static ALCboolean IsDevice(ALCdevice *pDevice)
{
ALCdevice *pTempDevice;
pTempDevice = g_pDeviceList;
while(pTempDevice && pTempDevice != pDevice)
pTempDevice = pTempDevice->next;
return (pTempDevice ? ALC_TRUE : ALC_FALSE);
}
/*
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);
}
/*
alcSetError
Store latest ALC Error
*/
ALCvoid alcSetError(ALCdevice *device, ALenum errorCode)
{
LockLists();
if(IsDevice(device))
device->LastError = errorCode;
else
g_eLastNullDeviceError = errorCode;
UnlockLists();
}
/* UpdateDeviceParams:
*
* Updates device parameters according to the attribute list.
*/
static ALCboolean UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
ALuint i;
// Check for attributes
if(attrList && attrList[0])
{
ALCuint freq, numMono, numStereo, numSends;
enum DevFmtChannels schans;
enum DevFmtType stype;
ALuint attrIdx;
// If a context is already running on the device, stop playback so the
// device attributes can be updated
if((device->Flags&DEVICE_RUNNING))
ALCdevice_StopPlayback(device);
device->Flags &= ~DEVICE_RUNNING;
freq = device->Frequency;
schans = device->FmtChans;
stype = device->FmtType;
numMono = device->NumMonoSources;
numStereo = device->NumStereoSources;
numSends = device->NumAuxSends;
freq = GetConfigValueInt(NULL, "frequency", freq);
if(freq < 8000) freq = 8000;
attrIdx = 0;
while(attrList[attrIdx])
{
if(attrList[attrIdx] == ALC_FORMAT_CHANNELS_SOFT &&
device->IsLoopbackDevice)
{
ALCint val = attrList[attrIdx + 1];
if(!IsValidChannels(val) || !ChannelsFromDevFmt(val))
{
alcSetError(device, ALC_INVALID_VALUE);
return ALC_FALSE;
}
schans = val;
}
if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT &&
device->IsLoopbackDevice)
{
ALCint val = attrList[attrIdx + 1];
if(!IsValidType(val) || !BytesFromDevFmt(val))
{
alcSetError(device, ALC_INVALID_VALUE);
return ALC_FALSE;
}
stype = val;
}
if(attrList[attrIdx] == ALC_FREQUENCY)
{
if(device->IsLoopbackDevice)
{
freq = attrList[attrIdx + 1];
if(freq < 8000)
{
alcSetError(device, ALC_INVALID_VALUE);
return ALC_FALSE;
}
}
else if(!ConfigValueExists(NULL, "frequency"))
{
freq = attrList[attrIdx + 1];
if(freq < 8000) freq = 8000;
device->Flags |= DEVICE_FREQUENCY_REQUEST;
}
}
if(attrList[attrIdx] == ALC_STEREO_SOURCES)
{
numStereo = attrList[attrIdx + 1];
if(numStereo > device->MaxNoOfSources)
numStereo = device->MaxNoOfSources;
numMono = device->MaxNoOfSources - numStereo;
}
if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS &&
!ConfigValueExists(NULL, "sends"))
{
numSends = attrList[attrIdx + 1];
if(numSends > MAX_SENDS)
numSends = MAX_SENDS;
}
attrIdx += 2;
}
device->UpdateSize = (ALuint64)device->UpdateSize * freq /
device->Frequency;
device->Frequency = freq;
device->FmtChans = schans;
device->FmtType = stype;
device->NumMonoSources = numMono;
device->NumStereoSources = numStereo;
device->NumAuxSends = numSends;
}
if((device->Flags&DEVICE_RUNNING))
return ALC_TRUE;
SuspendContext(NULL);
if(ALCdevice_ResetPlayback(device) == ALC_FALSE)
{
ProcessContext(NULL);
return ALC_FALSE;
}
device->Flags |= DEVICE_RUNNING;
aluInitPanning(device);
for(i = 0;i < MAXCHANNELS;i++)
{
device->ClickRemoval[i] = 0.0f;
device->PendingClicks[i] = 0.0f;
}
if(!device->IsLoopbackDevice && GetConfigValueBool(NULL, "hrtf", AL_FALSE))
device->Flags |= DEVICE_USE_HRTF;
if((device->Flags&DEVICE_USE_HRTF) && !IsHrtfCompatible(device))
{
AL_PRINT("HRTF disabled (format is %uhz %s)\n", device->Frequency, DevFmtChannelsString(device->FmtChans));
device->Flags &= ~DEVICE_USE_HRTF;
}
if(!(device->Flags&DEVICE_USE_HRTF) && device->Bs2bLevel > 0 && device->Bs2bLevel <= 6)
{
if(!device->Bs2b)
{
device->Bs2b = calloc(1, sizeof(*device->Bs2b));
bs2b_clear(device->Bs2b);
}
bs2b_set_srate(device->Bs2b, device->Frequency);
bs2b_set_level(device->Bs2b, device->Bs2bLevel);
}
else
{
free(device->Bs2b);
device->Bs2b = NULL;
}
device->Flags &= ~DEVICE_DUPLICATE_STEREO;
switch(device->FmtChans)
{
case DevFmtMono:
case DevFmtStereo:
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Side:
case DevFmtX61:
case DevFmtX71:
if(GetConfigValueBool(NULL, "stereodup", AL_TRUE))
device->Flags |= DEVICE_DUPLICATE_STEREO;
break;
}
for(i = 0;i < device->NumContexts;i++)
{
ALCcontext *context = device->Contexts[i];
ALsizei pos;
for(pos = 0;pos < context->EffectSlotMap.size;pos++)
{
ALeffectslot *slot = context->EffectSlotMap.array[pos].value;
if(ALEffect_DeviceUpdate(slot->EffectState, device) == AL_FALSE)
{
ProcessContext(NULL);
ALCdevice_StopPlayback(device);
device->Flags &= ~DEVICE_RUNNING;
return ALC_FALSE;
}
ALEffect_Update(slot->EffectState, context, &slot->effect);
}
for(pos = 0;pos < context->SourceMap.size;pos++)
{
ALsource *source = context->SourceMap.array[pos].value;
ALuint s = device->NumAuxSends;
while(s < MAX_SENDS)
{
if(source->Send[s].Slot)
source->Send[s].Slot->refcount--;
source->Send[s].Slot = NULL;
source->Send[s].WetFilter.type = 0;
source->Send[s].WetFilter.filter = 0;
s++;
}
ALsource_Update(source, context);
source->NeedsUpdate = AL_FALSE;
}
}
ProcessContext(NULL);
return ALC_TRUE;
}
/*
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);
}
/*
GetContextSuspended
Returns the currently active Context, in a locked state
*/
ALCcontext *GetContextSuspended(void)
{
ALCcontext *pContext = NULL;
LockLists();
pContext = tls_get(LocalContext);
if(pContext && !IsContext(pContext))
{
tls_set(LocalContext, NULL);
pContext = NULL;
}
if(!pContext)
pContext = GlobalContext;
if(pContext)
SuspendContext(pContext);
UnlockLists();
return pContext;
}
/*
InitContext
Initialize Context variables
*/
static ALvoid InitContext(ALCcontext *pContext)
{
//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->Suspended = AL_FALSE;
pContext->ActiveSourceCount = 0;
InitUIntMap(&pContext->SourceMap);
InitUIntMap(&pContext->EffectSlotMap);
//Set globals
pContext->DistanceModel = AL_INVERSE_DISTANCE_CLAMPED;
pContext->SourceDistanceModel = AL_FALSE;
pContext->DopplerFactor = 1.0f;
pContext->DopplerVelocity = 1.0f;
pContext->flSpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
pContext->ExtensionList = alExtList;
}
/*
ExitContext
Clean up Context, destroy any remaining Sources
*/
static ALCvoid ExitContext(ALCcontext *pContext)
{
//Invalidate context
pContext->LastError = AL_NO_ERROR;
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
// ALC Functions calls
// This should probably move to another c file but for now ...
ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
{
ALCboolean DeviceFound = ALC_FALSE;
ALCdevice *device = NULL;
ALCint i;
DO_INITCONFIG();
if(SampleSize <= 0)
{
alcSetError(NULL, ALC_INVALID_VALUE);
return NULL;
}
if(deviceName && !deviceName[0])
deviceName = NULL;
device = calloc(1, sizeof(ALCdevice));
if(!device)
{
alcSetError(NULL, ALC_OUT_OF_MEMORY);
return NULL;
}
//Validate device
device->Connected = ALC_TRUE;
device->IsCaptureDevice = AL_TRUE;
device->IsLoopbackDevice = AL_FALSE;
device->szDeviceName = NULL;
device->Flags |= DEVICE_FREQUENCY_REQUEST;
device->Frequency = frequency;
device->Flags |= DEVICE_CHANNELS_REQUEST;
if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE)
{
free(device);
alcSetError(NULL, ALC_INVALID_ENUM);
return NULL;
}
device->UpdateSize = SampleSize;
device->NumUpdates = 1;
LockLists();
for(i = 0;BackendList[i].Init;i++)
{
device->Funcs = &BackendList[i].Funcs;
if(ALCdevice_OpenCapture(device, deviceName))
{
device->next = g_pDeviceList;
g_pDeviceList = device;
g_ulDeviceCount++;
DeviceFound = ALC_TRUE;
break;
}
}
UnlockLists();
if(!DeviceFound)
{
alcSetError(NULL, ALC_INVALID_VALUE);
free(device);
device = NULL;
}
return device;
}
ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *pDevice)
{
ALCdevice **list;
LockLists();
list = &g_pDeviceList;
while(*list && *list != pDevice)
list = &(*list)->next;
if(!*list || !(*list)->IsCaptureDevice)
{
alcSetError(*list, ALC_INVALID_DEVICE);
UnlockLists();
return ALC_FALSE;
}
*list = (*list)->next;
g_ulDeviceCount--;
UnlockLists();
ALCdevice_CloseCapture(pDevice);
free(pDevice->szDeviceName);
pDevice->szDeviceName = NULL;
free(pDevice);
return ALC_TRUE;
}
ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
{
LockLists();
if(!IsDevice(device) || !device->IsCaptureDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else if(device->Connected)
ALCdevice_StartCapture(device);
UnlockLists();
}
ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
{
LockLists();
if(!IsDevice(device) || !device->IsCaptureDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else
ALCdevice_StopCapture(device);
UnlockLists();
}
ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
{
LockLists();
if(!IsDevice(device) || !device->IsCaptureDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else
ALCdevice_CaptureSamples(device, buffer, samples);
UnlockLists();
}
/*
alcGetError
Return last ALC generated error code
*/
ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
{
ALCenum errorCode;
LockLists();
if(IsDevice(device))
{
errorCode = device->LastError;
device->LastError = ALC_NO_ERROR;
}
else
{
errorCode = g_eLastNullDeviceError;
g_eLastNullDeviceError = ALC_NO_ERROR;
}
UnlockLists();
return errorCode;
}
/*
alcSuspendContext
Not functional
*/
ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *pContext)
{
LockLists();
if(IsContext(pContext))
pContext->Suspended = AL_TRUE;
UnlockLists();
}
/*
alcProcessContext
Not functional
*/
ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *pContext)
{
LockLists();
if(IsContext(pContext))
pContext->Suspended = AL_FALSE;
UnlockLists();
}
/*
alcGetString
Returns information about the Device, and error strings
*/
ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *pDevice,ALCenum param)
{
const ALCchar *value = NULL;
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_DEVICE_SPECIFIER:
LockLists();
if(IsDevice(pDevice))
value = pDevice->szDeviceName;
else
{
ProbeDeviceList();
value = alcDeviceList;
}
UnlockLists();
break;
case ALC_ALL_DEVICES_SPECIFIER:
ProbeAllDeviceList();
value = alcAllDeviceList;
break;
case ALC_CAPTURE_DEVICE_SPECIFIER:
LockLists();
if(IsDevice(pDevice))
value = pDevice->szDeviceName;
else
{
ProbeCaptureDeviceList();
value = alcCaptureDeviceList;
}
UnlockLists();
break;
/* Default devices are always first in the list */
case ALC_DEFAULT_DEVICE_SPECIFIER:
if(!alcDeviceList)
ProbeDeviceList();
free(alcDefaultDeviceSpecifier);
alcDefaultDeviceSpecifier = strdup(alcDeviceList ? alcDeviceList : "");
if(!alcDefaultDeviceSpecifier)
alcSetError(pDevice, ALC_OUT_OF_MEMORY);
value = alcDefaultDeviceSpecifier;
break;
case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
if(!alcAllDeviceList)
ProbeAllDeviceList();
free(alcDefaultAllDeviceSpecifier);
alcDefaultAllDeviceSpecifier = strdup(alcAllDeviceList ?
alcAllDeviceList : "");
if(!alcDefaultAllDeviceSpecifier)
alcSetError(pDevice, ALC_OUT_OF_MEMORY);
value = alcDefaultAllDeviceSpecifier;
break;
case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
if(!alcCaptureDeviceList)
ProbeCaptureDeviceList();
free(alcCaptureDefaultDeviceSpecifier);
alcCaptureDefaultDeviceSpecifier = strdup(alcCaptureDeviceList ?
alcCaptureDeviceList : "");
if(!alcCaptureDefaultDeviceSpecifier)
alcSetError(pDevice, ALC_OUT_OF_MEMORY);
value = alcCaptureDefaultDeviceSpecifier;
break;
case ALC_EXTENSIONS:
LockLists();
if(IsDevice(pDevice))
value = alcExtensionList;
else
value = alcNoDeviceExtList;
UnlockLists();
break;
default:
alcSetError(pDevice, ALC_INVALID_ENUM);
break;
}
return value;
}
/*
alcGetIntegerv
Returns information about the Device and the version of Open AL
*/
ALC_API ALCvoid ALC_APIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALsizei size,ALCint *data)
{
if(size == 0 || data == NULL)
{
alcSetError(device, ALC_INVALID_VALUE);
return;
}
LockLists();
if(!IsDevice(device))
{
switch(param)
{
case ALC_MAJOR_VERSION:
*data = alcMajorVersion;
break;
case ALC_MINOR_VERSION:
*data = alcMinorVersion;
break;
case ALC_ATTRIBUTES_SIZE:
case ALC_ALL_ATTRIBUTES:
case ALC_FREQUENCY:
case ALC_REFRESH:
case ALC_SYNC:
case ALC_MONO_SOURCES:
case ALC_STEREO_SOURCES:
case ALC_CAPTURE_SAMPLES:
case ALC_FORMAT_CHANNELS_SOFT:
case ALC_FORMAT_TYPE_SOFT:
alcSetError(NULL, ALC_INVALID_DEVICE);
break;
default:
alcSetError(NULL, ALC_INVALID_ENUM);
break;
}
}
else if(device->IsCaptureDevice)
{
switch(param)
{
case ALC_CAPTURE_SAMPLES:
*data = ALCdevice_AvailableSamples(device);
break;
case ALC_CONNECTED:
*data = device->Connected;
break;
default:
alcSetError(device, ALC_INVALID_ENUM);
break;
}
}
else /* render device */
{
switch(param)
{
case ALC_MAJOR_VERSION:
*data = alcMajorVersion;
break;
case ALC_MINOR_VERSION:
*data = alcMinorVersion;
break;
case ALC_EFX_MAJOR_VERSION:
*data = alcEFXMajorVersion;
break;
case ALC_EFX_MINOR_VERSION:
*data = alcEFXMinorVersion;
break;
case ALC_ATTRIBUTES_SIZE:
*data = 13;
break;
case ALC_ALL_ATTRIBUTES:
if(size < 13)
alcSetError(device, ALC_INVALID_VALUE);
else
{
int i = 0;
data[i++] = ALC_FREQUENCY;
data[i++] = device->Frequency;
if(!device->IsLoopbackDevice)
{
data[i++] = ALC_REFRESH;
data[i++] = device->Frequency / device->UpdateSize;
data[i++] = ALC_SYNC;
data[i++] = ALC_FALSE;
}
else
{
data[i++] = ALC_FORMAT_CHANNELS_SOFT;
data[i++] = device->FmtChans;
data[i++] = ALC_FORMAT_TYPE_SOFT;
data[i++] = device->FmtType;
}
data[i++] = ALC_MONO_SOURCES;
data[i++] = device->NumMonoSources;
data[i++] = ALC_STEREO_SOURCES;
data[i++] = device->NumStereoSources;
data[i++] = ALC_MAX_AUXILIARY_SENDS;
data[i++] = device->NumAuxSends;
data[i++] = 0;
}
break;
case ALC_FREQUENCY:
*data = device->Frequency;
break;
case ALC_REFRESH:
if(device->IsLoopbackDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else
*data = device->Frequency / device->UpdateSize;
break;
case ALC_SYNC:
if(device->IsLoopbackDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else
*data = ALC_FALSE;
break;
case ALC_FORMAT_CHANNELS_SOFT:
if(!device->IsLoopbackDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else
*data = device->FmtChans;
break;
case ALC_FORMAT_TYPE_SOFT:
if(!device->IsLoopbackDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else
*data = device->FmtType;
break;
case ALC_MONO_SOURCES:
*data = device->NumMonoSources;
break;
case ALC_STEREO_SOURCES:
*data = device->NumStereoSources;
break;
case ALC_MAX_AUXILIARY_SENDS:
*data = device->NumAuxSends;
break;
case ALC_CONNECTED:
*data = device->Connected;
break;
default:
alcSetError(device, ALC_INVALID_ENUM);
break;
}
}
UnlockLists();
}
/*
alcIsExtensionPresent
Determines if there is support for a particular extension
*/
ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName)
{
ALCboolean bResult = ALC_FALSE;
const char *ptr;
size_t len;
if(!extName)
{
alcSetError(device, ALC_INVALID_VALUE);
return ALC_FALSE;
}
len = strlen(extName);
LockLists();
ptr = (IsDevice(device) ? alcExtensionList : alcNoDeviceExtList);
UnlockLists();
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));
}
}
return bResult;
}
/*
alcGetProcAddress
Retrieves the function address for a particular extension function
*/
ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName)
{
ALsizei i = 0;
if(!funcName)
{
alcSetError(device, ALC_INVALID_VALUE);
return NULL;
}
while(alcFunctions[i].funcName && strcmp(alcFunctions[i].funcName,funcName) != 0)
i++;
return alcFunctions[i].address;
}
/*
alcGetEnumValue
Get the value for a particular ALC Enumerated Value
*/
ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName)
{
ALsizei i = 0;
if(!enumName)
{
alcSetError(device, ALC_INVALID_VALUE);
return (ALCenum)0;
}
while(enumeration[i].enumName && strcmp(enumeration[i].enumName,enumName) != 0)
i++;
return enumeration[i].value;
}
/*
alcCreateContext
Create and attach a Context to a particular Device.
*/
ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
{
ALCcontext *ALContext;
void *temp;
LockLists();
if(!IsDevice(device) || device->IsCaptureDevice || !device->Connected)
{
alcSetError(device, ALC_INVALID_DEVICE);
UnlockLists();
return NULL;
}
// Reset Context Last Error code
device->LastError = ALC_NO_ERROR;
if(UpdateDeviceParams(device, attrList) == ALC_FALSE)
{
alcSetError(device, ALC_INVALID_DEVICE);
aluHandleDisconnect(device);
UnlockLists();
return NULL;
}
SuspendContext(NULL);
ALContext = NULL;
temp = realloc(device->Contexts, (device->NumContexts+1) * sizeof(*device->Contexts));
if(temp)
{
device->Contexts = temp;
ALContext = calloc(1, sizeof(ALCcontext));
if(ALContext)
{
ALContext->MaxActiveSources = 256;
ALContext->ActiveSources = malloc(sizeof(ALContext->ActiveSources[0]) *
ALContext->MaxActiveSources);
}
}
if(!ALContext || !ALContext->ActiveSources)
{
free(ALContext);
alcSetError(device, ALC_OUT_OF_MEMORY);
ProcessContext(NULL);
if(device->NumContexts == 0)
{
ALCdevice_StopPlayback(device);
device->Flags &= ~DEVICE_RUNNING;
}
UnlockLists();
return NULL;
}
device->Contexts[device->NumContexts++] = ALContext;
ALContext->Device = device;
InitContext(ALContext);
ProcessContext(NULL);
ALContext->next = g_pContextList;
g_pContextList = ALContext;
g_ulContextCount++;
UnlockLists();
return ALContext;
}
/*
alcDestroyContext
Remove a Context
*/
ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
{
ALCdevice *Device;
ALCcontext **list;
ALuint i;
LockLists();
list = &g_pContextList;
while(*list && *list != context)
list = &(*list)->next;
if(!*list)
{
alcSetError(NULL, ALC_INVALID_CONTEXT);
UnlockLists();
return;
}
*list = (*list)->next;
g_ulContextCount--;
if(context == tls_get(LocalContext))
tls_set(LocalContext, NULL);
if(context == GlobalContext)
GlobalContext = NULL;
Device = context->Device;
SuspendContext(NULL);
for(i = 0;i < Device->NumContexts;i++)
{
if(Device->Contexts[i] == context)
{
Device->Contexts[i] = Device->Contexts[Device->NumContexts-1];
Device->NumContexts--;
break;
}
}
ProcessContext(NULL);
if(Device->NumContexts == 0)
{
ALCdevice_StopPlayback(Device);
Device->Flags &= ~DEVICE_RUNNING;
}
UnlockLists();
if(context->SourceMap.size > 0)
{
#ifdef _DEBUG
AL_PRINT("alcDestroyContext(): deleting %d Source(s)\n", context->SourceMap.size);
#endif
ReleaseALSources(context);
}
ResetUIntMap(&context->SourceMap);
if(context->EffectSlotMap.size > 0)
{
#ifdef _DEBUG
AL_PRINT("alcDestroyContext(): deleting %d AuxiliaryEffectSlot(s)\n", context->EffectSlotMap.size);
#endif
ReleaseALAuxiliaryEffectSlots(context);
}
ResetUIntMap(&context->EffectSlotMap);
free(context->ActiveSources);
context->ActiveSources = NULL;
context->MaxActiveSources = 0;
context->ActiveSourceCount = 0;
ExitContext(context);
memset(context, 0, sizeof(ALCcontext));
free(context);
}
/*
alcGetCurrentContext
Returns the currently active Context
*/
ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(ALCvoid)
{
ALCcontext *Context;
LockLists();
Context = tls_get(LocalContext);
if(Context && !IsContext(Context))
{
tls_set(LocalContext, NULL);
Context = NULL;
}
if(!Context)
Context = GlobalContext;
UnlockLists();
return Context;
}
/*
alcGetThreadContext
Returns the currently active thread-local Context
*/
ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
{
ALCcontext *pContext = NULL;
LockLists();
pContext = tls_get(LocalContext);
if(pContext && !IsContext(pContext))
{
tls_set(LocalContext, NULL);
pContext = NULL;
}
UnlockLists();
return pContext;
}
/*
alcGetContextsDevice
Returns the Device that a particular Context is attached to
*/
ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *pContext)
{
ALCdevice *pDevice = NULL;
LockLists();
if(IsContext(pContext))
pDevice = pContext->Device;
else
alcSetError(NULL, ALC_INVALID_CONTEXT);
UnlockLists();
return pDevice;
}
/*
alcMakeContextCurrent
Makes the given Context the active Context
*/
ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
{
ALboolean bReturn = AL_TRUE;
LockLists();
// context must be a valid Context or NULL
if(context == NULL || IsContext(context))
{
GlobalContext = context;
tls_set(LocalContext, NULL);
}
else
{
alcSetError(NULL, ALC_INVALID_CONTEXT);
bReturn = AL_FALSE;
}
UnlockLists();
return bReturn;
}
/*
alcSetThreadContext
Makes the given Context the active Context for the current thread
*/
ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
{
ALboolean bReturn = AL_TRUE;
LockLists();
// context must be a valid Context or NULL
if(context == NULL || IsContext(context))
tls_set(LocalContext, context);
else
{
alcSetError(NULL, ALC_INVALID_CONTEXT);
bReturn = AL_FALSE;
}
UnlockLists();
return bReturn;
}
// Sets the default channel order used by most non-WaveFormatEx-based APIs
void SetDefaultChannelOrder(ALCdevice *device)
{
switch(device->FmtChans)
{
case DevFmtX51: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1;
device->DevChannels[BACK_LEFT] = 2;
device->DevChannels[BACK_RIGHT] = 3;
device->DevChannels[FRONT_CENTER] = 4;
device->DevChannels[LFE] = 5;
return;
case DevFmtX71: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1;
device->DevChannels[BACK_LEFT] = 2;
device->DevChannels[BACK_RIGHT] = 3;
device->DevChannels[FRONT_CENTER] = 4;
device->DevChannels[LFE] = 5;
device->DevChannels[SIDE_LEFT] = 6;
device->DevChannels[SIDE_RIGHT] = 7;
return;
/* Same as WFX order */
case DevFmtMono:
case DevFmtStereo:
case DevFmtQuad:
case DevFmtX51Side:
case DevFmtX61:
break;
}
SetDefaultWFXChannelOrder(device);
}
// Sets the default order used by WaveFormatEx
void SetDefaultWFXChannelOrder(ALCdevice *device)
{
switch(device->FmtChans)
{
case DevFmtMono: device->DevChannels[FRONT_CENTER] = 0; break;
case DevFmtStereo: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1; break;
case DevFmtQuad: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1;
device->DevChannels[BACK_LEFT] = 2;
device->DevChannels[BACK_RIGHT] = 3; break;
case DevFmtX51: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1;
device->DevChannels[FRONT_CENTER] = 2;
device->DevChannels[LFE] = 3;
device->DevChannels[BACK_LEFT] = 4;
device->DevChannels[BACK_RIGHT] = 5; break;
case DevFmtX51Side: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1;
device->DevChannels[FRONT_CENTER] = 2;
device->DevChannels[LFE] = 3;
device->DevChannels[SIDE_LEFT] = 4;
device->DevChannels[SIDE_RIGHT] = 5; break;
case DevFmtX61: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1;
device->DevChannels[FRONT_CENTER] = 2;
device->DevChannels[LFE] = 3;
device->DevChannels[BACK_CENTER] = 4;
device->DevChannels[SIDE_LEFT] = 5;
device->DevChannels[SIDE_RIGHT] = 6; break;
case DevFmtX71: device->DevChannels[FRONT_LEFT] = 0;
device->DevChannels[FRONT_RIGHT] = 1;
device->DevChannels[FRONT_CENTER] = 2;
device->DevChannels[LFE] = 3;
device->DevChannels[BACK_LEFT] = 4;
device->DevChannels[BACK_RIGHT] = 5;
device->DevChannels[SIDE_LEFT] = 6;
device->DevChannels[SIDE_RIGHT] = 7; break;
}
}
static void GetFormatFromString(const char *str, enum DevFmtChannels *chans, enum DevFmtType *type)
{
if(strcasecmp(str, "AL_FORMAT_MONO32") == 0)
{
*chans = DevFmtMono;
*type = DevFmtFloat;
return;
}
if(strcasecmp(str, "AL_FORMAT_STEREO32") == 0)
{
*chans = DevFmtStereo;
*type = DevFmtFloat;
return;
}
if(strcasecmp(str, "AL_FORMAT_QUAD32") == 0)
{
*chans = DevFmtQuad;
*type = DevFmtFloat;
return;
}
if(strcasecmp(str, "AL_FORMAT_51CHN32") == 0)
{
*chans = DevFmtX51;
*type = DevFmtFloat;
return;
}
if(strcasecmp(str, "AL_FORMAT_61CHN32") == 0)
{
*chans = DevFmtX61;
*type = DevFmtFloat;
return;
}
if(strcasecmp(str, "AL_FORMAT_71CHN32") == 0)
{
*chans = DevFmtX71;
*type = DevFmtFloat;
return;
}
if(strcasecmp(str, "AL_FORMAT_MONO16") == 0)
{
*chans = DevFmtMono;
*type = DevFmtShort;
return;
}
if(strcasecmp(str, "AL_FORMAT_STEREO16") == 0)
{
*chans = DevFmtStereo;
*type = DevFmtShort;
return;
}
if(strcasecmp(str, "AL_FORMAT_QUAD16") == 0)
{
*chans = DevFmtQuad;
*type = DevFmtShort;
return;
}
if(strcasecmp(str, "AL_FORMAT_51CHN16") == 0)
{
*chans = DevFmtX51;
*type = DevFmtShort;
return;
}
if(strcasecmp(str, "AL_FORMAT_61CHN16") == 0)
{
*chans = DevFmtX61;
*type = DevFmtShort;
return;
}
if(strcasecmp(str, "AL_FORMAT_71CHN16") == 0)
{
*chans = DevFmtX71;
*type = DevFmtShort;
return;
}
if(strcasecmp(str, "AL_FORMAT_MONO8") == 0)
{
*chans = DevFmtMono;
*type = DevFmtByte;
return;
}
if(strcasecmp(str, "AL_FORMAT_STEREO8") == 0)
{
*chans = DevFmtStereo;
*type = DevFmtByte;
return;
}
if(strcasecmp(str, "AL_FORMAT_QUAD8") == 0)
{
*chans = DevFmtQuad;
*type = DevFmtByte;
return;
}
if(strcasecmp(str, "AL_FORMAT_51CHN8") == 0)
{
*chans = DevFmtX51;
*type = DevFmtByte;
return;
}
if(strcasecmp(str, "AL_FORMAT_61CHN8") == 0)
{
*chans = DevFmtX61;
*type = DevFmtByte;
return;
}
if(strcasecmp(str, "AL_FORMAT_71CHN8") == 0)
{
*chans = DevFmtX71;
*type = DevFmtByte;
return;
}
AL_PRINT("Unknown format: \"%s\"\n", str);
*chans = DevFmtStereo;
*type = DevFmtShort;
}
/*
alcOpenDevice
Open the Device specified.
*/
ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
{
ALboolean bDeviceFound = AL_FALSE;
const ALCchar *fmt;
ALCdevice *device;
ALint i;
DO_INITCONFIG();
if(deviceName && !deviceName[0])
deviceName = NULL;
device = calloc(1, sizeof(ALCdevice));
if(!device)
{
alcSetError(NULL, ALC_OUT_OF_MEMORY);
return NULL;
}
//Validate device
device->Connected = ALC_TRUE;
device->IsCaptureDevice = AL_FALSE;
device->IsLoopbackDevice = AL_FALSE;
device->LastError = ALC_NO_ERROR;
device->Flags = 0;
device->Bs2b = NULL;
device->szDeviceName = NULL;
device->Contexts = NULL;
device->NumContexts = 0;
InitUIntMap(&device->BufferMap);
InitUIntMap(&device->EffectMap);
InitUIntMap(&device->FilterMap);
//Set output format
if(ConfigValueExists(NULL, "frequency"))
device->Flags |= DEVICE_FREQUENCY_REQUEST;
device->Frequency = GetConfigValueInt(NULL, "frequency", DEFAULT_OUTPUT_RATE);
if(device->Frequency < 8000)
device->Frequency = 8000;
if(ConfigValueExists(NULL, "format"))
device->Flags |= DEVICE_CHANNELS_REQUEST;
fmt = GetConfigValue(NULL, "format", "AL_FORMAT_STEREO16");
GetFormatFromString(fmt, &device->FmtChans, &device->FmtType);
device->NumUpdates = GetConfigValueInt(NULL, "periods", 4);
if(device->NumUpdates < 2)
device->NumUpdates = 4;
device->UpdateSize = GetConfigValueInt(NULL, "period_size", 1024);
if(device->UpdateSize <= 0)
device->UpdateSize = 1024;
device->MaxNoOfSources = GetConfigValueInt(NULL, "sources", 256);
if(device->MaxNoOfSources <= 0)
device->MaxNoOfSources = 256;
device->AuxiliaryEffectSlotMax = GetConfigValueInt(NULL, "slots", 4);
if(device->AuxiliaryEffectSlotMax <= 0)
device->AuxiliaryEffectSlotMax = 4;
device->NumStereoSources = 1;
device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
device->NumAuxSends = GetConfigValueInt(NULL, "sends", 1);
if(device->NumAuxSends > MAX_SENDS)
device->NumAuxSends = MAX_SENDS;
device->Bs2bLevel = GetConfigValueInt(NULL, "cf_level", 0);
// Find a playback device to open
LockLists();
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;
}
}
UnlockLists();
if(!bDeviceFound)
{
// No suitable output device found
alcSetError(NULL, ALC_INVALID_VALUE);
free(device);
device = NULL;
}
return device;
}
/*
alcCloseDevice
Close the specified Device
*/
ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *pDevice)
{
ALCdevice **list;
LockLists();
list = &g_pDeviceList;
while(*list && *list != pDevice)
list = &(*list)->next;
if(!*list || (*list)->IsCaptureDevice)
{
alcSetError(*list, ALC_INVALID_DEVICE);
UnlockLists();
return ALC_FALSE;
}
*list = (*list)->next;
g_ulDeviceCount--;
UnlockLists();
if(pDevice->NumContexts > 0)
{
#ifdef _DEBUG
AL_PRINT("alcCloseDevice(): destroying %u Context(s)\n", pDevice->NumContexts);
#endif
while(pDevice->NumContexts > 0)
alcDestroyContext(pDevice->Contexts[0]);
}
ALCdevice_ClosePlayback(pDevice);
if(pDevice->BufferMap.size > 0)
{
#ifdef _DEBUG
AL_PRINT("alcCloseDevice(): deleting %d Buffer(s)\n", pDevice->BufferMap.size);
#endif
ReleaseALBuffers(pDevice);
}
ResetUIntMap(&pDevice->BufferMap);
if(pDevice->EffectMap.size > 0)
{
#ifdef _DEBUG
AL_PRINT("alcCloseDevice(): deleting %d Effect(s)\n", pDevice->EffectMap.size);
#endif
ReleaseALEffects(pDevice);
}
ResetUIntMap(&pDevice->EffectMap);
if(pDevice->FilterMap.size > 0)
{
#ifdef _DEBUG
AL_PRINT("alcCloseDevice(): deleting %d Filter(s)\n", pDevice->FilterMap.size);
#endif
ReleaseALFilters(pDevice);
}
ResetUIntMap(&pDevice->FilterMap);
free(pDevice->Bs2b);
pDevice->Bs2b = NULL;
free(pDevice->szDeviceName);
pDevice->szDeviceName = NULL;
free(pDevice->Contexts);
pDevice->Contexts = NULL;
//Release device structure
memset(pDevice, 0, sizeof(ALCdevice));
free(pDevice);
return ALC_TRUE;
}
ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(void)
{
ALCdevice *device;
DO_INITCONFIG();
device = calloc(1, sizeof(ALCdevice));
if(!device)
{
alcSetError(NULL, ALC_OUT_OF_MEMORY);
return NULL;
}
//Validate device
device->Connected = ALC_TRUE;
device->IsCaptureDevice = AL_FALSE;
device->IsLoopbackDevice = AL_TRUE;
device->LastError = ALC_NO_ERROR;
device->Flags = 0;
device->Bs2b = NULL;
device->szDeviceName = NULL;
device->Contexts = NULL;
device->NumContexts = 0;
InitUIntMap(&device->BufferMap);
InitUIntMap(&device->EffectMap);
InitUIntMap(&device->FilterMap);
//Set output format
device->Frequency = 44100;
device->FmtChans = DevFmtStereo;
device->FmtType = DevFmtShort;
device->NumUpdates = 0;
device->UpdateSize = 0;
device->MaxNoOfSources = GetConfigValueInt(NULL, "sources", 256);
if(device->MaxNoOfSources <= 0)
device->MaxNoOfSources = 256;
device->AuxiliaryEffectSlotMax = GetConfigValueInt(NULL, "slots", 4);
if(device->AuxiliaryEffectSlotMax <= 0)
device->AuxiliaryEffectSlotMax = 4;
device->NumStereoSources = 1;
device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
device->NumAuxSends = GetConfigValueInt(NULL, "sends", 1);
if(device->NumAuxSends > MAX_SENDS)
device->NumAuxSends = MAX_SENDS;
device->Bs2bLevel = GetConfigValueInt(NULL, "cf_level", 0);
// Open the "backend"
LockLists();
device->Funcs = &BackendLoopback.Funcs;
ALCdevice_OpenPlayback(device, "Loopback");
device->next = g_pDeviceList;
g_pDeviceList = device;
g_ulDeviceCount++;
UnlockLists();
return device;
}
ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALenum channels, ALenum type)
{
ALCboolean ret = ALC_FALSE;
LockLists();
if(!IsDevice(device) || !device->IsLoopbackDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else if(freq <= 0)
alcSetError(device, ALC_INVALID_VALUE);
else if(IsValidType(type) == AL_FALSE ||
IsValidChannels(channels) == AL_FALSE)
alcSetError(device, ALC_INVALID_ENUM);
else
{
if((type == DevFmtByte || type == DevFmtUByte || type == DevFmtShort ||
type == DevFmtUShort || type == DevFmtFloat) &&
(channels == DevFmtMono || channels == DevFmtStereo ||
channels == DevFmtQuad || channels == DevFmtX51 ||
channels == DevFmtX61 || channels == DevFmtX71) &&
freq >= 8000)
ret = ALC_TRUE;
}
UnlockLists();
return ret;
}
ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
{
LockLists();
if(!IsDevice(device) || !device->IsLoopbackDevice)
alcSetError(device, ALC_INVALID_DEVICE);
else if(samples < 0)
alcSetError(device, ALC_INVALID_VALUE);
else
aluMixData(device, buffer, samples);
UnlockLists();
}
static void ReleaseALC(void)
{
free(alcDeviceList); alcDeviceList = NULL;
alcDeviceListSize = 0;
free(alcAllDeviceList); alcAllDeviceList = NULL;
alcAllDeviceListSize = 0;
free(alcCaptureDeviceList); alcCaptureDeviceList = NULL;
alcCaptureDeviceListSize = 0;
free(alcDefaultDeviceSpecifier);
alcDefaultDeviceSpecifier = NULL;
free(alcDefaultAllDeviceSpecifier);
alcDefaultAllDeviceSpecifier = NULL;
free(alcCaptureDefaultDeviceSpecifier);
alcCaptureDefaultDeviceSpecifier = NULL;
#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);
}
}
///////////////////////////////////////////////////////