aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
Diffstat (limited to 'Alc')
-rw-r--r--Alc/ALc.c1117
-rw-r--r--Alc/alcConfig.c283
-rw-r--r--Alc/alcThread.c125
-rw-r--r--Alc/alsa.c828
-rw-r--r--Alc/dsound.c294
-rw-r--r--Alc/oss.c328
-rw-r--r--Alc/winmm.c421
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);
+ }
+ }
+}