/** * 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 */ #define _CRT_SECURE_NO_DEPRECATE // get rid of sprintf security warnings on VS2005 #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" /////////////////////////////////////////////////////// // DEBUG INFORMATION char _alDebug[256]; /////////////////////////////////////////////////////// #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 { "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); } } 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->lNumStereoSources = 1; pContext->lNumMonoSources = pContext->Device->MaxNoOfSources - pContext->lNumStereoSources; pContext->ExtensionList = "AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_OFFSET 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); } } /* 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(deviceName && !deviceName[0]) deviceName = NULL; pDevice = malloc(sizeof(ALCdevice)); if (pDevice) { if (SampleSize > 0) { //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; } } } else SetALCError(ALC_INVALID_VALUE); if(!DeviceFound) { 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 = MAX_SENDS; break; case ALC_ATTRIBUTES_SIZE: if(!device) SetALCError(ALC_INVALID_DEVICE); else if(!size) SetALCError(ALC_INVALID_VALUE); else *data = 12; break; case ALC_ALL_ATTRIBUTES: if(!device) SetALCError(ALC_INVALID_DEVICE); else if (size < 7) SetALCError(ALC_INVALID_VALUE); else { int i = 0; 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; SuspendContext(NULL); if(device->Context && size >= 12) { data[i++] = ALC_MONO_SOURCES; data[i++] = device->Context->lNumMonoSources; data[i++] = ALC_STEREO_SOURCES; data[i++] = device->Context->lNumStereoSources; data[i++] = ALC_MAX_AUXILIARY_SENDS; data[i++] = MAX_SENDS; } ProcessContext(NULL); data[i++] = 0; } 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->Context->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->Context->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; 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 Voice Count attributes if (attrList) { 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; ALContext->lNumStereoSources = ulRequestedStereoSources; ALContext->lNumMonoSources = ALContext->Device->MaxNoOfSources - ALContext->lNumStereoSources; break; } ulAttributeIndex += 2; } } } 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; // Find a playback device to open for(i = 0;BackendList[i].Init;i++) { device->Funcs = &BackendList[i].Funcs; if(ALCdevice_OpenPlayback(device, deviceName)) { SuspendContext(NULL); device->next = g_pDeviceList; g_pDeviceList = device; g_ulDeviceCount++; ProcessContext(NULL); bDeviceFound = AL_TRUE; break; } } if (!bDeviceFound) { // No suitable output device found free(device); device = NULL; } } 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); } } ///////////////////////////////////////////////////////