diff options
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/ALc.c | 1117 | ||||
-rw-r--r-- | Alc/alcConfig.c | 283 | ||||
-rw-r--r-- | Alc/alcThread.c | 125 | ||||
-rw-r--r-- | Alc/alsa.c | 828 | ||||
-rw-r--r-- | Alc/dsound.c | 294 | ||||
-rw-r--r-- | Alc/oss.c | 328 | ||||
-rw-r--r-- | Alc/winmm.c | 421 |
7 files changed, 3396 insertions, 0 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c new file mode 100644 index 00000000..2adcbb8e --- /dev/null +++ b/Alc/ALc.c @@ -0,0 +1,1117 @@ +/** + * 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 "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +/////////////////////////////////////////////////////// +// DEBUG INFORMATION + +#ifdef _DEBUG + char szDebug[256]; +#endif + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// FUNCTION PROTOTYPES + +void fill_device_list(); + +void alc_alsa_init(BackendFuncs *func_list); +void alc_oss_init(BackendFuncs *func_list); +void alcDSoundInit(BackendFuncs *func_list); +void alcWinMMInit(BackendFuncs *FuncList); + +#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +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_DSOUND + { "dsound", alcDSoundInit, EmptyFuncs }, +#endif +#ifdef HAVE_WINMM + { "winmm", alcWinMMInit, EmptyFuncs }, +#endif + + { NULL, NULL, EmptyFuncs } +}; +#undef EmptyFuncs + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// STRING and EXTENSIONS + +typedef struct ALCextension_struct +{ + ALCchar *extName; + ALvoid *address; +} ALCextension; + +typedef struct ALCfunction_struct +{ + ALCchar *funcName; + ALvoid *address; +} ALCfunction; + +static ALCextension alcExtensions[] = { + { "ALC_ENUMERATE_ALL_EXT", (ALvoid *) NULL }, + { "ALC_ENUMERATION_EXT", (ALvoid *) NULL }, + { "ALC_EXT_CAPTURE", (ALvoid *) NULL }, + { NULL, (ALvoid *) NULL } +}; + +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 }, + + // 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"; +static ALCint alcMajorVersion = 1; +static ALCint alcMinorVersion = 1; + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// Global Variables + +// Critical Section data +extern CRITICAL_SECTION g_mutex; + +// Context List +static ALCcontext *g_pContextList = NULL; +static ALCuint g_ulContextCount = 0; + +// Context Error +static ALCenum g_eLastContextError = ALC_NO_ERROR; + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// ALC Related helper functions + +ALCchar *AppendDeviceList(char *name) +{ + static int pos; + ALCchar *ret = alcDeviceList+pos; + pos += snprintf(alcDeviceList+pos, sizeof(alcDeviceList)-pos, "%s", name) + 1; + return ret; +} + +ALCchar *AppendAllDeviceList(char *name) +{ + static int pos; + ALCchar *ret = alcAllDeviceList+pos; + pos += snprintf(alcAllDeviceList+pos, sizeof(alcAllDeviceList)-pos, "%s", name) + 1; + return ret; +} + +ALCchar *AppendCaptureDeviceList(char *name) +{ + static int pos; + ALCchar *ret = alcCaptureDeviceList+pos; + pos += snprintf(alcCaptureDeviceList+pos, sizeof(alcCaptureDeviceList)-pos, "%s", name) + 1; + return ret; +} + +/* + IsContext + + Check pContext is a valid Context pointer +*/ +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_mutex); +} + + +/* + ProcessContext + + Thread-safe exit +*/ +ALCvoid ProcessContext(ALCcontext *pContext) +{ + (void)pContext; + LeaveCriticalSection(&g_mutex); +} + + +/* + InitContext + + Initialize Context variables +*/ +static ALvoid InitContext(ALCcontext *pContext) +{ + //Initialise listener + pContext->Listener.Gain = 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; + + strcpy(pContext->ExtensionList, "AL_EXT_EXPONENT_DISTANCE AL_EXT_LINEAR_DISTANCE AL_EXT_OFFSET"); +} + + +/* + ExitContext + + Clean up Context, destroy any remaining Sources +*/ +static ALCvoid ExitContext(ALCcontext *pContext) +{ + unsigned int i; + ALsource *ALSource; + ALsource *ALTempSource; + +#ifdef _DEBUG + if (pContext->SourceCount>0) + { + sprintf(szDebug,"OpenAL32 : alcDestroyContext() %d Source(s) NOT deleted\n", pContext->SourceCount); + OutputDebugString(szDebug); + } +#endif + + // Free all the Sources still remaining + ALSource = pContext->Source; + for (i = 0; i < pContext->SourceCount; i++) + { + ALTempSource = ALSource->next; + ALTHUNK_REMOVEENTRY(ALSource->source); + memset(ALSource, 0, sizeof(ALsource)); + free(ALSource); + ALSource = ALTempSource; + } + + //Invalidate context + pContext->LastError = AL_NO_ERROR; + pContext->InUse = AL_FALSE; +} + +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// 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; + + fill_device_list(); + + pDevice = malloc(sizeof(ALCdevice)); + if (pDevice) + { + if (SampleSize > 0) + { + //Initialise device structure + memset(pDevice, 0, sizeof(ALCdevice)); + + //Validate device + pDevice->InUse = AL_TRUE; + pDevice->IsCaptureDevice = AL_TRUE; + + pDevice->Frequency = frequency; + pDevice->Format = format; + pDevice->Channels = aluChannelsFromFormat(format); + pDevice->FrameSize = aluBytesFromFormat(format) * + pDevice->Channels; + + for(i = 0;BackendList[i].Init;i++) + { + pDevice->Funcs = &BackendList[i].Funcs; + if(ALCdevice_OpenCapture(pDevice, deviceName, frequency, format, SampleSize)) + { + 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; + + if ((pDevice)&&(pDevice->IsCaptureDevice)) + { + 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; + + fill_device_list(); + + 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) +{ + fill_device_list(); + + 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_ATTRIBUTES_SIZE: + if(!device) + SetALCError(ALC_INVALID_DEVICE); + else if(!size) + SetALCError(ALC_INVALID_VALUE); + else + *data = 11; + 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->UpdateFreq; + + data[i++] = ALC_SYNC; + data[i++] = ALC_FALSE; + + SuspendContext(NULL); + if(device->Context && size >= 11) + { + data[i++] = ALC_MONO_SOURCES; + data[i++] = device->Context->lNumMonoSources; + + data[i++] = ALC_STEREO_SOURCES; + data[i++] = device->Context->lNumStereoSources; + } + 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->UpdateFreq; + break; + + case ALC_SYNC: + if(!device) + SetALCError(ALC_INVALID_DEVICE); + else if(!size) + SetALCError(ALC_INVALID_VALUE); + else + *data = ALC_FALSE; + 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; + ALsizei i = 0; + + (void)device; + + if (extName) + { + while(alcExtensions[i].extName && + strcasecmp(alcExtensions[i].extName,extName) != 0) + i++; + + if (alcExtensions[i].extName) + bResult = ALC_TRUE; + } + 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; + + // Lock context list + SuspendContext(NULL); + + if (IsContext(context)) + { + // Lock context + SuspendContext(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; + + 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; + + 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; + + 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; + + fill_device_list(); + + device = malloc(sizeof(ALCdevice)); + if (device) + { + const char *fmt; + + //Initialise device structure + memset(device, 0, sizeof(ALCdevice)); + + //Validate device + device->InUse = AL_TRUE; + 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); + switch(device->Format) + { + case AL_FORMAT_MONO8: + device->Channels = 1; + device->FrameSize = 1; + break; + case AL_FORMAT_STEREO8: + device->Channels = 2; + device->FrameSize = 2; + break; + case AL_FORMAT_QUAD8: + device->Channels = 4; + device->FrameSize = 4; + break; + case AL_FORMAT_MONO16: + device->Channels = 1; + device->FrameSize = 2; + break; + case AL_FORMAT_STEREO16: + device->Channels = 2; + device->FrameSize = 4; + break; + case AL_FORMAT_QUAD16: + device->Channels = 4; + device->FrameSize = 8; + break; + default: + device->Format = AL_FORMAT_STEREO16; + device->Channels = 2; + device->FrameSize = 4; + break; + } + + device->UpdateFreq = GetConfigValueInt(NULL, "refresh", 0); + if((ALint)device->UpdateFreq <= 0) + device->UpdateFreq = 8192 * device->Frequency / 22050; + + // Find a playback device to open + for(i = 0;BackendList[i].Init;i++) + { + device->Funcs = &BackendList[i].Funcs; + if(ALCdevice_OpenPlayback(device, deviceName)) + { + 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; + + if ((pDevice)&&(!pDevice->IsCaptureDevice)) + { + ALCdevice_ClosePlayback(pDevice); + + //Release device structure + memset(pDevice, 0, sizeof(ALCdevice)); + free(pDevice); + + bReturn = ALC_TRUE; + } + else + SetALCError(ALC_INVALID_DEVICE); + + return bReturn; +} +/////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// ALC Device Specific Functions + +void fill_device_list() +{ + static int done = 0; + if(!done) + { + int i; + for(i = 0;BackendList[i].Init;i++) + BackendList[i].Init(&BackendList[i].Funcs); + done = 1; + } +} diff --git a/Alc/alcConfig.c b/Alc/alcConfig.c new file mode 100644 index 00000000..f20b5e4f --- /dev/null +++ b/Alc/alcConfig.c @@ -0,0 +1,283 @@ +/** + * 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 <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +typedef struct ConfigEntry { + char *key; + char *value; +} ConfigEntry; + +typedef struct ConfigBlock { + char *name; + ConfigEntry *entries; + size_t entryCount; +} ConfigBlock; + +static ConfigBlock *cfgBlocks; +static size_t cfgCount; + +static char buffer[1024]; + +static void LoadConfigFromFile(FILE *f) +{ + ConfigBlock *curBlock = cfgBlocks; + ConfigEntry *ent; + + while(fgets(buffer, sizeof(buffer), f)) + { + size_t i = 0; + + while(isspace(buffer[i])) + i++; + if(!buffer[i] || buffer[i] == '#') + continue; + + memmove(buffer, buffer+i, strlen(buffer+i)+1); + + if(buffer[0] == '[') + { + ConfigBlock *nextBlock; + + i = 1; + while(buffer[i] && buffer[i] != ']') + i++; + + if(!buffer[i]) + { + fprintf(stderr, "openal: config parse error: bad line \"%s\"\n", buffer); + continue; + } + buffer[i] = 0; + + do { + i++; + if(buffer[i] && !isspace(buffer[i])) + { + if(buffer[i] != '#') + fprintf(stderr, "openal: config warning: extra data after block: \"%s\"\n", buffer+i); + break; + } + } while(buffer[i]); + + nextBlock = NULL; + for(i = 0;i < cfgCount;i++) + { + if(strcasecmp(cfgBlocks[i].name, buffer+1) == 0) + { + nextBlock = cfgBlocks+i; +// printf("found block '%s'\n", nextBlock->name); + break; + } + } + + if(!nextBlock) + { + nextBlock = realloc(cfgBlocks, (cfgCount+1)*sizeof(ConfigBlock)); + if(!nextBlock) + { + fprintf(stderr, "openal: config parse error: error reallocating config blocks\n"); + continue; + } + cfgBlocks = nextBlock; + nextBlock = cfgBlocks+cfgCount; + cfgCount++; + + nextBlock->name = strdup(buffer+1); + nextBlock->entries = NULL; + nextBlock->entryCount = 0; + +// printf("found new block '%s'\n", nextBlock->name); + } + curBlock = nextBlock; + continue; + } + + /* Look for the option name */ + i = 0; + while(buffer[i] && buffer[i] != '#' && buffer[i] != '=' && + !isspace(buffer[i])) + i++; + + if(!buffer[i] || buffer[i] == '#' || i == 0) + { + fprintf(stderr, "openal: config parse error: malformed option line: \"%s\"\n", buffer); + continue; + } + + /* Seperate the option */ + if(buffer[i] != '=') + { + buffer[i++] = 0; + + while(isspace(buffer[i])) + i++; + if(buffer[i] != '=') + { + fprintf(stderr, "openal: config parse error: option without a value: \"%s\"\n", buffer); + continue; + } + } + /* Find the start of the value */ + buffer[i++] = 0; + while(isspace(buffer[i])) + i++; + + /* Check if we already have this option set */ + ent = curBlock->entries; + while((size_t)(ent-curBlock->entries) < curBlock->entryCount) + { + if(strcasecmp(ent->key, buffer) == 0) + break; + ent++; + } + + if((size_t)(ent-curBlock->entries) >= curBlock->entryCount) + { + /* Allocate a new option entry */ + ent = realloc(curBlock->entries, (curBlock->entryCount+1)*sizeof(ConfigEntry)); + if(!ent) + { + fprintf(stderr, "openal: config parse error: error reallocating config entries\n"); + continue; + } + curBlock->entries = ent; + ent = curBlock->entries + curBlock->entryCount; + curBlock->entryCount++; + + ent->key = strdup(buffer); + ent->value = NULL; + } + + /* Look for the end of the line (Null term, new-line, or #-symbol) and + eat up the trailing whitespace */ + memmove(buffer, buffer+i, strlen(buffer+i)+1); + + i = 0; + while(buffer[i] && buffer[i] != '#' && buffer[i] != '\n') + i++; + do { + i--; + } while(isspace(buffer[i])); + buffer[++i] = 0; + + free(ent->value); + ent->value = strdup(buffer); + +// printf("found '%s' = '%s'\n", ent->key, ent->value); + } +} + +void ReadALConfig(void) +{ + FILE *f; + + cfgBlocks = calloc(1, sizeof(ConfigBlock)); + cfgBlocks->name = strdup("general"); + cfgCount = 1; + +#ifdef _WIN32 +#else + f = fopen("/etc/openal/config", "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + if(getenv("HOME") && *(getenv("HOME"))) + { + snprintf(buffer, sizeof(buffer), "%s/.openalrc", getenv("HOME")); + f = fopen(buffer, "r"); + if(f) + { + LoadConfigFromFile(f); + fclose(f); + } + } +#endif +} + +void FreeALConfig(void) +{ + size_t i; + + for(i = 0;i < cfgCount;i++) + { + size_t j; + for(j = 0;j < cfgBlocks[i].entryCount;j++) + { + free(cfgBlocks[i].entries[j].key); + free(cfgBlocks[i].entries[j].value); + } + free(cfgBlocks[i].entries); + free(cfgBlocks[i].name); + } + free(cfgBlocks); + cfgBlocks = NULL; + cfgCount = 0; +} + +const char *GetConfigValue(const char *blockName, const char *keyName, const char *def) +{ + size_t i, j; + + if(keyName) + { + if(!blockName) + blockName = "general"; + + for(i = 0;i < cfgCount;i++) + { + if(strcasecmp(cfgBlocks[i].name, blockName) != 0) + continue; + + for(j = 0;j < cfgBlocks[i].entryCount;j++) + { + if(strcasecmp(cfgBlocks[i].entries[j].key, keyName) == 0) + return cfgBlocks[i].entries[j].value; + } + } + } + + return def; +} + +int GetConfigValueInt(const char *blockName, const char *keyName, int def) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + + if(!val[0]) return def; + return strtol(val, NULL, 0); +} + +float GetConfigValueFloat(const char *blockName, const char *keyName, float def) +{ + const char *val = GetConfigValue(blockName, keyName, ""); + + if(!val[0]) return def; +#ifdef HAVE_STRTOF + return strtof(val, NULL); +#else + return (float)strtod(val, NULL); +#endif +} diff --git a/Alc/alcThread.c b/Alc/alcThread.c new file mode 100644 index 00000000..6752f702 --- /dev/null +++ b/Alc/alcThread.c @@ -0,0 +1,125 @@ +/** + * 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 <stdlib.h> + +#include "alMain.h" +#include "alThunk.h" + + +#ifdef _WIN32 + +#include <windows.h> + +typedef struct { + ALuint (*func)(ALvoid*); + ALvoid *ptr; + HANDLE thread; +} ThreadInfo; + +static DWORD CALLBACK StarterFunc(void *ptr) +{ + ThreadInfo *inf = (ThreadInfo*)ptr; + ALint ret; + + ret = inf->func(inf->ptr); + ExitThread((DWORD)ret); + + return (DWORD)ret; +} + +ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr) +{ + ThreadInfo *inf = malloc(sizeof(ThreadInfo)); + if(!inf) return 0; + + inf->func = func; + inf->ptr = ptr; + + inf->thread = CreateThread(NULL, 0, StarterFunc, inf, 0, NULL); + if(!inf->thread) + { + free(inf); + return NULL; + } + + return inf; +} + +ALuint StopThread(ALvoid *thread) +{ + ThreadInfo *inf = thread; + DWORD ret = 0; + + WaitForSingleObject(inf->thread, INFINITE); + GetExitCodeThread(inf->thread, &ret); + + free(inf); + + return (ALuint)ret; +} + +#else + +#include <pthread.h> + +typedef struct { + ALuint (*func)(ALvoid*); + ALvoid *ptr; + pthread_t thread; +} ThreadInfo; + +static void *StarterFunc(void *ptr) +{ + ThreadInfo *inf = (ThreadInfo*)ptr; + ALint ret; + + ret = inf->func(inf->ptr); + return (void*)ret; +} + +ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr) +{ + ThreadInfo *inf = malloc(sizeof(ThreadInfo)); + if(!inf) return 0; + + inf->func = func; + inf->ptr = ptr; + if(pthread_create(&inf->thread, NULL, StarterFunc, inf) != 0) + { + free(inf); + return NULL; + } + + return inf; +} + +ALuint StopThread(ALvoid *thread) +{ + ThreadInfo *inf = thread; + void *ret; + + pthread_join(inf->thread, &ret); + free(inf); + + return (ALuint)ret; +} + +#endif diff --git a/Alc/alsa.c b/Alc/alsa.c new file mode 100644 index 00000000..df866a66 --- /dev/null +++ b/Alc/alsa.c @@ -0,0 +1,828 @@ +/** + * 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 <stdlib.h> +#include <stdio.h> +#include <memory.h> +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#endif +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include <alsa/asoundlib.h> + + +typedef struct { + snd_pcm_t *pcmHandle; + snd_pcm_format_t format; + int killNow; + ALvoid *thread; +} alsa_data; + +typedef struct { + ALCchar *name; + int card, dev; +} DevMap; + +static void *alsa_handle; +#define MAKE_FUNC(f) static typeof(f) * p##f +MAKE_FUNC(snd_strerror); +MAKE_FUNC(snd_pcm_open); +MAKE_FUNC(snd_pcm_close); +MAKE_FUNC(snd_pcm_nonblock); +MAKE_FUNC(snd_pcm_frames_to_bytes); +MAKE_FUNC(snd_pcm_hw_params_malloc); +MAKE_FUNC(snd_pcm_hw_params_free); +MAKE_FUNC(snd_pcm_hw_params_any); +MAKE_FUNC(snd_pcm_hw_params_set_access); +MAKE_FUNC(snd_pcm_hw_params_set_format); +MAKE_FUNC(snd_pcm_hw_params_set_channels); +MAKE_FUNC(snd_pcm_hw_params_set_periods_near); +MAKE_FUNC(snd_pcm_hw_params_set_rate_near); +MAKE_FUNC(snd_pcm_hw_params_set_rate); +MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near); +MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min); +MAKE_FUNC(snd_pcm_hw_params); +MAKE_FUNC(snd_pcm_prepare); +MAKE_FUNC(snd_pcm_start); +MAKE_FUNC(snd_pcm_resume); +MAKE_FUNC(snd_pcm_state); +MAKE_FUNC(snd_pcm_avail_update); +MAKE_FUNC(snd_pcm_areas_silence); +MAKE_FUNC(snd_pcm_mmap_begin); +MAKE_FUNC(snd_pcm_mmap_commit); +MAKE_FUNC(snd_pcm_drain); +MAKE_FUNC(snd_pcm_info_malloc); +MAKE_FUNC(snd_pcm_info_free); +MAKE_FUNC(snd_pcm_info_set_device); +MAKE_FUNC(snd_pcm_info_set_subdevice); +MAKE_FUNC(snd_pcm_info_set_stream); +MAKE_FUNC(snd_pcm_info_get_name); +MAKE_FUNC(snd_ctl_pcm_next_device); +MAKE_FUNC(snd_ctl_pcm_info); +MAKE_FUNC(snd_ctl_open); +MAKE_FUNC(snd_ctl_close); +MAKE_FUNC(snd_ctl_card_info_malloc); +MAKE_FUNC(snd_ctl_card_info_free); +MAKE_FUNC(snd_ctl_card_info); +MAKE_FUNC(snd_ctl_card_info_get_name); +MAKE_FUNC(snd_card_next); +#undef MAKE_FUNC + +#define MAX_DEVICES 16 +#define MAX_ALL_DEVICES 32 + +static DevMap allDevNameMap[MAX_ALL_DEVICES]; +static ALCchar *alsaDeviceList[MAX_DEVICES]; +static ALCchar *alsaCaptureDeviceList[MAX_DEVICES]; + +static int xrun_recovery(snd_pcm_t *handle, int err) +{ + if (err == -EPIPE) + { /* under-run */ + err = psnd_pcm_prepare(handle); + if(err >= 0) + err = psnd_pcm_start(handle); + if (err < 0) + printf("Can't recover from underrun, prepare failed: %s\n", psnd_strerror(err)); + } + else if (err == -ESTRPIPE) + { + while ((err = psnd_pcm_resume(handle)) == -EAGAIN) + usleep(1); /* wait until the suspend flag is released */ + if (err < 0) + { + err = psnd_pcm_prepare(handle); + if(err >= 0) + err = psnd_pcm_start(handle); + if (err < 0) + printf("Can't recover from suspend, prepare failed: %s\n", psnd_strerror(err)); + } + } + return err; +} + + +static ALuint ALSAProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + alsa_data *data = (alsa_data*)pDevice->ExtraData; + const snd_pcm_channel_area_t *areas = NULL; + snd_pcm_sframes_t avail, commitres; + snd_pcm_uframes_t offset, frames; + char *WritePtr; + int WriteCnt; + int err; + + while(!data->killNow) + { + snd_pcm_state_t state = psnd_pcm_state(data->pcmHandle); + if(state == SND_PCM_STATE_XRUN) + { + err = xrun_recovery(data->pcmHandle, -EPIPE); + if (err < 0) + { + fprintf(stderr, "XRUN recovery failed: %s\n", psnd_strerror(err)); + break; + } + } + else if (state == SND_PCM_STATE_SUSPENDED) + { + err = xrun_recovery(data->pcmHandle, -ESTRPIPE); + if (err < 0) + { + fprintf(stderr, "SUSPEND recovery failed: %s\n", psnd_strerror(err)); + break; + } + } + + avail = psnd_pcm_avail_update(data->pcmHandle); + if(avail < 0) + { + err = xrun_recovery(data->pcmHandle, avail); + if (err < 0) + { + fprintf(stderr, "available update failed: %s\n", psnd_strerror(err)); + break; + } + } + + // make sure there's frames to process + if(avail == 0) + { + usleep(1000); + continue; + } + + // it is possible that contiguous areas are smaller, thus we use a loop + while (avail > 0) + { + frames = avail; + + err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); + if (err < 0) + { + err = xrun_recovery(data->pcmHandle, err); + if (err < 0) + fprintf(stderr, "mmap begin error: %s\n", psnd_strerror(err)); + break; + } + + SuspendContext(NULL); + if(pDevice->Context) + { + // If we have an active context, mix data directly into output + // buffer + WritePtr = (char*)areas->addr + (offset * areas->step / 8); + WriteCnt = psnd_pcm_frames_to_bytes(data->pcmHandle, frames); + + aluMixData(pDevice->Context, WritePtr, WriteCnt, pDevice->Format); + } + else + psnd_pcm_areas_silence(areas, offset, pDevice->Channels, frames, data->format); + ProcessContext(NULL); + + commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, frames); + if (commitres < 0 || (commitres-frames) != 0) + { + fprintf(stderr, "mmap commit error: %s\n", + psnd_strerror(commitres >= 0 ? -EPIPE : commitres)); + break; + } + + avail -= frames; + } + } + + return 0; +} + +static void fill_silence(snd_pcm_t *pcmHandle, snd_pcm_format_t alsaFormat, int channels) +{ + const snd_pcm_channel_area_t *areas = NULL; + snd_pcm_sframes_t avail, commitres; + snd_pcm_uframes_t offset, frames; + int err; + + avail = psnd_pcm_avail_update(pcmHandle); + if(avail < 0) + { + err = xrun_recovery(pcmHandle, avail); + if (err < 0) + { + fprintf(stderr, "available update failed: %s\n", psnd_strerror(err)); + return; + } + } + + // it is possible that contiguous areas are smaller, thus we use a loop + while (avail > 0) + { + frames = avail; + + err = psnd_pcm_mmap_begin(pcmHandle, &areas, &offset, &frames); + if (err < 0) + { + err = xrun_recovery(pcmHandle, err); + if (err < 0) + { + fprintf(stderr, "mmap begin error: %s\n", psnd_strerror(err)); + break; + } + continue; + } + + psnd_pcm_areas_silence(areas, offset, channels, frames, alsaFormat); + + commitres = psnd_pcm_mmap_commit(pcmHandle, offset, frames); + if (commitres < 0 || (commitres-frames) != 0) + { + fprintf(stderr, "mmap commit error: %s\n", + psnd_strerror(commitres >= 0 ? -EPIPE : commitres)); + break; + } + + avail -= frames; + } +} + +static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + snd_pcm_uframes_t bufferSizeInFrames; + snd_pcm_hw_params_t *p = NULL; + unsigned int periods; + alsa_data *data; + char driver[64]; + int i; + + strncpy(driver, GetConfigValue("alsa", "default", "default"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + if(deviceName) + { + size_t idx; + + for(idx = 0;idx < MAX_ALL_DEVICES;idx++) + { + if(allDevNameMap[idx].name && + strcmp(deviceName, allDevNameMap[idx].name) == 0) + { + if(idx > 0) + sprintf(driver, "hw:%d,%d", allDevNameMap[idx].card, allDevNameMap[idx].dev); + goto open_alsa; + } + } + for(idx = 0;idx < MAX_DEVICES;idx++) + { + if(alsaDeviceList[idx] && + strcmp(deviceName, alsaDeviceList[idx]) == 0) + { + if(idx > 0) + sprintf(driver, "hw:%d,0", idx-1); + goto open_alsa; + } + } + return ALC_FALSE; + } + + if(deviceName) + strcpy(device->szDeviceName, deviceName); + else + strcpy(device->szDeviceName, alsaDeviceList[0]); + +open_alsa: + data = (alsa_data*)calloc(1, sizeof(alsa_data)); + + i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if(i < 0) + { + usleep(200000); + i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + } + if(i >= 0) + { + i = psnd_pcm_nonblock(data->pcmHandle, 0); + if(i < 0) + psnd_pcm_close(data->pcmHandle); + } + if(i < 0) + { + free(data); + return ALC_FALSE; + } + + switch(device->Format) + { + case AL_FORMAT_MONO8: + case AL_FORMAT_STEREO8: + case AL_FORMAT_QUAD8: + data->format = SND_PCM_FORMAT_U8; + break; + case AL_FORMAT_MONO16: + case AL_FORMAT_STEREO16: + case AL_FORMAT_QUAD16: + data->format = SND_PCM_FORMAT_S16; + break; + default: + data->format = SND_PCM_FORMAT_UNKNOWN; + fprintf(stderr, "Unknown format?! %x\n", device->Format); + } + + periods = GetConfigValueInt("alsa", "periods", 4); + if((int)periods <= 0) + periods = 4; + bufferSizeInFrames = device->UpdateFreq; + + psnd_pcm_hw_params_malloc(&p); +#define ok(func, str) (i=(func),((i<0)?fprintf(stderr,"%s failed: %s\n", str, psnd_strerror(i)),0:1)) + /* start with the largest configuration space possible */ + if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") && + /* set interleaved access */ + ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access") && + /* set format (implicitly sets sample bits) */ + ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, data->format), "set format") && + /* set channels (implicitly sets frame bits) */ + ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, device->Channels), "set channels") && + /* set periods (implicitly constrains period/buffer parameters) */ + ok(psnd_pcm_hw_params_set_periods_near(data->pcmHandle, p, &periods, NULL), "set periods near") && + /* set rate (implicitly constrains period/buffer parameters) */ + ok(psnd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &device->Frequency, NULL), "set rate near") && + /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ + ok(psnd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size near") && + /* install and prepare hardware configuration */ + ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params"))) + { + psnd_pcm_hw_params_free(p); + psnd_pcm_close(data->pcmHandle); + free(data); + return ALC_FALSE; + } +#undef ok + psnd_pcm_hw_params_free(p); + + device->MaxNoOfSources = 256; + device->UpdateFreq = bufferSizeInFrames; + + i = psnd_pcm_prepare(data->pcmHandle); + if(i < 0) + { + fprintf(stderr, "prepare error: %s\n", psnd_strerror(i)); + psnd_pcm_close(data->pcmHandle); + free(data); + return ALC_FALSE; + } + + fill_silence(data->pcmHandle, data->format, device->Channels); + i = psnd_pcm_start(data->pcmHandle); + if(i < 0) + { + fprintf(stderr, "start error: %s\n", psnd_strerror(i)); + psnd_pcm_close(data->pcmHandle); + free(data); + return ALC_FALSE; + } + + device->ExtraData = data; + data->thread = StartThread(ALSAProc, device); + if(data->thread == NULL) + { + psnd_pcm_close(data->pcmHandle); + device->ExtraData = NULL; + free(data); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void alsa_close_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + data->killNow = 1; + StopThread(data->thread); + psnd_pcm_close(data->pcmHandle); + + free(data); + device->ExtraData = NULL; +} + + +static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + snd_pcm_format_t alsaFormat; + snd_pcm_hw_params_t *p; + unsigned int periods = 4; + snd_pcm_uframes_t bufferSizeInFrames = SampleSize; + alsa_data *data; + char driver[64]; + int i; + + strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + if(deviceName) + { + size_t idx; + + for(idx = 0;idx < MAX_DEVICES;idx++) + { + if(alsaCaptureDeviceList[idx] && + strcmp(deviceName, alsaCaptureDeviceList[idx]) == 0) + { + if(idx > 0) + sprintf(driver, "hw:%d,0", idx-1); + goto open_alsa; + } + } + return ALC_FALSE; + } + + if(deviceName) + strcpy(pDevice->szDeviceName, deviceName); + else + strcpy(pDevice->szDeviceName, alsaCaptureDeviceList[0]); + +open_alsa: + data = (alsa_data*)calloc(1, sizeof(alsa_data)); + + i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + if(i < 0) + { + usleep(200000); + i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + } + if(i >= 0) + { + i = psnd_pcm_nonblock(data->pcmHandle, 0); + if(i < 0) + psnd_pcm_close(data->pcmHandle); + } + if(i < 0) + { + free(data); + return ALC_FALSE; + } + + switch(aluBytesFromFormat(format)) + { + case 1: + alsaFormat = SND_PCM_FORMAT_U8; + break; + case 2: + alsaFormat = SND_PCM_FORMAT_S16; + break; + default: + alsaFormat = SND_PCM_FORMAT_UNKNOWN; + fprintf(stderr, "Unknown format?! %x\n", format); + } + + psnd_pcm_hw_params_malloc(&p); +#define ok(func, str) (i=(func),((i<0)?fprintf(stderr,"%s failed\n", str),0:1)) + /* start with the largest configuration space possible */ + if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") && + /* set interleaved access */ + ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access") && + /* set format (implicitly sets sample bits) */ + ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, alsaFormat), "set format") && + /* set channels (implicitly sets frame bits) */ + ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, pDevice->Channels), "set channels") && + /* set periods (implicitly constrains period/buffer parameters) */ + ok(psnd_pcm_hw_params_set_periods_near(data->pcmHandle, p, &periods, NULL), "set periods near") && + /* set rate (implicitly constrains period/buffer parameters) */ + ok(psnd_pcm_hw_params_set_rate(data->pcmHandle, p, frequency, 0), "set rate") && + /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ + ok(psnd_pcm_hw_params_set_buffer_size_min(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size min") && + /* install and prepare hardware configuration */ + ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params"))) + { + psnd_pcm_hw_params_free(p); + psnd_pcm_close(data->pcmHandle); + free(data); + return ALC_FALSE; + } +#undef ok + psnd_pcm_hw_params_free(p); + + i = psnd_pcm_prepare(data->pcmHandle); + if(i < 0) + { + fprintf(stderr, "prepare error: %s\n", psnd_strerror(i)); + psnd_pcm_close(data->pcmHandle); + free(data); + return ALC_FALSE; + } + + pDevice->ExtraData = data; + return ALC_TRUE; +} + +static void alsa_close_capture(ALCdevice *pDevice) +{ + alsa_data *data = (alsa_data*)pDevice->ExtraData; + psnd_pcm_close(data->pcmHandle); + + free(data); + pDevice->ExtraData = NULL; +} + +static void alsa_start_capture(ALCdevice *pDevice) +{ + alsa_data *data = (alsa_data*)pDevice->ExtraData; + psnd_pcm_start(data->pcmHandle); +} + +static void alsa_stop_capture(ALCdevice *pDevice) +{ + alsa_data *data = (alsa_data*)pDevice->ExtraData; + psnd_pcm_drain(data->pcmHandle); +} + +static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + alsa_data *data = (alsa_data*)pDevice->ExtraData; + const snd_pcm_channel_area_t *areas = NULL; + snd_pcm_sframes_t frames, commitres; + snd_pcm_uframes_t size, offset; + int err; + + frames = psnd_pcm_avail_update(data->pcmHandle); + if(frames < 0) + { + err = xrun_recovery(data->pcmHandle, frames); + if (err < 0) + fprintf(stderr, "available update failed: %s\n", psnd_strerror(err)); + else + frames = psnd_pcm_avail_update(data->pcmHandle); + } + if (frames < (snd_pcm_sframes_t)lSamples) + { + SetALCError(ALC_INVALID_VALUE); + return; + } + + // it is possible that contiguous areas are smaller, thus we use a loop + while (lSamples > 0) + { + char *Pointer; + int Count; + + size = lSamples; + err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &size); + if (err < 0) + { + err = xrun_recovery(data->pcmHandle, err); + if (err < 0) + { + fprintf(stderr, "mmap begin error: %s\n", psnd_strerror(err)); + break; + } + continue; + } + + Pointer = (char*)areas->addr + (offset * areas->step / 8); + Count = size * pDevice->FrameSize; + + memcpy(pBuffer, Pointer, Count); + pBuffer = (char*)pBuffer + Count; + + commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, size); + if (commitres < 0 || (commitres-size) != 0) + { + fprintf(stderr, "mmap commit error: %s\n", + psnd_strerror(commitres >= 0 ? -EPIPE : commitres)); + break; + } + + lSamples -= size; + } +} + +static ALCuint alsa_available_samples(ALCdevice *pDevice) +{ + alsa_data *data = (alsa_data*)pDevice->ExtraData; + snd_pcm_sframes_t frames = psnd_pcm_avail_update(data->pcmHandle); + if(frames < 0) + { + int err = xrun_recovery(data->pcmHandle, frames); + if (err < 0) + fprintf(stderr, "available update failed: %s\n", psnd_strerror(err)); + else + frames = psnd_pcm_avail_update(data->pcmHandle); + if(frames < 0) /* ew.. */ + SetALCError(ALC_INVALID_DEVICE); + } + return max(frames, 0); +} + + +BackendFuncs alsa_funcs = { + alsa_open_playback, + alsa_close_playback, + alsa_open_capture, + alsa_close_capture, + alsa_start_capture, + alsa_stop_capture, + alsa_capture_samples, + alsa_available_samples +}; + +void alc_alsa_init(BackendFuncs *func_list) +{ +#define error(...) do { \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +}while(0) + snd_ctl_t *handle; + int card, err, dev, idx = 1; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + char name[128]; + char *str; + + *func_list = alsa_funcs; + +#ifdef HAVE_DLFCN_H + alsa_handle = dlopen("libasound.so.2", RTLD_NOW); + if(!alsa_handle) + return; + dlerror(); + +#define LOAD_FUNC(f) do { \ + p##f = (typeof(f)*)dlsym(alsa_handle, #f); \ + if((str=dlerror()) != NULL) \ + { \ + dlclose(alsa_handle); \ + alsa_handle = NULL; \ + error("Could not load %s from libasound.so.2: %s", #f, str); \ + return; \ + } \ +} while(0) +#else + str = NULL; + alsa_handle = NULL; +#define LOAD_FUNC(f) p##f = f +#endif + +LOAD_FUNC(snd_strerror); +LOAD_FUNC(snd_pcm_open); +LOAD_FUNC(snd_pcm_close); +LOAD_FUNC(snd_pcm_nonblock); +LOAD_FUNC(snd_pcm_frames_to_bytes); +LOAD_FUNC(snd_pcm_hw_params_malloc); +LOAD_FUNC(snd_pcm_hw_params_free); +LOAD_FUNC(snd_pcm_hw_params_any); +LOAD_FUNC(snd_pcm_hw_params_set_access); +LOAD_FUNC(snd_pcm_hw_params_set_format); +LOAD_FUNC(snd_pcm_hw_params_set_channels); +LOAD_FUNC(snd_pcm_hw_params_set_periods_near); +LOAD_FUNC(snd_pcm_hw_params_set_rate_near); +LOAD_FUNC(snd_pcm_hw_params_set_rate); +LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near); +LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min); +LOAD_FUNC(snd_pcm_hw_params); +LOAD_FUNC(snd_pcm_prepare); +LOAD_FUNC(snd_pcm_start); +LOAD_FUNC(snd_pcm_resume); +LOAD_FUNC(snd_pcm_state); +LOAD_FUNC(snd_pcm_avail_update); +LOAD_FUNC(snd_pcm_areas_silence); +LOAD_FUNC(snd_pcm_mmap_begin); +LOAD_FUNC(snd_pcm_mmap_commit); +LOAD_FUNC(snd_pcm_drain); + +LOAD_FUNC(snd_pcm_info_malloc); +LOAD_FUNC(snd_pcm_info_free); +LOAD_FUNC(snd_pcm_info_set_device); +LOAD_FUNC(snd_pcm_info_set_subdevice); +LOAD_FUNC(snd_pcm_info_set_stream); +LOAD_FUNC(snd_pcm_info_get_name); +LOAD_FUNC(snd_ctl_pcm_next_device); +LOAD_FUNC(snd_ctl_pcm_info); +LOAD_FUNC(snd_ctl_open); +LOAD_FUNC(snd_ctl_close); +LOAD_FUNC(snd_ctl_card_info_malloc); +LOAD_FUNC(snd_ctl_card_info_free); +LOAD_FUNC(snd_ctl_card_info); +LOAD_FUNC(snd_ctl_card_info_get_name); +LOAD_FUNC(snd_card_next); + +#undef LOAD_FUNC + + psnd_ctl_card_info_malloc(&info); + psnd_pcm_info_malloc(&pcminfo); + + card = -1; + if(psnd_card_next(&card) < 0 || card < 0) + error("no playback cards found..."); + else + { + alsaDeviceList[0] = AppendDeviceList("ALSA Software on default"); + allDevNameMap[0].name = AppendAllDeviceList("ALSA Software on default"); + } + + while (card >= 0) { + sprintf(name, "hw:%d", card); + if ((err = psnd_ctl_open(&handle, name, 0)) < 0) { + error("control open (%i): %s", card, psnd_strerror(err)); + goto next_card; + } + if ((err = psnd_ctl_card_info(handle, info)) < 0) { + error("control hardware info (%i): %s", card, psnd_strerror(err)); + psnd_ctl_close(handle); + goto next_card; + } + if(card < MAX_DEVICES-1) { + snprintf(name, sizeof(name), "ALSA Software on %s", + psnd_ctl_card_info_get_name(info)); + alsaDeviceList[card+1] = AppendDeviceList(name); + } + + dev = -1; + while (idx < MAX_ALL_DEVICES) { + if (psnd_ctl_pcm_next_device(handle, &dev)<0) + error("snd_ctl_pcm_next_device"); + if (dev < 0) + break; + psnd_pcm_info_set_device(pcminfo, dev); + psnd_pcm_info_set_subdevice(pcminfo, 0); + psnd_pcm_info_set_stream(pcminfo, stream); + if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) { + if (err != -ENOENT) + error("control digital audio info (%i): %s", card, psnd_strerror(err)); + continue; + } + snprintf(name, sizeof(name), "ALSA Software on %s [%s]", + psnd_ctl_card_info_get_name(info), + psnd_pcm_info_get_name(pcminfo)); + allDevNameMap[idx].name = AppendAllDeviceList(name); + allDevNameMap[idx].card = card; + allDevNameMap[idx].dev = dev; + idx++; + } + psnd_ctl_close(handle); +next_card: + if(psnd_card_next(&card) < 0) { + error("snd_card_next"); + break; + } + } + + + stream = SND_PCM_STREAM_CAPTURE; + + card = -1; + if(psnd_card_next(&card) < 0 || card < 0) { + error("no capture cards found..."); + psnd_pcm_info_free(pcminfo); + psnd_ctl_card_info_free(info); + return; + } + + alsaCaptureDeviceList[0] = AppendCaptureDeviceList("ALSA Capture on default"); + + while (card >= 0) { + sprintf(name, "hw:%d", card); + handle = NULL; + if ((err = psnd_ctl_open(&handle, name, 0)) < 0) { + error("control open (%i): %s", card, psnd_strerror(err)); + } + if (err >= 0 && (err = psnd_ctl_card_info(handle, info)) < 0) { + error("control hardware info (%i): %s", card, psnd_strerror(err)); + psnd_ctl_close(handle); + } + else if (err >= 0 && card < MAX_DEVICES-1) + { + snprintf(name, sizeof(name), "ALSA Capture on %s", + psnd_ctl_card_info_get_name(info)); + alsaCaptureDeviceList[card+1] = AppendCaptureDeviceList(name); + } + if(handle) psnd_ctl_close(handle); + if(psnd_card_next(&card) < 0) { + error("snd_card_next"); + break; + } + } + psnd_pcm_info_free(pcminfo); + psnd_ctl_card_info_free(info); +#undef error +} diff --git a/Alc/dsound.c b/Alc/dsound.c new file mode 100644 index 00000000..5d688567 --- /dev/null +++ b/Alc/dsound.c @@ -0,0 +1,294 @@ +/** + * 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 <stdlib.h> +#include <stdio.h> +#include <memory.h> + +#include <windows.h> +#include <mmsystem.h> +#include <dsound.h> + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + // DirectSound Playback Device + LPDIRECTSOUND lpDS; + LPDIRECTSOUNDBUFFER DSpbuffer; + LPDIRECTSOUNDBUFFER DSsbuffer; + MMRESULT ulDSTimerID; +} DSoundData; + + +static ALCchar *DeviceList[16]; + +static void CALLBACK DirectSoundProc(UINT uID,UINT uReserved,DWORD_PTR dwUser,DWORD_PTR dwReserved1,DWORD_PTR dwReserved2) +{ + static DWORD OldWriteCursor=0; + + ALCdevice *pDevice = (ALCdevice *)dwUser; + DSoundData *pData = pDevice->ExtraData; + DWORD PlayCursor,WriteCursor; + BYTE *WritePtr1,*WritePtr2; + DWORD WriteCnt1,WriteCnt2; + WAVEFORMATEX OutputType; + DWORD BytesPlayed; + DWORD BufSize; + HRESULT DSRes; + + (void)uID; + (void)uReserved; + (void)dwReserved1; + (void)dwReserved2; + + BufSize = pDevice->UpdateFreq * pDevice->FrameSize; + + // Get current play and write cursors + IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer,&PlayCursor,&WriteCursor); + if (!OldWriteCursor) OldWriteCursor=WriteCursor-PlayCursor; + + // Get the output format and figure the number of bytes played (block aligned) + IDirectSoundBuffer_GetFormat(pData->DSsbuffer,&OutputType,sizeof(WAVEFORMATEX),NULL); + BytesPlayed=((((WriteCursor<OldWriteCursor)?(BufSize+WriteCursor-OldWriteCursor):(WriteCursor-OldWriteCursor))/OutputType.nBlockAlign)*OutputType.nBlockAlign); + + // Lock output buffer started at 40msec in front of the old write cursor (15msec in front of the actual write cursor) + DSRes=IDirectSoundBuffer_Lock(pData->DSsbuffer,(OldWriteCursor+(OutputType.nSamplesPerSec/25)*OutputType.nBlockAlign)%BufSize,BytesPlayed,(LPVOID*)&WritePtr1,&WriteCnt1,(LPVOID*)&WritePtr2,&WriteCnt2,0); + + // If the buffer is lost, restore it, play and lock + if (DSRes==DSERR_BUFFERLOST) + { + IDirectSoundBuffer_Restore(pData->DSsbuffer); + IDirectSoundBuffer_Play(pData->DSsbuffer,0,0,DSBPLAY_LOOPING); + DSRes=IDirectSoundBuffer_Lock(pData->DSsbuffer,(OldWriteCursor+(OutputType.nSamplesPerSec/25)*OutputType.nBlockAlign)%BufSize,BytesPlayed,(LPVOID*)&WritePtr1,&WriteCnt1,(LPVOID*)&WritePtr2,&WriteCnt2,0); + } + + // Successfully locked the output buffer + if (DSRes==DS_OK) + { + // If we have an active context, mix data directly into output buffer otherwise fill with silence + SuspendContext(NULL); + if(pDevice->Context) + { + if (WritePtr1) + aluMixData(pDevice->Context, WritePtr1, WriteCnt1, pDevice->Format); + if (WritePtr2) + aluMixData(pDevice->Context, WritePtr2, WriteCnt2, pDevice->Format); + } + else + { + if (WritePtr1) memset(WritePtr1, 0, WriteCnt1); + if (WritePtr2) memset(WritePtr2, 0, WriteCnt2); + } + ProcessContext(NULL); + + // Unlock output buffer only when successfully locked + IDirectSoundBuffer_Unlock(pData->DSsbuffer,WritePtr1,WriteCnt1,WritePtr2,WriteCnt2); + } + + // Update old write cursor location + OldWriteCursor=((OldWriteCursor+BytesPlayed)%BufSize); +} + + +static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + DSBUFFERDESC DSBDescription; + DSoundData *pData = NULL; + WAVEFORMATEX OutputType; + HRESULT hr; + + if(deviceName) + { + int i; + for(i = 0;DeviceList[i];i++) + { + if(strcmp(deviceName, DeviceList[i]) == 0) + break; + } + if(!DeviceList[i]) + return ALC_FALSE; + } + + //Platform specific + memset(&OutputType, 0, sizeof(WAVEFORMATEX)); + OutputType.wFormatTag = WAVE_FORMAT_PCM; + OutputType.nChannels = device->Channels; + OutputType.wBitsPerSample = (((device->Format==AL_FORMAT_MONO16)||(device->Format==AL_FORMAT_STEREO16)||(device->Format==AL_FORMAT_QUAD16))?16:8); + OutputType.nBlockAlign = OutputType.nChannels*OutputType.wBitsPerSample/8; + OutputType.nSamplesPerSec = device->Frequency; + OutputType.nAvgBytesPerSec = OutputType.nSamplesPerSec*OutputType.nBlockAlign; + OutputType.cbSize = 0; + + //Initialise requested device + + pData = calloc(1, sizeof(DSoundData)); + if(!pData) + { + SetALCError(ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + //Init COM + CoInitialize(NULL); + + //DirectSound Init code + hr = CoCreateInstance(&CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectSound, (LPVOID*)&pData->lpDS); + if(SUCCEEDED(hr)) + hr = IDirectSound_Initialize(pData->lpDS, NULL); + if(SUCCEEDED(hr)) + hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY); + + if(SUCCEEDED(hr)) + { + memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); + DSBDescription.dwSize=sizeof(DSBUFFERDESC); + DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER; + hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL); + } + if(SUCCEEDED(hr)) + hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType); + + if(SUCCEEDED(hr)) + { + memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); + DSBDescription.dwSize=sizeof(DSBUFFERDESC); + DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2; + DSBDescription.dwBufferBytes=device->UpdateFreq * device->FrameSize; + DSBDescription.lpwfxFormat=&OutputType; + hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL); + } + + if(SUCCEEDED(hr)) + hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING); + + if(FAILED(hr)) + { + if (pData->DSsbuffer) + IDirectSoundBuffer_Release(pData->DSsbuffer); + if (pData->DSpbuffer) + IDirectSoundBuffer_Release(pData->DSpbuffer); + if (pData->lpDS) + IDirectSound_Release(pData->lpDS); + + free(pData); + return ALC_FALSE; + } + + pData->ulDSTimerID = timeSetEvent(25, 0, (LPTIMECALLBACK)DirectSoundProc, (DWORD)device, (UINT)TIME_CALLBACK_FUNCTION|TIME_PERIODIC); + device->MaxNoOfSources = 256; + + if(deviceName) + strcpy(device->szDeviceName, deviceName); + else + strcpy(device->szDeviceName, DeviceList[0]); + + device->ExtraData = pData; + return ALC_TRUE; +} + +static void DSoundClosePlayback(ALCdevice *device) +{ + DSoundData *pData = device->ExtraData; + + // Stop and release the DS timer + if (pData->ulDSTimerID) + timeKillEvent(pData->ulDSTimerID); + + // Wait ... just in case any timer events happen + Sleep(100); + + SuspendContext(NULL); + + if (pData->DSsbuffer) + IDirectSoundBuffer_Release(pData->DSsbuffer); + if (pData->DSpbuffer) + IDirectSoundBuffer_Release(pData->DSpbuffer); + IDirectSound_Release(pData->lpDS); + + //Deinit COM + CoUninitialize(); + + ProcessContext(NULL); + + free(pData); + device->ExtraData = NULL; +} + + +static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + (void)pDevice; + (void)deviceName; + (void)frequency; + (void)format; + (void)SampleSize; + return ALC_FALSE; +} + +static void DSoundCloseCapture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void DSoundStartCapture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void DSoundStopCapture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + (void)pDevice; + (void)pBuffer; + (void)lSamples; +} + +static ALCuint DSoundAvailableSamples(ALCdevice *pDevice) +{ + (void)pDevice; + return 0; +} + + +BackendFuncs DSoundFuncs = { + DSoundOpenPlayback, + DSoundClosePlayback, + DSoundOpenCapture, + DSoundCloseCapture, + DSoundStartCapture, + DSoundStopCapture, + DSoundCaptureSamples, + DSoundAvailableSamples +}; + +void alcDSoundInit(BackendFuncs *FuncList) +{ + *FuncList = DSoundFuncs; + + DeviceList[0] = AppendDeviceList("DirectSound Software"); + AppendAllDeviceList(DeviceList[0]); +} diff --git a/Alc/oss.c b/Alc/oss.c new file mode 100644 index 00000000..5874a88c --- /dev/null +++ b/Alc/oss.c @@ -0,0 +1,328 @@ +/** + * 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 <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> +#include <unistd.h> +#include <errno.h> +#include <math.h> +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + +#include <sys/soundcard.h> + +/* + * The OSS documentation talks about SOUND_MIXER_READ, but the header + * only contains MIXER_READ. Play safe. Same for WRITE. + */ +#ifndef SOUND_MIXER_READ +#define SOUND_MIXER_READ MIXER_READ +#endif +#ifndef SOUND_MIXER_WRITE +#define SOUND_MIXER_WRITE MIXER_WRITE +#endif + +static char *oss_device; + +typedef struct { + int fd; + int killNow; + ALvoid *thread; + + void *mix_data; + int data_size; + int silence; +} oss_data; + + +static int log2i(ALCuint x) +{ + int y = 0; + while (x > 1) + { + x >>= 1; + y++; + } + return y; +} + + +static ALuint OSSProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + oss_data *data = (oss_data*)pDevice->ExtraData; + int remaining; + int wrote; + + while(!data->killNow) + { + SuspendContext(NULL); + if(pDevice->Context) + aluMixData(pDevice->Context,data->mix_data,data->data_size,pDevice->Format); + else + memset(data->mix_data,data->silence,data->data_size); + ProcessContext(NULL); + + remaining = data->data_size; + while(remaining > 0) + { + wrote = write(data->fd, data->mix_data+data->data_size-remaining, remaining); + if(wrote < 0) + { + fprintf(stderr, "write failed: %s\n", strerror(errno)); + break; + } + if(wrote == 0) + { + usleep(1000); + continue; + } + remaining -= wrote; + } + } + + return 0; +} + +static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + int numFragmentsLogSize; + int log2FragmentSize; + unsigned int periods; + audio_buf_info info; + char driver[64]; + int numChannels; + oss_data *data; + int ossFormat; + int ossSpeed; + int i; + + strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1); + driver[sizeof(driver)-1] = 0; + if(deviceName) + { + if(strcmp(deviceName, oss_device)) + return ALC_FALSE; + } + + if(deviceName) + strcpy(device->szDeviceName, deviceName); + else + strcpy(device->szDeviceName, oss_device); + + data = (oss_data*)calloc(1, sizeof(oss_data)); + data->killNow = 0; + + data->fd = open(driver, O_WRONLY); + if(data->fd == -1) + { + free(data); + return ALC_FALSE; + } + + switch(device->Format) + { + case AL_FORMAT_MONO8: + case AL_FORMAT_STEREO8: + case AL_FORMAT_QUAD8: + data->silence = 0x80; + ossFormat = AFMT_U8; + break; + case AL_FORMAT_MONO16: + case AL_FORMAT_STEREO16: + case AL_FORMAT_QUAD16: + data->silence = 0; + ossFormat = AFMT_S16_NE; + break; + default: + ossFormat = -1; + fprintf(stderr, "Unknown format?! %x\n", device->Format); + } + + periods = GetConfigValueInt("oss", "periods", 4); + if((int)periods < 0) + periods = 4; + numChannels = device->Channels; + ossSpeed = device->Frequency; + log2FragmentSize = log2i(device->UpdateFreq * device->FrameSize / periods); + + /* according to the OSS spec, 16 bytes are the minimum */ + if (log2FragmentSize < 4) + log2FragmentSize = 4; + numFragmentsLogSize = (periods << 16) | log2FragmentSize; + +#define ok(func, str) (i=(func),((i<0)?fprintf(stderr,"%s failed\n", str),0:1)) + if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize), "set fragment") && + ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") && + ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") && + ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") && + ok(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info), "get space"))) + { + close(data->fd); + free(data); + return ALC_FALSE; + } +#undef ok + + device->Channels = numChannels; + device->Frequency = ossSpeed; + device->Format = 0; + if(ossFormat == AFMT_U8) + { + if(device->Channels == 1) + { + device->Format = AL_FORMAT_MONO8; + device->FrameSize = 1; + } + else if(device->Channels == 2) + { + device->Format = AL_FORMAT_STEREO8; + device->FrameSize = 2; + } + else if(device->Channels == 4) + { + device->Format = AL_FORMAT_QUAD8; + device->FrameSize = 4; + } + } + else if(ossFormat == AFMT_S16_NE) + { + if(device->Channels == 1) + { + device->Format = AL_FORMAT_MONO16; + device->FrameSize = 2; + } + else if(device->Channels == 2) + { + device->Format = AL_FORMAT_STEREO16; + device->FrameSize = 4; + } + else if(device->Channels == 4) + { + device->Format = AL_FORMAT_QUAD16; + device->FrameSize = 8; + } + } + + if(!device->Format) + { + fprintf(stderr, "Unknown returned format/channel %#x/%d!\n", ossFormat, numChannels); + close(data->fd); + free(data); + return ALC_FALSE; + } + + device->UpdateFreq = info.fragsize / device->FrameSize; + + data->data_size = device->UpdateFreq * device->FrameSize; + data->mix_data = calloc(1, data->data_size); + + device->MaxNoOfSources = 256; + + device->ExtraData = data; + data->thread = StartThread(OSSProc, device); + if(data->thread == NULL) + { + device->ExtraData = NULL; + free(data->mix_data); + free(data); + return ALC_FALSE; + } + + return ALC_TRUE; +} + +static void oss_close_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + data->killNow = 1; + StopThread(data->thread); + + close(data->fd); + + free(data->mix_data); + free(data); + device->ExtraData = NULL; +} + + +static ALCboolean oss_open_capture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + (void)pDevice; + (void)deviceName; + (void)frequency; + (void)format; + (void)SampleSize; + return ALC_FALSE; +} + +static void oss_close_capture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void oss_start_capture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void oss_stop_capture(ALCdevice *pDevice) +{ + (void)pDevice; +} + +static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + (void)pDevice; + (void)pBuffer; + (void)lSamples; +} + +static ALCuint oss_available_samples(ALCdevice *pDevice) +{ + (void)pDevice; + return 0; +} + + +BackendFuncs oss_funcs = { + oss_open_playback, + oss_close_playback, + oss_open_capture, + oss_close_capture, + oss_start_capture, + oss_stop_capture, + oss_capture_samples, + oss_available_samples +}; + +void alc_oss_init(BackendFuncs *func_list) +{ + *func_list = oss_funcs; + + oss_device = AppendDeviceList("OSS Software"); + AppendAllDeviceList(oss_device); +} diff --git a/Alc/winmm.c b/Alc/winmm.c new file mode 100644 index 00000000..26f40db2 --- /dev/null +++ b/Alc/winmm.c @@ -0,0 +1,421 @@ +/** + * 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 <stdlib.h> +#include <stdio.h> +#include <memory.h> + +#include <windows.h> +#include <mmsystem.h> + +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" + + +typedef struct { + // MMSYSTEM Capture Device + ALboolean bWaveInShutdown; + HANDLE hWaveInHdrEvent; + HANDLE hWaveInThreadEvent; + HANDLE hWaveInThread; + DWORD ulWaveInThreadID; + ALint lWaveInBuffersCommitted; + HWAVEIN hWaveInHandle; + WAVEHDR WaveInBuffer[4]; + ALCchar *pCapturedSampleData; + ALuint ulCapturedDataSize; + ALuint ulReadCapturedDataPos; + ALuint ulWriteCapturedDataPos; +} WinMMData; + + +static ALCchar *CaptureDeviceList[16]; + +/* + WaveInProc + + Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and + returns to the application (with more data) +*/ +static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2) +{ + ALCdevice *pDevice = (ALCdevice *)dwInstance; + WinMMData *pData = pDevice->ExtraData; + + (void)hDevice; + (void)dwParam2; + + if ((uMsg==WIM_DATA) && (pDevice)) + { + // Decrement number of buffers in use + pData->lWaveInBuffersCommitted--; + + if (pData->bWaveInShutdown == AL_FALSE) + { + // Notify Wave Processor Thread that a Wave Header has returned + PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1); + } + else + { + if (pData->lWaveInBuffersCommitted == 0) + { + // Signal Wave Buffers Returned event + if (pData->hWaveInHdrEvent) + SetEvent(pData->hWaveInHdrEvent); + + // Post 'Quit' Message to WaveIn Processor Thread + PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0); + } + } + } +} + +/* + CaptureThreadProc + + Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new + audio data. +*/ +DWORD WINAPI CaptureThreadProc(LPVOID lpParameter) +{ + ALCdevice *pDevice = (ALCdevice*)lpParameter; + WinMMData *pData = pDevice->ExtraData; + ALuint ulOffset, ulMaxSize, ulSection; + LPWAVEHDR pWaveHdr; + MSG msg; + + while (GetMessage(&msg, NULL, 0, 0)) + { + if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown)) + { + SuspendContext(NULL); + + pWaveHdr = ((LPWAVEHDR)msg.lParam); + + // Calculate offset in local buffer to write data to + ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize; + + if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize) + { + ulSection = pData->ulCapturedDataSize - ulOffset; + memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection); + memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection); + } + else + { + memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded); + } + + pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded; + + if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize)) + { + // Application has not read enough audio data from the capture buffer so data has been + // overwritten. Reset ReadPosition. + pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize; + } + + // To prevent an over-flow prevent the offset values from getting too large + ulMaxSize = pData->ulCapturedDataSize << 4; + if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize)) + { + pData->ulReadCapturedDataPos -= ulMaxSize; + pData->ulWriteCapturedDataPos -= ulMaxSize; + } + + // Send buffer back to capture more data + waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR)); + pData->lWaveInBuffersCommitted++; + + ProcessContext(NULL); + } + } + + // Signal Wave Thread completed event + if (pData->hWaveInThreadEvent) + SetEvent(pData->hWaveInThreadEvent); + + ExitThread(0); + + return 0; +} + + +static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + (void)device; + (void)deviceName; + return ALC_FALSE; +} + +static void WinMMClosePlayback(ALCdevice *device) +{ + (void)device; +} + + +static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + WAVEFORMATEX wfexCaptureFormat; + WinMMData *pData = NULL; + ALint lDeviceID = 0; + ALint lBufferSize; + ALint i; + + (void)format; + + // Find the Device ID matching the deviceName if valid + if (deviceName) + { + for(i = 0;CaptureDeviceList[i];i++) + { + if (!strcmp(deviceName, CaptureDeviceList[i])) + { + lDeviceID = i; + break; + } + } + if(!CaptureDeviceList[i]) + return ALC_FALSE; + } + + pData = calloc(1, sizeof(*pData)); + if(!pData) + { + SetALCError(ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + + memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX)); + wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM; + wfexCaptureFormat.nChannels = pDevice->Channels; + wfexCaptureFormat.wBitsPerSample = pDevice->FrameSize / pDevice->Channels * 8; + wfexCaptureFormat.nBlockAlign = pDevice->FrameSize; + wfexCaptureFormat.nSamplesPerSec = frequency; + wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec * + pDevice->FrameSize; + wfexCaptureFormat.cbSize = 0; + + if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) + goto failure; + + pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned"); + if (pData->hWaveInHdrEvent == NULL) + goto failure; + + pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed"); + if (pData->hWaveInThreadEvent == NULL) + goto failure; + + pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID); + if (pData->hWaveInThread == NULL) + goto failure; + + // Allocate circular memory buffer for the captured audio + pData->ulCapturedDataSize = SampleSize * wfexCaptureFormat.nBlockAlign; + + // Make sure circular buffer is at least 100ms in size (and an exact multiple of + // the block alignment + if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10)) + { + pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10; + pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign); + } + + pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize); + pData->lWaveInBuffersCommitted=0; + + // Create 4 Buffers of 50ms each + lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20; + lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign); + + for (i=0;i<4;i++) + { + memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR)); + pData->WaveInBuffer[i].dwBufferLength = lBufferSize; + pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength); + pData->WaveInBuffer[i].dwFlags = 0; + pData->WaveInBuffer[i].dwLoops = 0; + waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR)); + waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR)); + pData->lWaveInBuffersCommitted++; + } + + pData->ulReadCapturedDataPos = 0; + pData->ulWriteCapturedDataPos = 0; + + strcpy(pDevice->szDeviceName, CaptureDeviceList[lDeviceID]); + + pDevice->ExtraData = pData; + return ALC_TRUE; + +failure: + if (pData->hWaveInThreadEvent) + CloseHandle(pData->hWaveInThreadEvent); + if (pData->hWaveInHdrEvent) + CloseHandle(pData->hWaveInHdrEvent); + if (pData->hWaveInHandle) + waveInClose(pData->hWaveInHandle); + + free(pData); + return ALC_FALSE; +} + +static void WinMMCloseCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + int i; + + // Call waveOutReset to shutdown wave device + pData->bWaveInShutdown = AL_TRUE; + waveInReset(pData->hWaveInHandle); + + // Wait for signal that all Wave Buffers have returned + WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE); + + // Wait for signal that Wave Thread has been destroyed + WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE); + + // Release the wave buffers + for (i=0;i<4;i++) + { + waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR)); + free(pData->WaveInBuffer[i].lpData); + } + + // Free Audio Buffer data + free(pData->pCapturedSampleData); + pData->pCapturedSampleData = NULL; + + // Close the Wave device + waveInClose(pData->hWaveInHandle); + pData->hWaveInHandle = 0; + + CloseHandle(pData->hWaveInThread); + pData->hWaveInThread = 0; + + if (pData->hWaveInHdrEvent) + { + CloseHandle(pData->hWaveInHdrEvent); + pData->hWaveInHdrEvent = 0; + } + + if (pData->hWaveInThreadEvent) + { + CloseHandle(pData->hWaveInThreadEvent); + pData->hWaveInThreadEvent = 0; + } + + free(pData); + pDevice->ExtraData = NULL; +} + +static void WinMMStartCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStart(pData->hWaveInHandle); +} + +static void WinMMStopCapture(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + waveInStop(pData->hWaveInHandle); +} + +static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + ALuint ulSamples = (unsigned long)lSamples; + ALuint ulBytes, ulBytesToCopy; + ALuint ulCapturedSamples; + ALuint ulReadOffset; + + // Check that we have the requested numbers of Samples + ulCapturedSamples = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos) / pDevice->FrameSize; + if(ulSamples > ulCapturedSamples) + { + SetALCError(ALC_INVALID_VALUE); + return; + } + + ulBytes = ulSamples * pDevice->FrameSize; + + // Get Read Offset + ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize); + + // Check for wrap-around condition + if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize) + { + // Copy data from last Read position to end of data + ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset; + memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy); + + // Copy rest of the data from the start of the captured data + memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy); + } + else + { + // Copy data from the read position in the captured data + memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes); + } + + // Update Read Position + pData->ulReadCapturedDataPos += ulBytes; +} + +static ALCuint WinMMAvailableSamples(ALCdevice *pDevice) +{ + WinMMData *pData = (WinMMData*)pDevice->ExtraData; + ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos); + return lCapturedBytes / pDevice->FrameSize; +} + + +BackendFuncs WinMMFuncs = { + WinMMOpenPlayback, + WinMMClosePlayback, + WinMMOpenCapture, + WinMMCloseCapture, + WinMMStartCapture, + WinMMStopCapture, + WinMMCaptureSamples, + WinMMAvailableSamples +}; + +void alcWinMMInit(BackendFuncs *FuncList) +{ + ALint lNumDevs; + ALint lLoop; + + *FuncList = WinMMFuncs; + + lNumDevs = waveInGetNumDevs(); + for (lLoop = 0; lLoop < lNumDevs; lLoop++) + { + WAVEINCAPS WaveInCaps; + if(waveInGetDevCaps(lLoop, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) + { + char name[128]; + snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname); + CaptureDeviceList[lLoop] = AppendCaptureDeviceList(name); + } + } +} |