aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortma <tma@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-11-13 18:58:14 +0000
committertma <tma@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-11-13 18:58:14 +0000
commit38baaa2274a458f03f576dea5a6d3cd9e25f8063 (patch)
tree64500262c0756817893a96b4d6556e6a31d2ce8f
parentc37663e2d7c564e85af9d17afed0ee1dd283bfb5 (diff)
downloadioquake3-aero-38baaa2274a458f03f576dea5a6d3cd9e25f8063.tar.gz
ioquake3-aero-38baaa2274a458f03f576dea5a6d3cd9e25f8063.zip
* OpenAL support, from BlackAura aka Stuart Dalton <badcdev@gmail.com>
+ An abstract codec system, simplifying support for new formats + Changes versus BlackAura's patch: o Consolidated the OpenAL parts into one file o Changed the function naming scheme to more closely resemble Q3 o Changed the interface to fall back on the "base" sound system if loading OpenAL fails + This is enabled on Linux and MinGW for now, but should work on the other *nixs with appropriate additions to the Makefile + NOT enabled on OS X or MSVC Windows builds + Probably breaks the Windows build again * Retabulated sdl_snd.c and made the messages less verbose since there do not seem to be many having problems with SDL sound now git-svn-id: svn://svn.icculus.org/quake3/trunk@343 edf5b092-35ff-0310-97b2-ce42778d08ea
-rw-r--r--code/client/qal.c344
-rw-r--r--code/client/qal.h134
-rw-r--r--code/client/snd_codec.c226
-rw-r--r--code/client/snd_codec.h97
-rw-r--r--code/client/snd_codec_wav.c307
-rw-r--r--code/client/snd_dma.c531
-rw-r--r--code/client/snd_local.h51
-rw-r--r--code/client/snd_main.c433
-rw-r--r--code/client/snd_mem.c153
-rw-r--r--code/client/snd_openal.c1567
-rw-r--r--code/macosx/Quake3.pbproj/project.pbxproj161
-rw-r--r--code/unix/Makefile27
-rw-r--r--code/unix/sdl_snd.c428
-rw-r--r--i_o-q3-readme17
14 files changed, 3793 insertions, 683 deletions
diff --git a/code/client/qal.c b/code/client/qal.c
new file mode 100644
index 0000000..a01c915
--- /dev/null
+++ b/code/client/qal.c
@@ -0,0 +1,344 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// Dynamically loads OpenAL
+
+#if USE_OPENAL
+
+#include "qal.h"
+
+#if defined _WIN32
+#include <windows.h>
+#define OBJTYPE HMODULE
+#define OBJLOAD(x) LoadLibrary(x)
+#define SYMLOAD(x,y) GetProcAddress(x,y)
+#define OBJFREE(x) FreeLibrary(x)
+
+#elif defined __linux__ || defined __FreeBSD__
+#include <unistd.h>
+#include <sys/types.h>
+#include <dlfcn.h>
+#define OBJTYPE void *
+#define OBJLOAD(x) dlopen(x, RTLD_LAZY | RTLD_GLOBAL)
+#define SYMLOAD(x,y) dlsym(x,y)
+#define OBJFREE(x) dlclose(x)
+#else
+
+#error "Your platform has no lib loading code or it is disabled"
+#endif
+
+LPALENABLE qalEnable;
+LPALDISABLE qalDisable;
+LPALISENABLED qalIsEnabled;
+LPALGETSTRING qalGetString;
+LPALGETBOOLEANV qalGetBooleanv;
+LPALGETINTEGERV qalGetIntegerv;
+LPALGETFLOATV qalGetFloatv;
+LPALGETDOUBLEV qalGetDoublev;
+LPALGETBOOLEAN qalGetBoolean;
+LPALGETINTEGER qalGetInteger;
+LPALGETFLOAT qalGetFloat;
+LPALGETDOUBLE qalGetDouble;
+LPALGETERROR qalGetError;
+LPALISEXTENSIONPRESENT qalIsExtensionPresent;
+LPALGETPROCADDRESS qalGetProcAddress;
+LPALGETENUMVALUE qalGetEnumValue;
+LPALLISTENERF qalListenerf;
+LPALLISTENER3F qalListener3f;
+LPALLISTENERFV qalListenerfv;
+LPALLISTENERI qalListeneri;
+LPALGETLISTENERF qalGetListenerf;
+LPALGETLISTENER3F qalGetListener3f;
+LPALGETLISTENERFV qalGetListenerfv;
+LPALGETLISTENERI qalGetListeneri;
+LPALGENSOURCES qalGenSources;
+LPALDELETESOURCES qalDeleteSources;
+LPALISSOURCE qalIsSource;
+LPALSOURCEF qalSourcef;
+LPALSOURCE3F qalSource3f;
+LPALSOURCEFV qalSourcefv;
+LPALSOURCEI qalSourcei;
+LPALGETSOURCEF qalGetSourcef;
+LPALGETSOURCE3F qalGetSource3f;
+LPALGETSOURCEFV qalGetSourcefv;
+LPALGETSOURCEI qalGetSourcei;
+LPALSOURCEPLAYV qalSourcePlayv;
+LPALSOURCESTOPV qalSourceStopv;
+LPALSOURCEREWINDV qalSourceRewindv;
+LPALSOURCEPAUSEV qalSourcePausev;
+LPALSOURCEPLAY qalSourcePlay;
+LPALSOURCESTOP qalSourceStop;
+LPALSOURCEREWIND qalSourceRewind;
+LPALSOURCEPAUSE qalSourcePause;
+LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
+LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
+LPALGENBUFFERS qalGenBuffers;
+LPALDELETEBUFFERS qalDeleteBuffers;
+LPALISBUFFER qalIsBuffer;
+LPALBUFFERDATA qalBufferData;
+LPALGETBUFFERF qalGetBufferf;
+LPALGETBUFFERI qalGetBufferi;
+LPALDOPPLERFACTOR qalDopplerFactor;
+LPALDOPPLERVELOCITY qalDopplerVelocity;
+LPALDISTANCEMODEL qalDistanceModel;
+
+LPALCCREATECONTEXT qalcCreateContext;
+LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent;
+LPALCPROCESSCONTEXT qalcProcessContext;
+LPALCSUSPENDCONTEXT qalcSuspendContext;
+LPALCDESTROYCONTEXT qalcDestroyContext;
+LPALCGETCURRENTCONTEXT qalcGetCurrentContext;
+LPALCGETCONTEXTSDEVICE qalcGetContextsDevice;
+LPALCOPENDEVICE qalcOpenDevice;
+LPALCCLOSEDEVICE qalcCloseDevice;
+LPALCGETERROR qalcGetError;
+LPALCISEXTENSIONPRESENT qalcIsExtensionPresent;
+LPALCGETPROCADDRESS qalcGetProcAddress;
+LPALCGETENUMVALUE qalcGetEnumValue;
+LPALCGETSTRING qalcGetString;
+LPALCGETINTEGERV qalcGetIntegerv;
+
+static OBJTYPE OpenALLib = NULL;
+
+static qboolean alinit_fail = qfalse;
+
+/*
+=================
+GPA
+=================
+*/
+static void *GPA(char *str)
+{
+ void *rv;
+
+ rv = SYMLOAD(OpenALLib, str);
+ if(!rv)
+ {
+ Com_Printf( " Can't load symbol %s\n", str);
+ alinit_fail = qtrue;
+ return NULL;
+ }
+ else
+ {
+ Com_DPrintf( " Loaded symbol %s (0x%08X)\n", str, rv);
+ return rv;
+ }
+}
+
+/*
+=================
+QAL_Init
+=================
+*/
+qboolean QAL_Init(const char *libname)
+{
+ if(OpenALLib)
+ return qtrue;
+
+ Com_Printf( "Loading \"%s\"...\n", libname);
+ if( (OpenALLib = OBJLOAD(libname)) == 0 )
+ {
+#ifdef _WIN32
+ return qfalse;
+#else
+ char fn[1024];
+ getcwd(fn, sizeof(fn));
+ strncat(fn, "/", sizeof(fn));
+ strncat(fn, libname, sizeof(fn));
+
+ if( (OpenALLib = OBJLOAD(fn)) == 0 )
+ {
+ return qfalse;
+ }
+#endif
+ }
+
+ alinit_fail = qfalse;
+
+ qalEnable = GPA("alEnable");
+ qalDisable = GPA("alDisable");
+ qalIsEnabled = GPA("alIsEnabled");
+ qalGetString = GPA("alGetString");
+ qalGetBooleanv = GPA("alGetBooleanv");
+ qalGetIntegerv = GPA("alGetIntegerv");
+ qalGetFloatv = GPA("alGetFloatv");
+ qalGetDoublev = GPA("alGetDoublev");
+ qalGetBoolean = GPA("alGetBoolean");
+ qalGetInteger = GPA("alGetInteger");
+ qalGetFloat = GPA("alGetFloat");
+ qalGetDouble = GPA("alGetDouble");
+ qalGetError = GPA("alGetError");
+ qalIsExtensionPresent = GPA("alIsExtensionPresent");
+ qalGetProcAddress = GPA("alGetProcAddress");
+ qalGetEnumValue = GPA("alGetEnumValue");
+ qalListenerf = GPA("alListenerf");
+ qalListener3f = GPA("alListener3f");
+ qalListenerfv = GPA("alListenerfv");
+ qalListeneri = GPA("alListeneri");
+ qalGetListenerf = GPA("alGetListenerf");
+ qalGetListener3f = GPA("alGetListener3f");
+ qalGetListenerfv = GPA("alGetListenerfv");
+ qalGetListeneri = GPA("alGetListeneri");
+ qalGenSources = GPA("alGenSources");
+ qalDeleteSources = GPA("alDeleteSources");
+ qalIsSource = GPA("alIsSource");
+ qalSourcef = GPA("alSourcef");
+ qalSource3f = GPA("alSource3f");
+ qalSourcefv = GPA("alSourcefv");
+ qalSourcei = GPA("alSourcei");
+ qalGetSourcef = GPA("alGetSourcef");
+ qalGetSource3f = GPA("alGetSource3f");
+ qalGetSourcefv = GPA("alGetSourcefv");
+ qalGetSourcei = GPA("alGetSourcei");
+ qalSourcePlayv = GPA("alSourcePlayv");
+ qalSourceStopv = GPA("alSourceStopv");
+ qalSourceRewindv = GPA("alSourceRewindv");
+ qalSourcePausev = GPA("alSourcePausev");
+ qalSourcePlay = GPA("alSourcePlay");
+ qalSourceStop = GPA("alSourceStop");
+ qalSourceRewind = GPA("alSourceRewind");
+ qalSourcePause = GPA("alSourcePause");
+ qalSourceQueueBuffers = GPA("alSourceQueueBuffers");
+ qalSourceUnqueueBuffers = GPA("alSourceUnqueueBuffers");
+ qalGenBuffers = GPA("alGenBuffers");
+ qalDeleteBuffers = GPA("alDeleteBuffers");
+ qalIsBuffer = GPA("alIsBuffer");
+ qalBufferData = GPA("alBufferData");
+ qalGetBufferf = GPA("alGetBufferf");
+ qalGetBufferi = GPA("alGetBufferi");
+ qalDopplerFactor = GPA("alDopplerFactor");
+ qalDopplerVelocity = GPA("alDopplerVelocity");
+ qalDistanceModel = GPA("alDistanceModel");
+
+ qalcCreateContext = GPA("alcCreateContext");
+ qalcMakeContextCurrent = GPA("alcMakeContextCurrent");
+ qalcProcessContext = GPA("alcProcessContext");
+ qalcSuspendContext = GPA("alcSuspendContext");
+ qalcDestroyContext = GPA("alcDestroyContext");
+ qalcGetCurrentContext = GPA("alcGetCurrentContext");
+ qalcGetContextsDevice = GPA("alcGetContextsDevice");
+ qalcOpenDevice = GPA("alcOpenDevice");
+ qalcCloseDevice = GPA("alcCloseDevice");
+ qalcGetError = GPA("alcGetError");
+ qalcIsExtensionPresent = GPA("alcIsExtensionPresent");
+ qalcGetProcAddress = GPA("alcGetProcAddress");
+ qalcGetEnumValue = GPA("alcGetEnumValue");
+ qalcGetString = GPA("alcGetString");
+ qalcGetIntegerv = GPA("alcGetIntegerv");
+
+ if(alinit_fail)
+ {
+ QAL_Shutdown();
+ Com_Printf( " One or more symbols not found\n");
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+/*
+=================
+QAL_Shutdown
+=================
+*/
+void QAL_Shutdown( void )
+{
+ if(OpenALLib)
+ {
+ OBJFREE(OpenALLib);
+ OpenALLib = NULL;
+ }
+
+ qalEnable = NULL;
+ qalDisable = NULL;
+ qalIsEnabled = NULL;
+ qalGetString = NULL;
+ qalGetBooleanv = NULL;
+ qalGetIntegerv = NULL;
+ qalGetFloatv = NULL;
+ qalGetDoublev = NULL;
+ qalGetBoolean = NULL;
+ qalGetInteger = NULL;
+ qalGetFloat = NULL;
+ qalGetDouble = NULL;
+ qalGetError = NULL;
+ qalIsExtensionPresent = NULL;
+ qalGetProcAddress = NULL;
+ qalGetEnumValue = NULL;
+ qalListenerf = NULL;
+ qalListener3f = NULL;
+ qalListenerfv = NULL;
+ qalListeneri = NULL;
+ qalGetListenerf = NULL;
+ qalGetListener3f = NULL;
+ qalGetListenerfv = NULL;
+ qalGetListeneri = NULL;
+ qalGenSources = NULL;
+ qalDeleteSources = NULL;
+ qalIsSource = NULL;
+ qalSourcef = NULL;
+ qalSource3f = NULL;
+ qalSourcefv = NULL;
+ qalSourcei = NULL;
+ qalGetSourcef = NULL;
+ qalGetSource3f = NULL;
+ qalGetSourcefv = NULL;
+ qalGetSourcei = NULL;
+ qalSourcePlayv = NULL;
+ qalSourceStopv = NULL;
+ qalSourceRewindv = NULL;
+ qalSourcePausev = NULL;
+ qalSourcePlay = NULL;
+ qalSourceStop = NULL;
+ qalSourceRewind = NULL;
+ qalSourcePause = NULL;
+ qalSourceQueueBuffers = NULL;
+ qalSourceUnqueueBuffers = NULL;
+ qalGenBuffers = NULL;
+ qalDeleteBuffers = NULL;
+ qalIsBuffer = NULL;
+ qalBufferData = NULL;
+ qalGetBufferf = NULL;
+ qalGetBufferi = NULL;
+ qalDopplerFactor = NULL;
+ qalDopplerVelocity = NULL;
+ qalDistanceModel = NULL;
+
+ qalcCreateContext = NULL;
+ qalcMakeContextCurrent = NULL;
+ qalcProcessContext = NULL;
+ qalcSuspendContext = NULL;
+ qalcDestroyContext = NULL;
+ qalcGetCurrentContext = NULL;
+ qalcGetContextsDevice = NULL;
+ qalcOpenDevice = NULL;
+ qalcCloseDevice = NULL;
+ qalcGetError = NULL;
+ qalcIsExtensionPresent = NULL;
+ qalcGetProcAddress = NULL;
+ qalcGetEnumValue = NULL;
+ qalcGetString = NULL;
+ qalcGetIntegerv = NULL;
+}
+
+#endif
diff --git a/code/client/qal.h b/code/client/qal.h
new file mode 100644
index 0000000..b3592db
--- /dev/null
+++ b/code/client/qal.h
@@ -0,0 +1,134 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+
+#ifndef __QAL_H__
+#define __QAL_H__
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+#define AL_NO_PROTOTYPES
+#define ALC_NO_PROTOTYPES
+#include <AL/al.h>
+#include <AL/alc.h>
+
+extern LPALENABLE qalEnable;
+extern LPALDISABLE qalDisable;
+extern LPALISENABLED qalIsEnabled;
+extern LPALGETSTRING qalGetString;
+extern LPALGETBOOLEANV qalGetBooleanv;
+extern LPALGETINTEGERV qalGetIntegerv;
+extern LPALGETFLOATV qalGetFloatv;
+extern LPALGETDOUBLEV qalGetDoublev;
+extern LPALGETBOOLEAN qalGetBoolean;
+extern LPALGETINTEGER qalGetInteger;
+extern LPALGETFLOAT qalGetFloat;
+extern LPALGETDOUBLE qalGetDouble;
+extern LPALGETERROR qalGetError;
+extern LPALISEXTENSIONPRESENT qalIsExtensionPresent;
+extern LPALGETPROCADDRESS qalGetProcAddress;
+extern LPALGETENUMVALUE qalGetEnumValue;
+extern LPALLISTENERF qalListenerf;
+extern LPALLISTENER3F qalListener3f;
+extern LPALLISTENERFV qalListenerfv;
+extern LPALLISTENERI qalListeneri;
+extern LPALLISTENER3I qalListener3i;
+extern LPALLISTENERIV qalListeneriv;
+extern LPALGETLISTENERF qalGetListenerf;
+extern LPALGETLISTENER3F qalGetListener3f;
+extern LPALGETLISTENERFV qalGetListenerfv;
+extern LPALGETLISTENERI qalGetListeneri;
+extern LPALGETLISTENER3I qalGetListener3i;
+extern LPALGETLISTENERIV qalGetListeneriv;
+extern LPALGENSOURCES qalGenSources;
+extern LPALDELETESOURCES qalDeleteSources;
+extern LPALISSOURCE qalIsSource;
+extern LPALSOURCEF qalSourcef;
+extern LPALSOURCE3F qalSource3f;
+extern LPALSOURCEFV qalSourcefv;
+extern LPALSOURCEI qalSourcei;
+extern LPALSOURCE3I qalSource3i;
+extern LPALSOURCEIV qalSourceiv;
+extern LPALGETSOURCEF qalGetSourcef;
+extern LPALGETSOURCE3F qalGetSource3f;
+extern LPALGETSOURCEFV qalGetSourcefv;
+extern LPALGETSOURCEI qalGetSourcei;
+extern LPALGETSOURCE3I qalGetSource3i;
+extern LPALGETSOURCEIV qalGetSourceiv;
+extern LPALSOURCEPLAYV qalSourcePlayv;
+extern LPALSOURCESTOPV qalSourceStopv;
+extern LPALSOURCEREWINDV qalSourceRewindv;
+extern LPALSOURCEPAUSEV qalSourcePausev;
+extern LPALSOURCEPLAY qalSourcePlay;
+extern LPALSOURCESTOP qalSourceStop;
+extern LPALSOURCEREWIND qalSourceRewind;
+extern LPALSOURCEPAUSE qalSourcePause;
+extern LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
+extern LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
+extern LPALGENBUFFERS qalGenBuffers;
+extern LPALDELETEBUFFERS qalDeleteBuffers;
+extern LPALISBUFFER qalIsBuffer;
+extern LPALBUFFERDATA qalBufferData;
+extern LPALBUFFERF qalBufferf;
+extern LPALBUFFER3F qalBuffer3f;
+extern LPALBUFFERFV qalBufferfv;
+extern LPALBUFFERF qalBufferi;
+extern LPALBUFFER3F qalBuffer3i;
+extern LPALBUFFERFV qalBufferiv;
+extern LPALGETBUFFERF qalGetBufferf;
+extern LPALGETBUFFER3F qalGetBuffer3f;
+extern LPALGETBUFFERFV qalGetBufferfv;
+extern LPALGETBUFFERI qalGetBufferi;
+extern LPALGETBUFFER3I qalGetBuffer3i;
+extern LPALGETBUFFERIV qalGetBufferiv;
+extern LPALDOPPLERFACTOR qalDopplerFactor;
+extern LPALDOPPLERVELOCITY qalDopplerVelocity;
+extern LPALSPEEDOFSOUND qalSpeedOfSound;
+extern LPALDISTANCEMODEL qalDistanceModel;
+
+extern LPALCCREATECONTEXT qalcCreateContext;
+extern LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent;
+extern LPALCPROCESSCONTEXT qalcProcessContext;
+extern LPALCSUSPENDCONTEXT qalcSuspendContext;
+extern LPALCDESTROYCONTEXT qalcDestroyContext;
+extern LPALCGETCURRENTCONTEXT qalcGetCurrentContext;
+extern LPALCGETCONTEXTSDEVICE qalcGetContextsDevice;
+extern LPALCOPENDEVICE qalcOpenDevice;
+extern LPALCCLOSEDEVICE qalcCloseDevice;
+extern LPALCGETERROR qalcGetError;
+extern LPALCISEXTENSIONPRESENT qalcIsExtensionPresent;
+extern LPALCGETPROCADDRESS qalcGetProcAddress;
+extern LPALCGETENUMVALUE qalcGetEnumValue;
+extern LPALCGETSTRING qalcGetString;
+extern LPALCGETINTEGERV qalcGetIntegerv;
+extern LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice;
+extern LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice;
+extern LPALCCAPTURESTART qalcCaptureStart;
+extern LPALCCAPTURESTOP qalcCaptureStop;
+extern LPALCCAPTURESAMPLES qalcCaptureSamples;
+
+qboolean QAL_Init(const char *libname);
+void QAL_Shutdown( void );
+
+#endif // __QAL_H__
diff --git a/code/client/snd_codec.c b/code/client/snd_codec.c
new file mode 100644
index 0000000..de35255
--- /dev/null
+++ b/code/client/snd_codec.c
@@ -0,0 +1,226 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "client.h"
+#include "snd_codec.h"
+
+static snd_codec_t *codecs;
+
+/*
+=================
+S_FileExtension
+=================
+*/
+static char *S_FileExtension(const char *fni)
+{
+ char *fn = (char *)fni;
+ char *eptr = NULL;
+ while(*fn)
+ {
+ if(*fn == '.')
+ eptr = fn;
+ fn++;
+ }
+
+ return eptr;
+}
+
+/*
+=================
+S_FindCodecForFile
+
+Select an appropriate codec for a file based on its extension
+=================
+*/
+static snd_codec_t *S_FindCodecForFile(const char *filename)
+{
+ char *ext = S_FileExtension(filename);
+ snd_codec_t *codec = codecs;
+
+ if(!ext)
+ {
+ // No extension - auto-detect
+ while(codec)
+ {
+ char fn[MAX_QPATH];
+ Q_strncpyz(fn, filename, sizeof(fn) - 4);
+ COM_DefaultExtension(fn, sizeof(fn), codec->ext);
+
+ // Check it exists
+ if(FS_ReadFile(fn, NULL) != -1)
+ return codec;
+
+ // Nope. Next!
+ codec = codec->next;
+ }
+
+ // Nothin'
+ return NULL;
+ }
+
+ while(codec)
+ {
+ if(!Q_stricmp(ext, codec->ext))
+ return codec;
+ codec = codec->next;
+ }
+
+ return NULL;
+}
+
+/*
+=================
+S_CodecInit
+=================
+*/
+void S_CodecInit()
+{
+ codecs = NULL;
+ S_CodecRegister(&wav_codec);
+#ifdef USE_CODEC_VORBIS
+ S_CodecRegister(&ogg_codec);
+#endif
+}
+
+/*
+=================
+S_CodecShutdown
+=================
+*/
+void S_CodecShutdown()
+{
+ codecs = NULL;
+}
+
+/*
+=================
+S_CodecRegister
+=================
+*/
+void S_CodecRegister(snd_codec_t *codec)
+{
+ codec->next = codecs;
+ codecs = codec;
+}
+
+/*
+=================
+S_CodecLoad
+=================
+*/
+void *S_CodecLoad(const char *filename, snd_info_t *info)
+{
+ snd_codec_t *codec;
+ char fn[MAX_QPATH];
+
+ codec = S_FindCodecForFile(filename);
+ if(!codec)
+ {
+ Com_Printf("Unknown extension for %s\n", filename);
+ return NULL;
+ }
+
+ strncpy(fn, filename, sizeof(fn));
+ COM_DefaultExtension(fn, sizeof(fn), codec->ext);
+
+ return codec->load(fn, info);
+}
+
+/*
+=================
+S_CodecOpenStream
+=================
+*/
+snd_stream_t *S_CodecOpenStream(const char *filename)
+{
+ snd_codec_t *codec;
+ char fn[MAX_QPATH];
+
+ codec = S_FindCodecForFile(filename);
+ if(!codec)
+ {
+ Com_Printf("Unknown extension for %s\n", filename);
+ return NULL;
+ }
+
+ strncpy(fn, filename, sizeof(fn));
+ COM_DefaultExtension(fn, sizeof(fn), codec->ext);
+
+ return codec->open(fn);
+}
+
+void S_CodecCloseStream(snd_stream_t *stream)
+{
+ stream->codec->close(stream);
+}
+
+int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
+{
+ return stream->codec->read(stream, bytes, buffer);
+}
+
+//=======================================================================
+// Util functions (used by codecs)
+
+/*
+=================
+S_CodecUtilOpen
+=================
+*/
+snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec)
+{
+ snd_stream_t *stream;
+ fileHandle_t hnd;
+
+ // Try to open the file
+ FS_FOpenFileRead(filename, &hnd, qtrue);
+ if(!hnd)
+ {
+ Com_Printf("Can't read sound file %s\n", filename);
+ return NULL;
+ }
+
+ // Allocate a stream
+ stream = Z_Malloc(sizeof(snd_stream_t));
+ if(!stream)
+ {
+ FS_FCloseFile(hnd);
+ return NULL;
+ }
+
+ // Copy over, return
+ stream->codec = codec;
+ stream->file = hnd;
+ return stream;
+}
+
+/*
+=================
+S_CodecUtilClose
+=================
+*/
+void S_CodecUtilClose(snd_stream_t *stream)
+{
+ FS_FCloseFile(stream->file);
+ Z_Free(stream);
+}
diff --git a/code/client/snd_codec.h b/code/client/snd_codec.h
new file mode 100644
index 0000000..75f35b3
--- /dev/null
+++ b/code/client/snd_codec.h
@@ -0,0 +1,97 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#ifndef _SND_CODEC_H_
+#define _SND_CODEC_H_
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+typedef struct snd_info_s
+{
+ int rate;
+ int width;
+ int channels;
+ int samples;
+ int size;
+ int dataofs;
+} snd_info_t;
+
+typedef struct snd_codec_s snd_codec_t;
+
+typedef struct snd_stream_s
+{
+ snd_codec_t *codec;
+ fileHandle_t file;
+ snd_info_t info;
+ int pos;
+ void *ptr;
+} snd_stream_t;
+
+// Codec functions
+typedef void *(*CODEC_LOAD)(const char *filename, snd_info_t *info);
+typedef snd_stream_t *(*CODEC_OPEN)(const char *filename);
+typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer);
+typedef void (*CODEC_CLOSE)(snd_stream_t *stream);
+
+// Codec data structure
+struct snd_codec_s
+{
+ char *ext;
+ CODEC_LOAD load;
+ CODEC_OPEN open;
+ CODEC_READ read;
+ CODEC_CLOSE close;
+ snd_codec_t *next;
+};
+
+// Codec management
+void S_CodecInit( void );
+void S_CodecShutdown( void );
+void S_CodecRegister(snd_codec_t *codec);
+void *S_CodecLoad(const char *filename, snd_info_t *info);
+snd_stream_t *S_CodecOpenStream(const char *filename);
+void S_CodecCloseStream(snd_stream_t *stream);
+int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
+
+// Util functions (used by codecs)
+snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec);
+void S_CodecUtilClose(snd_stream_t *stream);
+
+// WAV Codec
+extern snd_codec_t wav_codec;
+void *S_WAV_CodecLoad(const char *filename, snd_info_t *info);
+snd_stream_t *S_WAV_CodecOpenStream(const char *filename);
+void S_WAV_CodecCloseStream(snd_stream_t *stream);
+int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
+
+// Ogg Vorbis codec
+#ifdef USE_CODEC_VORBIS
+extern snd_codec_t ogg_codec;
+void *S_OGG_CodecLoad(const char *filename, snd_info_t *info);
+snd_stream_t *S_OGG_CodecOpenStream(const char *filename);
+void S_OGG_CodecCloseStream(snd_stream_t *stream);
+int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
+#endif // USE_CODEC_VORBIS
+
+#endif // !_SND_CODEC_H_
diff --git a/code/client/snd_codec_wav.c b/code/client/snd_codec_wav.c
new file mode 100644
index 0000000..bc9c00c
--- /dev/null
+++ b/code/client/snd_codec_wav.c
@@ -0,0 +1,307 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "client.h"
+#include "snd_codec.h"
+
+/*
+=================
+FGetLittleLong
+=================
+*/
+static int FGetLittleLong( fileHandle_t f ) {
+ int v;
+
+ FS_Read( &v, sizeof(v), f );
+
+ return LittleLong( v);
+}
+
+/*
+=================
+FGetLittleShort
+=================
+*/
+static int FGetLittleShort( fileHandle_t f ) {
+ short v;
+
+ FS_Read( &v, sizeof(v), f );
+
+ return LittleShort( v);
+}
+
+/*
+=================
+S_ReadChunkInfo
+=================
+*/
+static int S_ReadChunkInfo(fileHandle_t f, char *name)
+{
+ int len, r;
+
+ name[4] = 0;
+
+ r = FS_Read(name, 4, f);
+ if(r != 4)
+ return 0;
+
+ len = FGetLittleLong(f);
+ if(len < 0 || len > 0xffffffff)
+ return 0;
+
+ //FIXME: 11/11/05 <tim@ngus.net>
+ // I'm not sure I understand why this needs to be padded.
+ // Surely this results in reading past the end of the data?
+ //len = (len + 1 ) & ~1; // pad to word boundary
+
+ return len;
+}
+
+/*
+=================
+S_SkipChunk
+=================
+*/
+static void S_SkipChunk(fileHandle_t f, int length)
+{
+ byte buffer[32*1024];
+
+ while(length > 0)
+ {
+ int toread = length;
+ if(toread > sizeof(buffer))
+ toread = sizeof(buffer);
+ FS_Read(buffer, toread, f);
+ length -= toread;
+ }
+}
+
+/*
+=================
+S_FindWavChunk
+
+Returns the length of the data in the chunk, or 0 if not found
+=================
+*/
+static int S_FindWavChunk( fileHandle_t f, char *chunk ) {
+ char name[5];
+ int len;
+
+ // This is a bit dangerous...
+ while(1)
+ {
+ len = S_ReadChunkInfo(f, name);
+
+ // Read failure?
+ if(len == 0)
+ return 0;
+
+ // If this is the right chunk, return
+ if(!strcmp(name, chunk))
+ return len;
+
+ // Not the right chunk - skip it
+ S_SkipChunk(f, len);
+ }
+}
+
+/*
+=================
+S_ByteSwapRawSamples
+=================
+*/
+static void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
+ int i;
+
+ if ( width != 2 ) {
+ return;
+ }
+ if ( LittleShort( 256 ) == 256 ) {
+ return;
+ }
+
+ if ( s_channels == 2 ) {
+ samples <<= 1;
+ }
+ for ( i = 0 ; i < samples ; i++ ) {
+ ((short *)data)[i] = LittleShort( ((short *)data)[i] );
+ }
+}
+
+/*
+=================
+S_ReadWavHeader
+=================
+*/
+static qboolean S_ReadWavHeader(fileHandle_t file, snd_info_t *info)
+{
+ char dump[16];
+ int wav_format;
+ int fmtlen = 0;
+
+ // skip the riff wav header
+ FS_Read(dump, 12, file);
+
+ // Scan for the format chunk
+ if((fmtlen = S_FindWavChunk(file, "fmt ")) == 0)
+ {
+ Com_Printf("No fmt chunk\n");
+ return qfalse;
+ }
+
+ // Save the parameters
+ wav_format = FGetLittleShort(file);
+ info->channels = FGetLittleShort(file);
+ info->rate = FGetLittleLong(file);
+ FGetLittleLong(file);
+ FGetLittleShort(file);
+ info->width = FGetLittleShort(file) / 8;
+ info->dataofs = 0;
+
+ // Skip the rest of the format chunk if required
+ if(fmtlen > 16)
+ {
+ fmtlen -= 16;
+ S_SkipChunk(file, fmtlen);
+ }
+
+ // Scan for the data chunk
+ if( (info->size = S_FindWavChunk(file, "data")) == 0)
+ {
+ Com_Printf("No data chunk\n");
+ return qfalse;
+ }
+ info->samples = (info->size / info->width) / info->channels;
+
+ return qtrue;
+}
+
+// WAV codec
+snd_codec_t wav_codec =
+{
+ ".wav",
+ S_WAV_CodecLoad,
+ S_WAV_CodecOpenStream,
+ S_WAV_CodecReadStream,
+ S_WAV_CodecCloseStream,
+ NULL
+};
+
+/*
+=================
+S_WAV_CodecLoad
+=================
+*/
+void *S_WAV_CodecLoad(const char *filename, snd_info_t *info)
+{
+ fileHandle_t file;
+ void *buffer;
+
+ // Try to open the file
+ FS_FOpenFileRead(filename, &file, qtrue);
+ if(!file)
+ {
+ Com_Printf("Can't read sound file %s\n", filename);
+ return NULL;
+ }
+
+ // Read the RIFF header
+ if(!S_ReadWavHeader(file, info))
+ {
+ FS_FCloseFile(file);
+ Com_Printf("Can't understand wav file %s\n", filename);
+ return NULL;
+ }
+
+ // Allocate some memory
+ buffer = Z_Malloc(info->size);
+ if(!buffer)
+ {
+ FS_FCloseFile(file);
+ Com_Printf("Out of memory reading %s\n", filename);
+ return NULL;
+ }
+
+ // Read, byteswap
+ FS_Read(buffer, info->size, file);
+ S_ByteSwapRawSamples(info->samples, info->width, info->channels, (byte *)buffer);
+
+ // Close and return
+ FS_FCloseFile(file);
+ return buffer;
+}
+
+/*
+=================
+S_WAV_CodecOpenStream
+=================
+*/
+snd_stream_t *S_WAV_CodecOpenStream(const char *filename)
+{
+ snd_stream_t *rv;
+
+ // Open
+ rv = S_CodecUtilOpen(filename, &wav_codec);
+ if(!rv)
+ return NULL;
+
+ // Read the RIFF header
+ if(!S_ReadWavHeader(rv->file, &rv->info))
+ {
+ S_CodecUtilClose(rv);
+ return NULL;
+ }
+
+ return rv;
+}
+
+/*
+=================
+S_WAV_CodecCloseStream
+=================
+*/
+void S_WAV_CodecCloseStream(snd_stream_t *stream)
+{
+ S_CodecUtilClose(stream);
+}
+
+/*
+=================
+S_WAV_CodecReadStream
+=================
+*/
+int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
+{
+ int remaining = stream->info.size - stream->pos;
+ int samples;
+
+ if(remaining <= 0)
+ return 0;
+ if(bytes > remaining)
+ bytes = remaining;
+ stream->pos += bytes;
+ samples = (bytes / stream->info.width) / stream->info.channels;
+ FS_Read(buffer, bytes, stream->file);
+ S_ByteSwapRawSamples(samples, stream->info.width, stream->info.channels, buffer);
+ return bytes;
+}
diff --git a/code/client/snd_dma.c b/code/client/snd_dma.c
index e807d90..3977703 100644
--- a/code/client/snd_dma.c
+++ b/code/client/snd_dma.c
@@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
#include "snd_local.h"
+#include "snd_codec.h"
#include "client.h"
void S_Play_f(void);
@@ -37,13 +38,11 @@ void S_SoundList_f(void);
void S_Music_f(void);
void S_Update_( void );
-void S_StopAllSounds(void);
void S_UpdateBackgroundTrack( void );
+void S_Base_StopAllSounds(void);
+void S_Base_StopBackgroundTrack( void );
-static fileHandle_t s_backgroundFile;
-static wavinfo_t s_backgroundInfo;
-//int s_nextWavChunk;
-static int s_backgroundSamples;
+snd_stream_t *s_backgroundStream = NULL;
static char s_backgroundLoop[MAX_QPATH];
//static char s_backgroundMusic[MAX_QPATH]; //TTimo: unused
@@ -82,15 +81,11 @@ int s_numSfx = 0;
#define LOOP_HASH 128
static sfx_t *sfxHash[LOOP_HASH];
-cvar_t *s_volume;
cvar_t *s_testsound;
cvar_t *s_khz;
cvar_t *s_show;
cvar_t *s_mixahead;
cvar_t *s_mixPreStep;
-cvar_t *s_musicVolume;
-cvar_t *s_separation;
-cvar_t *s_doppler;
static loopSound_t loopSounds[MAX_GENTITIES];
static channel_t *freelist = NULL;
@@ -104,7 +99,7 @@ portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
// ====================================================================
-void S_SoundInfo_f(void) {
+void S_Base_SoundInfo(void) {
Com_Printf("----- Sound Info -----\n" );
if (!s_soundStarted) {
Com_Printf ("sound system not started\n");
@@ -115,7 +110,7 @@ void S_SoundInfo_f(void) {
Com_Printf("%5d submission_chunk\n", dma.submission_chunk);
Com_Printf("%5d speed\n", dma.speed);
Com_Printf("0x%x dma buffer\n", dma.buffer);
- if ( s_backgroundFile ) {
+ if ( s_backgroundStream ) {
Com_Printf("Background file: %s\n", s_backgroundLoop );
} else {
Com_Printf("No background file.\n" );
@@ -125,64 +120,37 @@ void S_SoundInfo_f(void) {
Com_Printf("----------------------\n" );
}
-
-
/*
-================
-S_Init
-================
+=================
+S_Base_SoundList
+=================
*/
-void S_Init( void ) {
- cvar_t *cv;
- qboolean r;
-
- Com_Printf("\n------- sound initialization -------\n");
-
- s_volume = Cvar_Get ("s_volume", "0.8", CVAR_ARCHIVE);
- s_musicVolume = Cvar_Get ("s_musicvolume", "0.25", CVAR_ARCHIVE);
- s_separation = Cvar_Get ("s_separation", "0.5", CVAR_ARCHIVE);
- s_doppler = Cvar_Get ("s_doppler", "1", CVAR_ARCHIVE);
- s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE);
- s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE);
-
- s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE);
- s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT);
- s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT);
-
- cv = Cvar_Get ("s_initsound", "1", 0);
- if ( !cv->integer ) {
- Com_Printf ("not initializing.\n");
- Com_Printf("------------------------------------\n");
- return;
- }
-
- Cmd_AddCommand("play", S_Play_f);
- Cmd_AddCommand("music", S_Music_f);
- Cmd_AddCommand("s_list", S_SoundList_f);
- Cmd_AddCommand("s_info", S_SoundInfo_f);
- Cmd_AddCommand("s_stop", S_StopAllSounds);
-
- r = SNDDMA_Init();
- Com_Printf("------------------------------------\n");
-
- if ( r ) {
- s_soundStarted = 1;
- s_soundMuted = 1;
-// s_numSfx = 0;
-
- Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
-
- s_soundtime = 0;
- s_paintedtime = 0;
-
- S_StopAllSounds ();
+void S_Base_SoundList( void ) {
+ int i;
+ sfx_t *sfx;
+ int size, total;
+ char type[4][16];
+ char mem[2][16];
- S_SoundInfo_f();
+ strcpy(type[0], "16bit");
+ strcpy(type[1], "adpcm");
+ strcpy(type[2], "daub4");
+ strcpy(type[3], "mulaw");
+ strcpy(mem[0], "paged out");
+ strcpy(mem[1], "resident ");
+ total = 0;
+ for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++) {
+ size = sfx->soundLength;
+ total += size;
+ Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod],
+ sfx->soundName, mem[sfx->inMemory] );
}
-
+ Com_Printf ("Total resident: %i\n", total);
+ S_DisplayFreeMemory();
}
+
void S_ChannelFree(channel_t *v) {
v->thesfx = NULL;
*(channel_t **)v = freelist;
@@ -217,25 +185,6 @@ void S_ChannelSetup( void ) {
Com_DPrintf("Channel memory manager started\n");
}
-// =======================================================================
-// Shutdown sound engine
-// =======================================================================
-
-void S_Shutdown( void ) {
- if ( !s_soundStarted ) {
- return;
- }
-
- SNDDMA_Shutdown();
-
- s_soundStarted = 0;
-
- Cmd_RemoveCommand("play");
- Cmd_RemoveCommand("music");
- Cmd_RemoveCommand("stopsound");
- Cmd_RemoveCommand("soundlist");
- Cmd_RemoveCommand("soundinfo");
-}
// =======================================================================
@@ -352,40 +301,19 @@ This is called when the hunk is cleared and the sounds
are no longer valid.
===================
*/
-void S_DisableSounds( void ) {
- S_StopAllSounds();
+void S_Base_DisableSounds( void ) {
+ S_Base_StopAllSounds();
s_soundMuted = qtrue;
}
/*
-=====================
-S_BeginRegistration
-
-=====================
-*/
-void S_BeginRegistration( void ) {
- s_soundMuted = qfalse; // we can play again
-
- if (s_numSfx == 0) {
- SND_setup();
-
- s_numSfx = 0;
- Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) );
- Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
-
- S_RegisterSound("sound/feedback/hit.wav", qfalse); // changed to a sound in baseq3
- }
-}
-
-
-/*
==================
S_RegisterSound
Creates a default buzz sound if the file can't be loaded
==================
*/
-sfxHandle_t S_RegisterSound( const char *name, qboolean compressed ) {
+sfxHandle_t S_Base_RegisterSound( const char *name, qboolean compressed ) {
sfx_t *sfx;
compressed = qfalse;
@@ -420,6 +348,26 @@ sfxHandle_t S_RegisterSound( const char *name, qboolean compressed ) {
return sfx - s_knownSfx;
}
+/*
+=====================
+S_BeginRegistration
+
+=====================
+*/
+void S_Base_BeginRegistration( void ) {
+ s_soundMuted = qfalse; // we can play again
+
+ if (s_numSfx == 0) {
+ SND_setup();
+
+ s_numSfx = 0;
+ Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) );
+ Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
+
+ S_Base_RegisterSound("sound/feedback/hit.wav", qfalse); // changed to a sound in baseq3
+ }
+}
+
void S_memoryLoad(sfx_t *sfx) {
// load the sound file
if ( !S_LoadSound ( sfx ) ) {
@@ -470,8 +418,6 @@ void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *righ
{
rscale = 0.5 * (1.0 + dot);
lscale = 0.5 * (1.0 - dot);
- //rscale = s_separation->value + ( 1.0 - s_separation->value ) * dot;
- //lscale = s_separation->value - ( 1.0 - s_separation->value ) * dot;
if ( rscale < 0 ) {
rscale = 0;
}
@@ -505,7 +451,7 @@ if pos is NULL, the sound will be dynamically sourced from the entity
Entchannel 0 will never override a playing sound
====================
*/
-void S_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) {
+void S_Base_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) {
channel_t *ch;
sfx_t *sfx;
int i, oldest, chosen, time;
@@ -626,7 +572,7 @@ void S_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxH
S_StartLocalSound
==================
*/
-void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) {
+void S_Base_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) {
if ( !s_soundStarted || s_soundMuted ) {
return;
}
@@ -636,7 +582,7 @@ void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) {
return;
}
- S_StartSound (NULL, listener_number, channelNum, sfxHandle );
+ S_Base_StartSound (NULL, listener_number, channelNum, sfxHandle );
}
@@ -648,7 +594,7 @@ If we are about to perform file access, clear the buffer
so sound doesn't stutter.
==================
*/
-void S_ClearSoundBuffer( void ) {
+void S_Base_ClearSoundBuffer( void ) {
int clear;
if (!s_soundStarted)
@@ -683,15 +629,15 @@ void S_ClearSoundBuffer( void ) {
S_StopAllSounds
==================
*/
-void S_StopAllSounds(void) {
+void S_Base_StopAllSounds(void) {
if ( !s_soundStarted ) {
return;
}
// stop the background music
- S_StopBackgroundTrack();
+ S_Base_StopBackgroundTrack();
- S_ClearSoundBuffer ();
+ S_Base_ClearSoundBuffer ();
}
/*
@@ -702,7 +648,7 @@ continuous looping sounds are added each frame
==============================================================
*/
-void S_StopLoopingSound(int entityNum) {
+void S_Base_StopLoopingSound(int entityNum) {
loopSounds[entityNum].active = qfalse;
// loopSounds[entityNum].sfx = 0;
loopSounds[entityNum].kill = qfalse;
@@ -714,12 +660,12 @@ S_ClearLoopingSounds
==================
*/
-void S_ClearLoopingSounds( qboolean killall ) {
+void S_Base_ClearLoopingSounds( qboolean killall ) {
int i;
for ( i = 0 ; i < MAX_GENTITIES ; i++) {
if (killall || loopSounds[i].kill == qtrue || (loopSounds[i].sfx && loopSounds[i].sfx->soundLength == 0)) {
loopSounds[i].kill = qfalse;
- S_StopLoopingSound(i);
+ S_Base_StopLoopingSound(i);
}
}
numLoopChannels = 0;
@@ -733,7 +679,7 @@ Called during entity generation for a frame
Include velocity in case I get around to doing doppler...
==================
*/
-void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
+void S_Base_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
sfx_t *sfx;
if ( !s_soundStarted || s_soundMuted ) {
@@ -796,7 +742,7 @@ Called during entity generation for a frame
Include velocity in case I get around to doing doppler...
==================
*/
-void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
+void S_Base_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
sfx_t *sfx;
if ( !s_soundStarted || s_soundMuted ) {
@@ -947,7 +893,7 @@ S_RawSamples
Music streaming
============
*/
-void S_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
+void S_Base_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
int i;
int src, dst;
float scale;
@@ -1051,7 +997,7 @@ S_UpdateEntityPosition
let the sound system know where an entity currently is
======================
*/
-void S_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
+void S_Base_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
if ( entityNum < 0 || entityNum > MAX_GENTITIES ) {
Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
}
@@ -1066,7 +1012,7 @@ S_Respatialize
Change the volumes of all the playing sounds for changes in their positions
============
*/
-void S_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) {
+void S_Base_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) {
int i;
channel_t *ch;
vec3_t origin;
@@ -1151,7 +1097,7 @@ S_Update
Called once each time through the main loop
============
*/
-void S_Update( void ) {
+void S_Base_Update( void ) {
int i;
int total;
channel_t *ch;
@@ -1204,7 +1150,7 @@ void S_GetSoundtime(void)
{ // time to chop things off to avoid 32 bit limits
buffers = 0;
s_paintedtime = fullsamples;
- S_StopAllSounds ();
+ S_Base_StopAllSounds ();
}
}
oldsamplepos = samplepos;
@@ -1289,73 +1235,6 @@ void S_Update_(void) {
lastTime = thisTime;
}
-/*
-===============================================================================
-
-console functions
-
-===============================================================================
-*/
-
-void S_Play_f( void ) {
- int i;
- sfxHandle_t h;
- char name[256];
-
- i = 1;
- while ( i<Cmd_Argc() ) {
- if ( !Q_strrchr(Cmd_Argv(i), '.') ) {
- Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
- } else {
- Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
- }
- h = S_RegisterSound( name, qfalse );
- if( h ) {
- S_StartLocalSound( h, CHAN_LOCAL_SOUND );
- }
- i++;
- }
-}
-
-void S_Music_f( void ) {
- int c;
-
- c = Cmd_Argc();
-
- if ( c == 2 ) {
- S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(1) );
- s_backgroundLoop[0] = 0;
- } else if ( c == 3 ) {
- S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) );
- } else {
- Com_Printf ("music <musicfile> [loopfile]\n");
- return;
- }
-
-}
-
-void S_SoundList_f( void ) {
- int i;
- sfx_t *sfx;
- int size, total;
- char type[4][16];
- char mem[2][16];
-
- strcpy(type[0], "16bit");
- strcpy(type[1], "adpcm");
- strcpy(type[2], "daub4");
- strcpy(type[3], "mulaw");
- strcpy(mem[0], "paged out");
- strcpy(mem[1], "resident ");
- total = 0;
- for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++) {
- size = sfx->soundLength;
- total += size;
- Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], sfx->soundName, mem[sfx->inMemory] );
- }
- Com_Printf ("Total resident: %i\n", total);
- S_DisplayFreeMemory();
-}
/*
@@ -1366,61 +1245,16 @@ background music functions
===============================================================================
*/
-int FGetLittleLong( fileHandle_t f ) {
- int v;
-
- FS_Read( &v, sizeof(v), f );
-
- return LittleLong( v);
-}
-
-int FGetLittleShort( fileHandle_t f ) {
- short v;
-
- FS_Read( &v, sizeof(v), f );
-
- return LittleShort( v);
-}
-
-// returns the length of the data in the chunk, or 0 if not found
-int S_FindWavChunk( fileHandle_t f, char *chunk ) {
- char name[5];
- int len;
- int r;
-
- name[4] = 0;
- len = 0;
- r = FS_Read( name, 4, f );
- if ( r != 4 ) {
- return 0;
- }
- len = FGetLittleLong( f );
- if ( len < 0 || len > 0xfffffff ) {
- len = 0;
- return 0;
- }
- len = (len + 1 ) & ~1; // pad to word boundary
-// s_nextWavChunk += len + 8;
-
- if ( strcmp( name, chunk ) ) {
- return 0;
- }
-
- return len;
-}
-
/*
======================
S_StopBackgroundTrack
======================
*/
-void S_StopBackgroundTrack( void ) {
- if ( !s_backgroundFile ) {
+void S_Base_StopBackgroundTrack( void ) {
+ if(!s_backgroundStream)
return;
- }
- Sys_EndStreamedFile( s_backgroundFile );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
+ S_CodecCloseStream(s_backgroundStream);
+ s_backgroundStream = NULL;
s_rawend = 0;
}
@@ -1429,11 +1263,7 @@ void S_StopBackgroundTrack( void ) {
S_StartBackgroundTrack
======================
*/
-void S_StartBackgroundTrack( const char *intro, const char *loop ){
- int len;
- char dump[16];
- char name[MAX_QPATH];
-
+void S_Base_StartBackgroundTrack( const char *intro, const char *loop ){
if ( !intro ) {
intro = "";
}
@@ -1442,77 +1272,34 @@ void S_StartBackgroundTrack( const char *intro, const char *loop ){
}
Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop );
- Q_strncpyz( name, intro, sizeof( name ) - 4 );
- COM_DefaultExtension( name, sizeof( name ), ".wav" );
-
if ( !intro[0] ) {
return;
}
- Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
+ if( !loop ) {
+ s_backgroundLoop[0] = 0;
+ } else {
+ Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
+ }
// close the background track, but DON'T reset s_rawend
// if restarting the same back ground track
- if ( s_backgroundFile ) {
- Sys_EndStreamedFile( s_backgroundFile );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- }
-
- //
- // open up a wav file and get all the info
- //
- FS_FOpenFileRead( name, &s_backgroundFile, qtrue );
- if ( !s_backgroundFile ) {
- Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", name );
- return;
+ if(s_backgroundStream)
+ {
+ S_CodecCloseStream(s_backgroundStream);
+ s_backgroundStream = NULL;
}
- // skip the riff wav header
-
- FS_Read(dump, 12, s_backgroundFile);
-
- if ( !S_FindWavChunk( s_backgroundFile, "fmt " ) ) {
- Com_Printf( "No fmt chunk in %s\n", name );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
+ // Open stream
+ s_backgroundStream = S_CodecOpenStream(intro);
+ if(!s_backgroundStream) {
+ Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", intro );
return;
}
- // save name for soundinfo
- s_backgroundInfo.format = FGetLittleShort( s_backgroundFile );
- s_backgroundInfo.channels = FGetLittleShort( s_backgroundFile );
- s_backgroundInfo.rate = FGetLittleLong( s_backgroundFile );
- FGetLittleLong( s_backgroundFile );
- FGetLittleShort( s_backgroundFile );
- s_backgroundInfo.width = FGetLittleShort( s_backgroundFile ) / 8;
-
- if ( s_backgroundInfo.format != WAV_FORMAT_PCM ) {
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- Com_Printf("Not a microsoft PCM format wav: %s\n", name);
- return;
+ if(s_backgroundStream->info.channels != 2 || s_backgroundStream->info.rate != 22050) {
+ Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", intro );
}
-
- if ( s_backgroundInfo.channels != 2 || s_backgroundInfo.rate != 22050 ) {
- Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", name );
- }
-
- if ( ( len = S_FindWavChunk( s_backgroundFile, "data" ) ) == 0 ) {
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- Com_Printf("No data chunk in %s\n", name);
- return;
- }
-
- s_backgroundInfo.samples = len / (s_backgroundInfo.width * s_backgroundInfo.channels);
-
- s_backgroundSamples = s_backgroundInfo.samples;
-
- //
- // start the background streaming
- //
- Sys_BeginStreamedFile( s_backgroundFile, 0x10000 );
}
/*
@@ -1528,7 +1315,7 @@ void S_UpdateBackgroundTrack( void ) {
int r;
static float musicVolume = 0.5f;
- if ( !s_backgroundFile ) {
+ if(!s_backgroundStream) {
return;
}
@@ -1549,50 +1336,47 @@ void S_UpdateBackgroundTrack( void ) {
bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime);
// decide how much data needs to be read from the file
- fileSamples = bufferSamples * s_backgroundInfo.rate / dma.speed;
-
- // don't try and read past the end of the file
- if ( fileSamples > s_backgroundSamples ) {
- fileSamples = s_backgroundSamples;
- }
+ fileSamples = bufferSamples * s_backgroundStream->info.rate / dma.speed;
// our max buffer size
- fileBytes = fileSamples * (s_backgroundInfo.width * s_backgroundInfo.channels);
+ fileBytes = fileSamples * (s_backgroundStream->info.width * s_backgroundStream->info.channels);
if ( fileBytes > sizeof(raw) ) {
fileBytes = sizeof(raw);
- fileSamples = fileBytes / (s_backgroundInfo.width * s_backgroundInfo.channels);
+ fileSamples = fileBytes / (s_backgroundStream->info.width * s_backgroundStream->info.channels);
}
- r = Sys_StreamedRead( raw, 1, fileBytes, s_backgroundFile );
- if ( r != fileBytes ) {
- Com_Printf("StreamedRead failure on music track\n");
- S_StopBackgroundTrack();
- return;
+ // Read
+ r = S_CodecReadStream(s_backgroundStream, fileBytes, raw);
+ if(r < fileBytes)
+ {
+ fileBytes = r;
+ fileSamples = r / (s_backgroundStream->info.width * s_backgroundStream->info.channels);
}
- // byte swap if needed
- S_ByteSwapRawSamples( fileSamples, s_backgroundInfo.width, s_backgroundInfo.channels, raw );
-
- // add to raw buffer
- S_RawSamples( fileSamples, s_backgroundInfo.rate,
- s_backgroundInfo.width, s_backgroundInfo.channels, raw, musicVolume );
-
- s_backgroundSamples -= fileSamples;
- if ( !s_backgroundSamples ) {
+ if(r > 0)
+ {
+ // add to raw buffer
+ S_Base_RawSamples( fileSamples, s_backgroundStream->info.rate,
+ s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, musicVolume );
+ }
+ else
+ {
// loop
- if (s_backgroundLoop[0]) {
- Sys_EndStreamedFile( s_backgroundFile );
- FS_FCloseFile( s_backgroundFile );
- s_backgroundFile = 0;
- S_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop );
- if ( !s_backgroundFile ) {
- return; // loop failed to restart
- }
- } else {
- s_backgroundFile = 0;
+ if(s_backgroundLoop[0])
+ {
+ S_CodecCloseStream(s_backgroundStream);
+ s_backgroundStream = NULL;
+ S_Base_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop );
+ if(!s_backgroundStream)
+ return;
+ }
+ else
+ {
+ S_Base_StopBackgroundTrack();
return;
}
}
+
}
}
@@ -1632,3 +1416,78 @@ void S_FreeOldestSound( void ) {
sfx->inMemory = qfalse;
sfx->soundData = NULL;
}
+
+// =======================================================================
+// Shutdown sound engine
+// =======================================================================
+
+void S_Base_Shutdown( void ) {
+ if ( !s_soundStarted ) {
+ return;
+ }
+
+ SNDDMA_Shutdown();
+
+ s_soundStarted = 0;
+
+ Cmd_RemoveCommand("s_info");
+}
+
+/*
+================
+S_Init
+================
+*/
+qboolean S_Base_Init( soundInterface_t *si ) {
+ qboolean r;
+
+ if( !si ) {
+ return qfalse;
+ }
+
+ s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE);
+ s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE);
+ s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE);
+ s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT);
+ s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT);
+
+ r = SNDDMA_Init();
+
+ if ( r ) {
+ s_soundStarted = 1;
+ s_soundMuted = 1;
+// s_numSfx = 0;
+
+ Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
+
+ s_soundtime = 0;
+ s_paintedtime = 0;
+
+ S_Base_StopAllSounds( );
+ } else {
+ return qfalse;
+ }
+
+ si->Shutdown = S_Base_Shutdown;
+ si->StartSound = S_Base_StartSound;
+ si->StartLocalSound = S_Base_StartLocalSound;
+ si->StartBackgroundTrack = S_Base_StartBackgroundTrack;
+ si->StopBackgroundTrack = S_Base_StopBackgroundTrack;
+ si->RawSamples = S_Base_RawSamples;
+ si->StopAllSounds = S_Base_StopAllSounds;
+ si->ClearLoopingSounds = S_Base_ClearLoopingSounds;
+ si->AddLoopingSound = S_Base_AddLoopingSound;
+ si->AddRealLoopingSound = S_Base_AddRealLoopingSound;
+ si->StopLoopingSound = S_Base_StopLoopingSound;
+ si->Respatialize = S_Base_Respatialize;
+ si->UpdateEntityPosition = S_Base_UpdateEntityPosition;
+ si->Update = S_Base_Update;
+ si->DisableSounds = S_Base_DisableSounds;
+ si->BeginRegistration = S_Base_BeginRegistration;
+ si->RegisterSound = S_Base_RegisterSound;
+ si->ClearSoundBuffer = S_Base_ClearSoundBuffer;
+ si->SoundInfo = S_Base_SoundInfo;
+ si->SoundList = S_Base_SoundList;
+
+ return qtrue;
+}
diff --git a/code/client/snd_local.h b/code/client/snd_local.h
index 49fcbac..3b45a99 100644
--- a/code/client/snd_local.h
+++ b/code/client/snd_local.h
@@ -117,6 +117,31 @@ typedef struct {
int dataofs; // chunk starts this many bytes from file start
} wavinfo_t;
+// Interface between Q3 sound "api" and the sound backend
+typedef struct
+{
+ void (*Shutdown)(void);
+ void (*StartSound)( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx );
+ void (*StartLocalSound)( sfxHandle_t sfx, int channelNum );
+ void (*StartBackgroundTrack)( const char *intro, const char *loop );
+ void (*StopBackgroundTrack)( void );
+ void (*RawSamples)(int samples, int rate, int width, int channels, const byte *data, float volume);
+ void (*StopAllSounds)( void );
+ void (*ClearLoopingSounds)( qboolean killall );
+ void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
+ void (*AddRealLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
+ void (*StopLoopingSound)(int entityNum );
+ void (*Respatialize)( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
+ void (*UpdateEntityPosition)( int entityNum, const vec3_t origin );
+ void (*Update)( void );
+ void (*DisableSounds)( void );
+ void (*BeginRegistration)( void );
+ sfxHandle_t (*RegisterSound)( const char *sample, qboolean compressed );
+ void (*ClearSoundBuffer)( void );
+ void (*SoundInfo)( void );
+ void (*SoundList)( void );
+} soundInterface_t;
+
/*
====================================================================
@@ -157,14 +182,11 @@ extern dma_t dma;
#define MAX_RAW_SAMPLES 16384
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
-extern cvar_t *s_volume;
-extern cvar_t *s_nosound;
-extern cvar_t *s_khz;
-extern cvar_t *s_show;
-extern cvar_t *s_mixahead;
+extern cvar_t *s_volume;
+extern cvar_t *s_musicVolume;
+extern cvar_t *s_doppler;
-extern cvar_t *s_testsound;
-extern cvar_t *s_separation;
+extern cvar_t *s_testsound;
qboolean S_LoadSound( sfx_t *sfx );
@@ -204,3 +226,18 @@ extern short *sfxScratchBuffer;
extern sfx_t *sfxScratchPointer;
extern int sfxScratchIndex;
+qboolean S_Base_Init( soundInterface_t *si );
+
+// OpenAL stuff
+typedef enum
+{
+ SRCPRI_AMBIENT = 0, // Ambient sound effects
+ SRCPRI_ENTITY, // Entity sound effects
+ SRCPRI_ONESHOT, // One-shot sounds
+ SRCPRI_LOCAL, // Local sounds
+ SRCPRI_STREAM // Streams (music, cutscenes)
+} alSrcPriority_t;
+
+typedef int srcHandle_t;
+
+qboolean S_AL_Init( soundInterface_t *si );
diff --git a/code/client/snd_main.c b/code/client/snd_main.c
new file mode 100644
index 0000000..cbb87f5
--- /dev/null
+++ b/code/client/snd_main.c
@@ -0,0 +1,433 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "client.h"
+#include "snd_codec.h"
+#include "snd_local.h"
+#include "snd_public.h"
+
+cvar_t *s_volume;
+cvar_t *s_musicVolume;
+cvar_t *s_doppler;
+
+static soundInterface_t si;
+
+/*
+=================
+S_ValidateInterface
+=================
+*/
+static qboolean S_ValidSoundInterface( soundInterface_t *si )
+{
+ if( !si->Shutdown ) return qfalse;
+ if( !si->StartSound ) return qfalse;
+ if( !si->StartLocalSound ) return qfalse;
+ if( !si->StartBackgroundTrack ) return qfalse;
+ if( !si->StopBackgroundTrack ) return qfalse;
+ if( !si->RawSamples ) return qfalse;
+ if( !si->StopAllSounds ) return qfalse;
+ if( !si->ClearLoopingSounds ) return qfalse;
+ if( !si->AddLoopingSound ) return qfalse;
+ if( !si->AddRealLoopingSound ) return qfalse;
+ if( !si->StopLoopingSound ) return qfalse;
+ if( !si->Respatialize ) return qfalse;
+ if( !si->UpdateEntityPosition ) return qfalse;
+ if( !si->Update ) return qfalse;
+ if( !si->DisableSounds ) return qfalse;
+ if( !si->BeginRegistration ) return qfalse;
+ if( !si->RegisterSound ) return qfalse;
+ if( !si->ClearSoundBuffer ) return qfalse;
+ if( !si->SoundInfo ) return qfalse;
+ if( !si->SoundList ) return qfalse;
+
+ return qtrue;
+}
+
+/*
+=================
+S_StartSound
+=================
+*/
+void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx )
+{
+ if( si.StartSound ) {
+ si.StartSound( origin, entnum, entchannel, sfx );
+ }
+}
+
+/*
+=================
+S_StartLocalSound
+=================
+*/
+void S_StartLocalSound( sfxHandle_t sfx, int channelNum )
+{
+ if( si.StartLocalSound ) {
+ si.StartLocalSound( sfx, channelNum );
+ }
+}
+
+/*
+=================
+S_StartBackgroundTrack
+=================
+*/
+void S_StartBackgroundTrack( const char *intro, const char *loop )
+{
+ if( si.StartBackgroundTrack ) {
+ si.StartBackgroundTrack( intro, loop );
+ }
+}
+
+/*
+=================
+S_StopBackgroundTrack
+=================
+*/
+void S_StopBackgroundTrack( void )
+{
+ if( si.StopBackgroundTrack ) {
+ si.StopBackgroundTrack( );
+ }
+}
+
+/*
+=================
+S_RawSamples
+=================
+*/
+void S_RawSamples (int samples, int rate, int width, int channels,
+ const byte *data, float volume)
+{
+ if( si.RawSamples ) {
+ si.RawSamples( samples, rate, width, channels, data, volume );
+ }
+}
+
+/*
+=================
+S_StopAllSounds
+=================
+*/
+void S_StopAllSounds( void )
+{
+ if( si.StopAllSounds ) {
+ si.StopAllSounds( );
+ }
+}
+
+/*
+=================
+S_ClearLoopingSounds
+=================
+*/
+void S_ClearLoopingSounds( qboolean killall )
+{
+ if( si.ClearLoopingSounds ) {
+ si.ClearLoopingSounds( killall );
+ }
+}
+
+/*
+=================
+S_AddLoopingSound
+=================
+*/
+void S_AddLoopingSound( int entityNum, const vec3_t origin,
+ const vec3_t velocity, sfxHandle_t sfx )
+{
+ if( si.AddLoopingSound ) {
+ si.AddLoopingSound( entityNum, origin, velocity, sfx );
+ }
+}
+
+/*
+=================
+S_AddRealLoopingSound
+=================
+*/
+void S_AddRealLoopingSound( int entityNum, const vec3_t origin,
+ const vec3_t velocity, sfxHandle_t sfx )
+{
+ if( si.AddRealLoopingSound ) {
+ si.AddRealLoopingSound( entityNum, origin, velocity, sfx );
+ }
+}
+
+/*
+=================
+S_StopLoopingSound
+=================
+*/
+void S_StopLoopingSound( int entityNum )
+{
+ if( si.StopLoopingSound ) {
+ si.StopLoopingSound( entityNum );
+ }
+}
+
+/*
+=================
+S_Respatialize
+=================
+*/
+void S_Respatialize( int entityNum, const vec3_t origin,
+ vec3_t axis[3], int inwater )
+{
+ if( si.Respatialize ) {
+ si.Respatialize( entityNum, origin, axis, inwater );
+ }
+}
+
+/*
+=================
+S_UpdateEntityPosition
+=================
+*/
+void S_UpdateEntityPosition( int entityNum, const vec3_t origin )
+{
+ if( si.UpdateEntityPosition ) {
+ si.UpdateEntityPosition( entityNum, origin );
+ }
+}
+
+/*
+=================
+S_Update
+=================
+*/
+void S_Update( void )
+{
+ if( si.Update ) {
+ si.Update( );
+ }
+}
+
+/*
+=================
+S_DisableSounds
+=================
+*/
+void S_DisableSounds( void )
+{
+ if( si.DisableSounds ) {
+ si.DisableSounds( );
+ }
+}
+
+/*
+=================
+S_BeginRegistration
+=================
+*/
+void S_BeginRegistration( void )
+{
+ if( si.BeginRegistration ) {
+ si.BeginRegistration( );
+ }
+}
+
+/*
+=================
+S_RegisterSound
+=================
+*/
+sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed )
+{
+ if( si.RegisterSound ) {
+ return si.RegisterSound( sample, compressed );
+ } else {
+ return 0;
+ }
+}
+
+/*
+=================
+S_ClearSoundBuffer
+=================
+*/
+void S_ClearSoundBuffer( void )
+{
+ if( si.ClearSoundBuffer ) {
+ si.ClearSoundBuffer( );
+ }
+}
+
+/*
+=================
+S_SoundInfo
+=================
+*/
+void S_SoundInfo( void )
+{
+ if( si.SoundInfo ) {
+ si.SoundInfo( );
+ }
+}
+
+/*
+=================
+S_SoundList
+=================
+*/
+void S_SoundList( void )
+{
+ if( si.SoundList ) {
+ si.SoundList( );
+ }
+}
+
+//=============================================================================
+
+/*
+=================
+S_Play_f
+=================
+*/
+void S_Play_f( void ) {
+ int i;
+ sfxHandle_t h;
+ char name[256];
+
+ if( !si.RegisterSound || !si.StartLocalSound ) {
+ return;
+ }
+
+ i = 1;
+ while ( i<Cmd_Argc() ) {
+ if ( !Q_strrchr(Cmd_Argv(i), '.') ) {
+ Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
+ } else {
+ Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
+ }
+ h = si.RegisterSound( name, qfalse );
+ if( h ) {
+ si.StartLocalSound( h, CHAN_LOCAL_SOUND );
+ }
+ i++;
+ }
+}
+
+/*
+=================
+S_Music_f
+=================
+*/
+void S_Music_f( void ) {
+ int c;
+
+ if( !si.StartBackgroundTrack ) {
+ return;
+ }
+
+ c = Cmd_Argc();
+
+ if ( c == 2 ) {
+ si.StartBackgroundTrack( Cmd_Argv(1), NULL );
+ } else if ( c == 3 ) {
+ si.StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) );
+ } else {
+ Com_Printf ("music <musicfile> [loopfile]\n");
+ return;
+ }
+
+}
+
+//=============================================================================
+
+/*
+=================
+S_Init
+=================
+*/
+void S_Init( void )
+{
+ cvar_t *cv;
+ qboolean started = qfalse;
+
+ Com_Printf( "------ Initializing Sound ------\n" );
+
+ s_volume = Cvar_Get( "s_volume", "0.8", CVAR_ARCHIVE );
+ s_musicVolume = Cvar_Get( "s_musicvolume", "0.25", CVAR_ARCHIVE );
+ s_doppler = Cvar_Get( "s_doppler", "1", CVAR_ARCHIVE );
+
+ cv = Cvar_Get( "s_initsound", "1", 0 );
+ if( !cv->integer ) {
+ Com_Printf( "Sound disabled.\n" );
+ } else {
+
+ S_CodecInit( );
+
+ Cmd_AddCommand( "play", S_Play_f );
+ Cmd_AddCommand( "music", S_Music_f );
+ Cmd_AddCommand( "s_list", S_SoundList );
+ Cmd_AddCommand( "s_stop", S_StopAllSounds );
+ Cmd_AddCommand( "s_info", S_SoundInfo );
+
+ cv = Cvar_Get( "s_useOpenAL", "1", CVAR_ARCHIVE );
+ if( cv->integer ) {
+ //OpenAL
+ started = S_AL_Init( &si );
+ }
+
+ if( !started ) {
+ started = S_Base_Init( &si );
+ }
+
+ if( started ) {
+ if( !S_ValidSoundInterface( &si ) ) {
+ Com_Error( ERR_FATAL, "Sound interface invalid." );
+ }
+
+ S_SoundInfo( );
+ Com_Printf( "Sound intialization successful.\n" );
+ } else {
+ Com_Printf( "Sound intialization failed.\n" );
+ }
+ }
+
+ Com_Printf( "--------------------------------\n");
+}
+
+/*
+=================
+S_Shutdown
+=================
+*/
+void S_Shutdown( void )
+{
+ if( si.Shutdown ) {
+ si.Shutdown( );
+ }
+
+ Com_Memset( &si, 0, sizeof( soundInterface_t ) );
+
+ Cmd_RemoveCommand( "play" );
+ Cmd_RemoveCommand( "music");
+ Cmd_RemoveCommand( "s_list" );
+ Cmd_RemoveCommand( "s_stop" );
+ Cmd_RemoveCommand( "s_info" );
+
+ S_CodecShutdown( );
+}
+
diff --git a/code/client/snd_mem.c b/code/client/snd_mem.c
index 8f994ba..77b370d 100644
--- a/code/client/snd_mem.c
+++ b/code/client/snd_mem.c
@@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
#include "snd_local.h"
+#include "snd_codec.h"
#define DEF_COMSOUNDMEGS "8"
@@ -100,137 +101,6 @@ void SND_setup(void) {
}
/*
-===============================================================================
-
-WAV loading
-
-===============================================================================
-*/
-
-static byte *data_p;
-static byte *iff_end;
-static byte *last_chunk;
-static byte *iff_data;
-static int iff_chunk_len;
-
-static short GetLittleShort(void)
-{
- short val = 0;
- val = *data_p;
- val = val + (*(data_p+1)<<8);
- data_p += 2;
- return val;
-}
-
-static int GetLittleLong(void)
-{
- int val = 0;
- val = *data_p;
- val = val + (*(data_p+1)<<8);
- val = val + (*(data_p+2)<<16);
- val = val + (*(data_p+3)<<24);
- data_p += 4;
- return val;
-}
-
-static void FindNextChunk(char *name)
-{
- while (1)
- {
- data_p=last_chunk;
-
- if (data_p >= iff_end)
- { // didn't find the chunk
- data_p = NULL;
- return;
- }
-
- data_p += 4;
- iff_chunk_len = GetLittleLong();
- if (iff_chunk_len < 0)
- {
- data_p = NULL;
- return;
- }
- data_p -= 8;
- last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
- if (!strncmp((char *)data_p, name, 4))
- return;
- }
-}
-
-static void FindChunk(char *name)
-{
- last_chunk = iff_data;
- FindNextChunk (name);
-}
-
-/*
-============
-GetWavinfo
-============
-*/
-static wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
-{
- wavinfo_t info;
-
- Com_Memset (&info, 0, sizeof(info));
-
- if (!wav)
- return info;
-
- iff_data = wav;
- iff_end = wav + wavlength;
-
-// find "RIFF" chunk
- FindChunk("RIFF");
- if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
- {
- Com_Printf("Missing RIFF/WAVE chunks\n");
- return info;
- }
-
-// get "fmt " chunk
- iff_data = data_p + 12;
-// DumpChunks ();
-
- FindChunk("fmt ");
- if (!data_p)
- {
- Com_Printf("Missing fmt chunk\n");
- return info;
- }
- data_p += 8;
- info.format = GetLittleShort();
- info.channels = GetLittleShort();
- info.rate = GetLittleLong();
- data_p += 4+2;
- info.width = GetLittleShort() / 8;
-
- if (info.format != 1)
- {
- Com_Printf("Microsoft PCM format only\n");
- return info;
- }
-
-
-// find data chunk
- FindChunk("data");
- if (!data_p)
- {
- Com_Printf("Missing data chunk\n");
- return info;
- }
-
- data_p += 4;
- info.samples = GetLittleLong () / info.width;
- info.dataofs = data_p - wav;
-
- return info;
-}
-
-
-/*
================
ResampleSfx
@@ -315,7 +185,6 @@ static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byt
return outcount;
}
-
//=============================================================================
/*
@@ -330,8 +199,8 @@ qboolean S_LoadSound( sfx_t *sfx )
{
byte *data;
short *samples;
- wavinfo_t info;
- int size;
+ snd_info_t info;
+// int size;
// player specific sounds are never directly loaded
if ( sfx->soundName[0] == '*') {
@@ -339,17 +208,9 @@ qboolean S_LoadSound( sfx_t *sfx )
}
// load it in
- size = FS_ReadFile( sfx->soundName, (void **)&data );
- if ( !data ) {
- return qfalse;
- }
-
- info = GetWavinfo( sfx->soundName, data, size );
- if ( info.channels != 1 ) {
- Com_Printf ("%s is a stereo wav file\n", sfx->soundName);
- FS_FreeFile (data);
+ data = S_CodecLoad(sfx->soundName, &info);
+ if(!data)
return qfalse;
- }
if ( info.width == 1 ) {
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
@@ -372,7 +233,7 @@ qboolean S_LoadSound( sfx_t *sfx )
if( sfx->soundCompressed == qtrue) {
sfx->soundCompressionMethod = 1;
sfx->soundData = NULL;
- sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
+ sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, data + info.dataofs );
S_AdpcmEncodeSound(sfx, samples);
#if 0
} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
@@ -394,7 +255,7 @@ qboolean S_LoadSound( sfx_t *sfx )
}
Hunk_FreeTempMemory(samples);
- FS_FreeFile( data );
+ Z_Free(data);
return qtrue;
}
diff --git a/code/client/snd_openal.c b/code/client/snd_openal.c
new file mode 100644
index 0000000..16a8bf9
--- /dev/null
+++ b/code/client/snd_openal.c
@@ -0,0 +1,1567 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "snd_local.h"
+#include "snd_codec.h"
+#include "client.h"
+
+#if USE_OPENAL
+
+#include "qal.h"
+
+// Console variables specific to OpenAL
+cvar_t *s_alPrecache;
+cvar_t *s_alGain;
+cvar_t *s_alSources;
+cvar_t *s_alDopplerFactor;
+cvar_t *s_alDopplerSpeed;
+cvar_t *s_alMinDistance;
+cvar_t *s_alRolloff;
+cvar_t *s_alDriver;
+
+/*
+=================
+S_AL_Format
+=================
+*/
+ALuint S_AL_Format(int width, int channels)
+{
+ ALuint format = AL_FORMAT_MONO16;
+
+ // Work out format
+ if(width == 1)
+ {
+ if(channels == 1)
+ format = AL_FORMAT_MONO8;
+ else if(channels == 2)
+ format = AL_FORMAT_STEREO8;
+ }
+ else if(width == 2)
+ {
+ if(channels == 1)
+ format = AL_FORMAT_MONO16;
+ else if(channels == 2)
+ format = AL_FORMAT_STEREO16;
+ }
+
+ return format;
+}
+
+/*
+=================
+S_AL_ErrorMsg
+=================
+*/
+char *S_AL_ErrorMsg(ALenum error)
+{
+ switch(error)
+ {
+ case AL_NO_ERROR:
+ return "No error";
+ case AL_INVALID_NAME:
+ return "Invalid name";
+ case AL_INVALID_ENUM:
+ return "Invalid enumerator";
+ case AL_INVALID_VALUE:
+ return "Invalid value";
+ case AL_INVALID_OPERATION:
+ return "Invalid operation";
+ case AL_OUT_OF_MEMORY:
+ return "Out of memory";
+ default:
+ return "Unknown error";
+ }
+}
+
+
+//===========================================================================
+
+
+typedef struct alSfx_s
+{
+ char filename[MAX_QPATH];
+ ALuint buffer; // OpenAL buffer
+ qboolean isDefault; // Couldn't be loaded - use default FX
+ qboolean inMemory; // Sound is stored in memory
+ qboolean isLocked; // Sound is locked (can not be unloaded)
+ int used; // Time last used
+ struct alSfx_t *next; // Next entry in hash list
+} alSfx_t;
+
+static qboolean alBuffersInitialised = qfalse;
+
+// Sound effect storage, data structures
+#define MAX_SFX 4096
+static alSfx_t knownSfx[MAX_SFX];
+static int numSfx;
+
+static sfxHandle_t default_sfx;
+
+/*
+=================
+S_AL_BufferFindFree
+
+Find a free handle
+=================
+*/
+static sfxHandle_t S_AL_BufferFindFree( void )
+{
+ int i;
+
+ for(i = 0; i < MAX_SFX; i++)
+ {
+ // Got one
+ if(knownSfx[i].filename[0] == '\0')
+ return i;
+ }
+
+ // Shit...
+ Com_Error(ERR_FATAL, "S_AL_BufferFindFree: No free sound handles");
+ return -1;
+}
+
+/*
+=================
+S_AL_BufferFind
+
+Find a sound effect if loaded, set up a handle otherwise
+=================
+*/
+static sfxHandle_t S_AL_BufferFind(const char *filename)
+{
+ // Look it up in the hash table
+ sfxHandle_t sfx = -1;
+ int i;
+
+ for(i = 0; i < MAX_SFX; i++)
+ {
+ if(!Q_stricmp(knownSfx[i].filename, filename))
+ {
+ sfx = i;
+ break;
+ }
+ }
+
+ // Not found in hash table?
+ if(sfx == -1)
+ {
+ alSfx_t *ptr;
+
+ sfx = S_AL_BufferFindFree();
+
+ // Clear and copy the filename over
+ ptr = &knownSfx[sfx];
+ memset(ptr, 0, sizeof(*ptr));
+ strcpy(ptr->filename, filename);
+ }
+
+ // Return the handle
+ return sfx;
+}
+
+/*
+=================
+S_AL_BufferUseDefault
+=================
+*/
+static void S_AL_BufferUseDefault(sfxHandle_t sfx)
+{
+ if(sfx == default_sfx)
+ Com_Error(ERR_FATAL, "Can't load default sound effect %s\n", knownSfx[sfx].filename);
+
+ Com_Printf( "Warning: Using default sound for %s\n", knownSfx[sfx].filename);
+ knownSfx[sfx].isDefault = qtrue;
+ knownSfx[sfx].buffer = knownSfx[default_sfx].buffer;
+}
+
+/*
+=================
+S_AL_BufferEvict
+
+Doesn't work yet, so if OpenAL reports that you're out of memory, you'll just
+get "Catastrophic sound memory exhaustion". Whoops.
+=================
+*/
+static qboolean S_AL_BufferEvict( void )
+{
+ return qfalse;
+}
+
+/*
+=================
+S_AL_BufferLoad
+=================
+*/
+static void S_AL_BufferLoad(sfxHandle_t sfx)
+{
+ ALenum error;
+
+ void *data;
+ snd_info_t info;
+ ALuint format;
+
+ // Nothing?
+ if(knownSfx[sfx].filename[0] == '\0')
+ return;
+
+ // Player SFX
+ if(knownSfx[sfx].filename[0] == '*')
+ return;
+
+ // Already done?
+ if((knownSfx[sfx].inMemory) || (knownSfx[sfx].isDefault))
+ return;
+
+ // Try to load
+ data = S_CodecLoad(knownSfx[sfx].filename, &info);
+ if(!data)
+ {
+ Com_Printf( "Can't load %s\n", knownSfx[sfx].filename);
+ S_AL_BufferUseDefault(sfx);
+ return;
+ }
+
+ format = S_AL_Format(info.width, info.channels);
+
+ // Create a buffer
+ qalGenBuffers(1, &knownSfx[sfx].buffer);
+ if((error = qalGetError()) != AL_NO_ERROR)
+ {
+ S_AL_BufferUseDefault(sfx);
+ Z_Free(data);
+ Com_Printf( "Can't create a sound buffer for %s - %s\n", knownSfx[sfx].filename, S_AL_ErrorMsg(error));
+ return;
+ }
+
+ // Fill the buffer
+ qalGetError();
+ qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate);
+ error = qalGetError();
+
+ // If we ran out of memory, start evicting the least recently used sounds
+ while(error == AL_OUT_OF_MEMORY)
+ {
+ qboolean rv = S_AL_BufferEvict();
+ if(!rv)
+ {
+ S_AL_BufferUseDefault(sfx);
+ Z_Free(data);
+ Com_Printf( "Out of memory loading %s\n", knownSfx[sfx].filename);
+ return;
+ }
+
+ // Try load it again
+ qalGetError();
+ qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate);
+ error = qalGetError();
+ }
+
+ // Some other error condition
+ if(error != AL_NO_ERROR)
+ {
+ S_AL_BufferUseDefault(sfx);
+ Z_Free(data);
+ Com_Printf( "Can't fill sound buffer for %s - %s", knownSfx[sfx].filename, S_AL_ErrorMsg(error));
+ return;
+ }
+
+ // Free the memory
+ Z_Free(data);
+
+ // Woo!
+ knownSfx[sfx].inMemory = qtrue;
+}
+
+/*
+=================
+S_AL_BufferUse
+=================
+*/
+void S_AL_BufferUse(sfxHandle_t sfx)
+{
+ if(knownSfx[sfx].filename[0] == '\0')
+ return;
+
+ if((!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
+ S_AL_BufferLoad(sfx);
+ knownSfx[sfx].used = Com_Milliseconds();
+}
+
+/*
+=================
+S_AL_BufferInit
+=================
+*/
+qboolean S_AL_BufferInit( void )
+{
+ if(alBuffersInitialised)
+ return qtrue;
+
+ // Clear the hash table, and SFX table
+ memset(knownSfx, 0, sizeof(knownSfx));
+ numSfx = 0;
+
+ // Load the default sound, and lock it
+ default_sfx = S_AL_BufferFind("sound/feedback/hit.wav");
+ S_AL_BufferUse(default_sfx);
+ knownSfx[default_sfx].isLocked = qtrue;
+
+ // All done
+ alBuffersInitialised = qtrue;
+ return qtrue;
+}
+
+/*
+=================
+S_AL_BufferUnload
+=================
+*/
+static void S_AL_BufferUnload(sfxHandle_t sfx)
+{
+ ALenum error;
+
+ if(knownSfx[sfx].filename[0] == '\0')
+ return;
+
+ if(!knownSfx[sfx].inMemory)
+ return;
+
+ // Delete it
+ qalDeleteBuffers(1, &knownSfx[sfx].buffer);
+ if((error = qalGetError()) != AL_NO_ERROR)
+ Com_Printf( "Can't delete sound buffer for %s", knownSfx[sfx].filename);
+
+ knownSfx[sfx].inMemory = qfalse;
+}
+
+/*
+=================
+S_AL_BufferShutdown
+=================
+*/
+void S_AL_BufferShutdown( void )
+{
+ int i;
+
+ if(!alBuffersInitialised)
+ return;
+
+ // Unlock the default sound effect
+ knownSfx[default_sfx].isLocked = qfalse;
+
+ // Free all used effects
+ for(i = 0; i < MAX_SFX; i++)
+ S_AL_BufferUnload(i);
+
+ // Clear the tables
+ memset(knownSfx, 0, sizeof(knownSfx));
+
+ // All undone
+ alBuffersInitialised = qfalse;
+}
+
+/*
+=================
+S_AL_RegisterSound
+=================
+*/
+sfxHandle_t S_AL_RegisterSound( const char *sample, qboolean compressed )
+{
+ sfxHandle_t sfx = S_AL_BufferFind(sample);
+
+ if((s_alPrecache->integer == 1) && (!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
+ S_AL_BufferLoad(sfx);
+ knownSfx[sfx].used = Com_Milliseconds();
+
+ return sfx;
+}
+
+/*
+=================
+S_AL_BufferGet
+
+Return's an sfx's buffer
+=================
+*/
+ALuint S_AL_BufferGet(sfxHandle_t sfx)
+{
+ return knownSfx[sfx].buffer;
+}
+
+
+//===========================================================================
+
+
+typedef struct src_s
+{
+ ALuint source; // OpenAL source object
+ sfxHandle_t sfx; // Sound effect in use
+
+ int lastUse; // Last time used
+ alSrcPriority_t priority; // Priority
+ int entity; // Owning entity (-1 if none)
+ int channel; // Associated channel (-1 if none)
+
+ int isActive; // Is this source currently in use?
+ int isLocked; // This is locked (un-allocatable)
+ int isLooping; // Is this a looping effect (attached to an entity)
+ int isTracking; // Is this object tracking it's owner
+
+ qboolean local; // Is this local (relative to the cam)
+} src_t;
+
+#define MAX_SRC 128
+static src_t srcList[MAX_SRC];
+static int srcCount = 0;
+static qboolean alSourcesInitialised = qfalse;
+
+static int ambientCount = 0;
+
+typedef struct sentity_s
+{
+ vec3_t origin; // Object position
+
+ int has_sfx; // Associated sound source
+ int sfx;
+ int touched; // Sound present this update?
+} sentity_t;
+
+static sentity_t entityList[MAX_GENTITIES];
+
+/*
+=================
+S_AL_SrcInit
+=================
+*/
+qboolean S_AL_SrcInit( void )
+{
+ int i;
+ int limit;
+ ALenum error;
+
+ // Clear the sources data structure
+ memset(srcList, 0, sizeof(srcList));
+ srcCount = 0;
+
+ // Cap s_sources to MAX_SRC
+ limit = s_alSources->integer;
+ if(limit > MAX_SRC)
+ limit = MAX_SRC;
+ else if(limit < 16)
+ limit = 16;
+
+ // Allocate as many sources as possible
+ for(i = 0; i < limit; i++)
+ {
+ qalGenSources(1, &srcList[i].source);
+ if((error = qalGetError()) != AL_NO_ERROR)
+ break;
+ srcCount++;
+ }
+
+ // All done. Print this for informational purposes
+ Com_Printf( "Allocated %d sources.\n", srcCount);
+ alSourcesInitialised = qtrue;
+ return qtrue;
+}
+
+/*
+=================
+S_AL_SrcShutdown
+=================
+*/
+void S_AL_SrcShutdown( void )
+{
+ int i;
+
+ if(!alSourcesInitialised)
+ return;
+
+ // Destroy all the sources
+ for(i = 0; i < srcCount; i++)
+ {
+ if(srcList[i].isLocked)
+ Com_DPrintf("Warning: Source %d is locked\n", i);
+
+ qalSourceStop(srcList[i].source);
+ qalDeleteSources(1, &srcList[i].source);
+ }
+
+ memset(srcList, 0, sizeof(srcList));
+
+ alSourcesInitialised = qfalse;
+}
+
+/*
+=================
+S_AL_SrcSetup
+=================
+*/
+static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t priority,
+ int entity, int channel, qboolean local)
+{
+ ALuint buffer;
+ float null_vector[] = {0, 0, 0};
+
+ // Mark the SFX as used, and grab the raw AL buffer
+ S_AL_BufferUse(sfx);
+ buffer = S_AL_BufferGet(sfx);
+
+ // Set up src struct
+ srcList[src].lastUse = Sys_Milliseconds();
+ srcList[src].sfx = sfx;
+ srcList[src].priority = priority;
+ srcList[src].entity = entity;
+ srcList[src].channel = channel;
+ srcList[src].isActive = qtrue;
+ srcList[src].isLocked = qfalse;
+ srcList[src].isLooping = qfalse;
+ srcList[src].isTracking = qfalse;
+ srcList[src].local = local;
+
+ // Set up OpenAL source
+ qalSourcei(srcList[src].source, AL_BUFFER, buffer);
+ qalSourcef(srcList[src].source, AL_PITCH, 1.0f);
+ qalSourcef(srcList[src].source, AL_GAIN, s_alGain->value * s_volume->value);
+ qalSourcefv(srcList[src].source, AL_POSITION, null_vector);
+ qalSourcefv(srcList[src].source, AL_VELOCITY, null_vector);
+ qalSourcei(srcList[src].source, AL_LOOPING, AL_FALSE);
+ qalSourcef(srcList[src].source, AL_REFERENCE_DISTANCE, s_alMinDistance->value);
+
+ if(local)
+ {
+ qalSourcei(srcList[src].source, AL_SOURCE_RELATIVE, AL_TRUE);
+ qalSourcef(srcList[src].source, AL_ROLLOFF_FACTOR, 0);
+ }
+ else
+ {
+ qalSourcei(srcList[src].source, AL_SOURCE_RELATIVE, AL_FALSE);
+ qalSourcef(srcList[src].source, AL_ROLLOFF_FACTOR, s_alRolloff->value);
+ }
+}
+
+/*
+=================
+S_AL_SrcKill
+=================
+*/
+static void S_AL_SrcKill(srcHandle_t src)
+{
+ // I'm not touching it. Unlock it first.
+ if(srcList[src].isLocked)
+ return;
+
+ // Stop it if it's playing
+ if(srcList[src].isActive)
+ qalSourceStop(srcList[src].source);
+
+ // Remove the entity association
+ if((srcList[src].isLooping) && (srcList[src].entity != -1))
+ {
+ int ent = srcList[src].entity;
+ entityList[ent].has_sfx = 0;
+ entityList[ent].sfx = -1;
+ entityList[ent].touched = qfalse;
+ }
+
+ // Remove the buffer
+ qalSourcei(srcList[src].source, AL_BUFFER, 0);
+
+ srcList[src].sfx = 0;
+ srcList[src].lastUse = 0;
+ srcList[src].priority = 0;
+ srcList[src].entity = -1;
+ srcList[src].channel = -1;
+ srcList[src].isActive = qfalse;
+ srcList[src].isLocked = qfalse;
+ srcList[src].isLooping = qfalse;
+ srcList[src].isTracking = qfalse;
+}
+
+/*
+=================
+S_AL_SrcAlloc
+=================
+*/
+srcHandle_t S_AL_SrcAlloc( alSrcPriority_t priority, int entnum, int channel )
+{
+ int i;
+ int empty = -1;
+ int weakest = -1;
+ int weakest_time = Sys_Milliseconds();
+ int weakest_pri = 999;
+
+ for(i = 0; i < srcCount; i++)
+ {
+ // If it's locked, we aren't even going to look at it
+ if(srcList[i].isLocked)
+ continue;
+
+ // Is it empty or not?
+ if((!srcList[i].isActive) && (empty == -1))
+ empty = i;
+ else if(srcList[i].priority < priority)
+ {
+ // If it's older or has lower priority, flag it as weak
+ if((srcList[i].priority < weakest_pri) ||
+ (srcList[i].lastUse < weakest_time))
+ {
+ weakest_pri = srcList[i].priority;
+ weakest_time = srcList[i].lastUse;
+ weakest = i;
+ }
+ }
+
+ // Is it an exact match, and not on channel 0?
+ if((srcList[i].entity == entnum) && (srcList[i].channel == channel) && (channel != 0))
+ {
+ S_AL_SrcKill(i);
+ return i;
+ }
+ }
+
+ // Do we have an empty one?
+ if(empty != -1)
+ return empty;
+
+ // No. How about an overridable one?
+ if(weakest != -1)
+ {
+ S_AL_SrcKill(weakest);
+ return weakest;
+ }
+
+ // Nothing. Return failure (cries...)
+ return -1;
+}
+
+/*
+=================
+S_AL_SrcFind
+
+Finds an active source with matching entity and channel numbers
+Returns -1 if there isn't one
+=================
+*/
+srcHandle_t S_AL_SrcFind(int entnum, int channel)
+{
+ int i;
+ for(i = 0; i < srcCount; i++)
+ {
+ if(!srcList[i].isActive)
+ continue;
+ if((srcList[i].entity == entnum) && (srcList[i].channel == channel))
+ return i;
+ }
+ return -1;
+}
+
+/*
+=================
+S_AL_SrcLock
+
+Locked sources will not be automatically reallocated or managed
+=================
+*/
+void S_AL_SrcLock(srcHandle_t src)
+{
+ srcList[src].isLocked = qtrue;
+}
+
+/*
+=================
+S_AL_SrcUnlock
+
+Once unlocked, the source may be reallocated again
+=================
+*/
+void S_AL_SrcUnlock(srcHandle_t src)
+{
+ srcList[src].isLocked = qfalse;
+}
+
+/*
+=================
+S_AL_UpdateEntityPosition
+=================
+*/
+void S_AL_UpdateEntityPosition( int entityNum, const vec3_t origin )
+{
+ if ( entityNum < 0 || entityNum > MAX_GENTITIES )
+ Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
+ VectorCopy( origin, entityList[entityNum].origin );
+}
+
+/*
+=================
+S_AL_StartLocalSound
+
+Play a local (non-spatialized) sound effect
+=================
+*/
+void S_AL_StartLocalSound(sfxHandle_t sfx, int channel)
+{
+ // Try to grab a source
+ srcHandle_t src = S_AL_SrcAlloc(SRCPRI_LOCAL, -1, channel);
+ if(src == -1)
+ return;
+
+ // Set up the effect
+ S_AL_SrcSetup(src, sfx, SRCPRI_LOCAL, -1, channel, qtrue);
+
+ // Start it playing
+ qalSourcePlay(srcList[src].source);
+}
+
+#define POSITION_SCALE 1.0f
+
+/*
+=================
+S_AL_StartSound
+
+Play a one-shot sound effect
+=================
+*/
+void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx )
+{
+ vec3_t sorigin;
+
+ // Try to grab a source
+ srcHandle_t src = S_AL_SrcAlloc(SRCPRI_ONESHOT, entnum, entchannel);
+ if(src == -1)
+ return;
+
+ // Set up the effect
+ S_AL_SrcSetup(src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse);
+
+ if(origin == NULL)
+ {
+ srcList[src].isTracking = qtrue;
+ VectorScale(entityList[entnum].origin, POSITION_SCALE, sorigin);
+ }
+ else
+ VectorScale(origin, POSITION_SCALE, sorigin);
+ qalSourcefv(srcList[src].source, AL_POSITION, sorigin);
+
+ // Start it playing
+ qalSourcePlay(srcList[src].source);
+}
+
+/*
+=================
+S_AL_ClearLoopingSounds
+=================
+*/
+void S_AL_ClearLoopingSounds( qboolean killall )
+{
+ int i;
+ for(i = 0; i < srcCount; i++)
+ {
+ if((srcList[i].isLooping) && (srcList[i].entity != -1))
+ entityList[srcList[i].entity].touched = qfalse;
+ }
+}
+
+/*
+=================
+S_AL_SrcLoop
+=================
+*/
+static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx,
+ const vec3_t origin, const vec3_t velocity, int entnum)
+{
+ int src;
+ qboolean need_to_play = qfalse;
+ vec3_t sorigin;
+
+ // Do we need to start a new sound playing?
+ if(!entityList[entnum].has_sfx)
+ {
+ // Try to get a channel
+ ambientCount++;
+ src = S_AL_SrcAlloc(priority, entnum, -1);
+ if(src == -1)
+ return;
+ need_to_play = qtrue;
+ }
+ else if(srcList[entityList[entnum].sfx].sfx != sfx)
+ {
+ // Need to restart. Just re-use this channel
+ src = entityList[entnum].sfx;
+ S_AL_SrcKill(src);
+ need_to_play = qtrue;
+ }
+ else
+ src = entityList[entnum].sfx;
+
+ if(need_to_play)
+ {
+ // Set up the effect
+ S_AL_SrcSetup(src, sfx, priority, entnum, -1, qfalse);
+ qalSourcei(srcList[src].source, AL_LOOPING, AL_TRUE);
+ srcList[src].isLooping = qtrue;
+
+ // Set up the entity
+ entityList[entnum].has_sfx = qtrue;
+ entityList[entnum].sfx = src;
+ need_to_play = qtrue;
+ }
+
+ // Set up the position and velocity
+ VectorScale(entityList[entnum].origin, POSITION_SCALE, sorigin);
+ qalSourcefv(srcList[src].source, AL_POSITION, sorigin);
+ qalSourcefv(srcList[src].source, AL_VELOCITY, velocity);
+
+ // Flag it
+ entityList[entnum].touched = qtrue;
+
+ // Play if need be
+ if(need_to_play)
+ qalSourcePlay(srcList[src].source);
+}
+
+/*
+=================
+S_AL_AddLoopingSound
+=================
+*/
+void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
+{
+ S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, origin, velocity, entityNum);
+}
+
+/*
+=================
+S_AL_AddRealLoopingSound
+=================
+*/
+void S_AL_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
+{
+ S_AL_SrcLoop(SRCPRI_ENTITY, sfx, origin, velocity, entityNum);
+}
+
+/*
+=================
+S_AL_StopLoopingSound
+=================
+*/
+void S_AL_StopLoopingSound(int entityNum )
+{
+ if(entityList[entityNum].has_sfx)
+ S_AL_SrcKill(entityList[entityNum].sfx);
+}
+
+/*
+=================
+S_AL_SrcUpdate
+
+Update state (move things around, manage sources, and so on)
+=================
+*/
+void S_AL_SrcUpdate( void )
+{
+ int i;
+ int ent;
+ ALint state;
+
+ for(i = 0; i < srcCount; i++)
+ {
+ if(srcList[i].isLocked)
+ continue;
+
+ if(!srcList[i].isActive)
+ continue;
+
+ // Check if it's done, and flag it
+ qalGetSourcei(srcList[i].source, AL_SOURCE_STATE, &state);
+ if(state == AL_STOPPED)
+ {
+ S_AL_SrcKill(i);
+ continue;
+ }
+
+ // Update source parameters
+ if((s_alGain->modified)||(s_volume->modified))
+ qalSourcef(srcList[i].source, AL_GAIN, s_alGain->value * s_volume->value);
+ if((s_alRolloff->modified)&&(!srcList[i].local))
+ qalSourcef(srcList[i].source, AL_ROLLOFF_FACTOR, s_alRolloff->value);
+ if(s_alMinDistance->modified)
+ qalSourcef(srcList[i].source, AL_REFERENCE_DISTANCE, s_alMinDistance->value);
+
+ ent = srcList[i].entity;
+
+ // If a looping effect hasn't been touched this frame, kill it
+ if(srcList[i].isLooping)
+ {
+ if(!entityList[ent].touched)
+ {
+ ambientCount--;
+ S_AL_SrcKill(i);
+ }
+ continue;
+ }
+
+ // See if it needs to be moved
+ if(srcList[i].isTracking)
+ {
+ vec3_t sorigin;
+ VectorScale(entityList[ent].origin, POSITION_SCALE, sorigin);
+ qalSourcefv(srcList[i].source, AL_POSITION, entityList[ent].origin);
+ }
+ }
+}
+
+/*
+=================
+S_AL_SrcShutup
+=================
+*/
+void S_AL_SrcShutup( void )
+{
+ int i;
+ for(i = 0; i < srcCount; i++)
+ S_AL_SrcKill(i);
+}
+
+/*
+=================
+S_AL_SrcGet
+=================
+*/
+ALuint S_AL_SrcGet(srcHandle_t src)
+{
+ return srcList[src].source;
+}
+
+
+//===========================================================================
+
+
+static srcHandle_t streamSourceHandle = -1;
+static qboolean streamPlaying = qfalse;
+static ALuint streamSource;
+
+/*
+=================
+S_AL_AllocateStreamChannel
+=================
+*/
+static void S_AL_AllocateStreamChannel( void )
+{
+ // Allocate a streamSource at high priority
+ streamSourceHandle = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
+ if(streamSourceHandle == -1)
+ return;
+
+ // Lock the streamSource so nobody else can use it, and get the raw streamSource
+ S_AL_SrcLock(streamSourceHandle);
+ streamSource = S_AL_SrcGet(streamSourceHandle);
+
+ // Set some streamSource parameters
+ qalSourcei (streamSource, AL_BUFFER, 0 );
+ qalSourcei (streamSource, AL_LOOPING, AL_FALSE );
+ qalSource3f(streamSource, AL_POSITION, 0.0, 0.0, 0.0);
+ qalSource3f(streamSource, AL_VELOCITY, 0.0, 0.0, 0.0);
+ qalSource3f(streamSource, AL_DIRECTION, 0.0, 0.0, 0.0);
+ qalSourcef (streamSource, AL_ROLLOFF_FACTOR, 0.0 );
+ qalSourcei (streamSource, AL_SOURCE_RELATIVE, AL_TRUE );
+}
+
+/*
+=================
+S_AL_FreeStreamChannel
+=================
+*/
+static void S_AL_FreeStreamChannel( void )
+{
+ // Release the output streamSource
+ S_AL_SrcUnlock(streamSourceHandle);
+ streamSource = 0;
+ streamSourceHandle = -1;
+}
+
+/*
+=================
+S_AL_RawSamples
+=================
+*/
+void S_AL_RawSamples(int samples, int rate, int width, int channels, const byte *data, float volume)
+{
+ ALuint buffer;
+ ALuint format = AL_FORMAT_STEREO16;
+ ALint state;
+
+ // Work out AL format
+ if(width == 1)
+ {
+ if(channels == 1)
+ format = AL_FORMAT_MONO8;
+ else if(channels == 2)
+ format = AL_FORMAT_STEREO8;
+ }
+ else if(width == 2)
+ {
+ if(channels == 1)
+ format = AL_FORMAT_MONO16;
+ else if(channels == 2)
+ format = AL_FORMAT_STEREO16;
+ }
+
+ // Create the streamSource if necessary
+ if(streamSourceHandle == -1)
+ {
+ S_AL_AllocateStreamChannel();
+
+ // Failed?
+ if(streamSourceHandle == -1)
+ {
+ Com_Printf( "Can't allocate streaming streamSource\n");
+ return;
+ }
+ }
+
+ // Create a buffer, and stuff the data into it
+ qalGenBuffers(1, &buffer);
+ qalBufferData(buffer, format, data, (samples * width * channels), rate);
+
+ // Shove the data onto the streamSource
+ qalSourceQueueBuffers(streamSource, 1, &buffer);
+
+ // Start the streamSource playing if necessary
+ qalGetSourcei(streamSource, AL_SOURCE_STATE, &state);
+
+ // Volume
+ qalSourcef (streamSource, AL_GAIN, volume * s_volume->value * s_alGain->value);
+
+ if(!streamPlaying)
+ {
+ qalSourcePlay(streamSource);
+ streamPlaying = qtrue;
+ }
+}
+
+/*
+=================
+S_AL_StreamUpdate
+=================
+*/
+void S_AL_StreamUpdate( void )
+{
+ int processed;
+ ALint state;
+
+ if(streamSourceHandle == -1)
+ return;
+
+ // Un-queue any buffers, and delete them
+ qalGetSourcei(streamSource, AL_BUFFERS_PROCESSED, &processed);
+ if(processed)
+ {
+ while(processed--)
+ {
+ ALuint buffer;
+ qalSourceUnqueueBuffers(streamSource, 1, &buffer);
+ qalDeleteBuffers(1, &buffer);
+ }
+ }
+
+ // If it's stopped, release the streamSource
+ qalGetSourcei(streamSource, AL_SOURCE_STATE, &state);
+ if(state == AL_STOPPED)
+ {
+ streamPlaying = qfalse;
+ qalSourceStop(streamSource);
+ S_AL_FreeStreamChannel();
+ }
+}
+
+/*
+=================
+S_AL_StreamDie
+=================
+*/
+void S_AL_StreamDie( void )
+{
+ if(streamSourceHandle == -1)
+ return;
+
+ streamPlaying = qfalse;
+ qalSourceStop(streamSource);
+ S_AL_FreeStreamChannel();
+}
+
+
+//===========================================================================
+
+
+#define NUM_MUSIC_BUFFERS 4
+#define MUSIC_BUFFER_SIZE 4096
+
+static qboolean musicPlaying = qfalse;
+static srcHandle_t musicSourceHandle = -1;
+static ALuint musicSource;
+static ALuint musicBuffers[NUM_MUSIC_BUFFERS];
+
+static snd_stream_t *mus_stream;
+static char s_backgroundLoop[MAX_QPATH];
+
+static byte decode_buffer[MUSIC_BUFFER_SIZE];
+
+/*
+=================
+S_AL_MusicSourceGet
+=================
+*/
+static void S_AL_MusicSourceGet( void )
+{
+ // Allocate a musicSource at high priority
+ musicSourceHandle = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
+ if(musicSourceHandle == -1)
+ return;
+
+ // Lock the musicSource so nobody else can use it, and get the raw musicSource
+ S_AL_SrcLock(musicSourceHandle);
+ musicSource = S_AL_SrcGet(musicSourceHandle);
+
+ // Set some musicSource parameters
+ qalSource3f(musicSource, AL_POSITION, 0.0, 0.0, 0.0);
+ qalSource3f(musicSource, AL_VELOCITY, 0.0, 0.0, 0.0);
+ qalSource3f(musicSource, AL_DIRECTION, 0.0, 0.0, 0.0);
+ qalSourcef (musicSource, AL_ROLLOFF_FACTOR, 0.0 );
+ qalSourcei (musicSource, AL_SOURCE_RELATIVE, AL_TRUE );
+}
+
+/*
+=================
+S_AL_MusicSourceFree
+=================
+*/
+static void S_AL_MusicSourceFree( void )
+{
+ // Release the output musicSource
+ S_AL_SrcUnlock(musicSourceHandle);
+ musicSource = 0;
+ musicSourceHandle = -1;
+}
+
+/*
+=================
+S_AL_StopBackgroundTrack
+=================
+*/
+void S_AL_StopBackgroundTrack( void )
+{
+ if(!musicPlaying)
+ return;
+
+ // Stop playing
+ qalSourceStop(musicSource);
+
+ // De-queue the musicBuffers
+ qalSourceUnqueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
+
+ // Destroy the musicBuffers
+ qalDeleteBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
+
+ // Free the musicSource
+ S_AL_MusicSourceFree();
+
+ // Unload the stream
+ if(mus_stream)
+ S_CodecCloseStream(mus_stream);
+ mus_stream = NULL;
+
+ musicPlaying = qfalse;
+}
+
+/*
+=================
+S_AL_MusicProcess
+=================
+*/
+void S_AL_MusicProcess(ALuint b)
+{
+ int l;
+ ALuint format;
+
+ l = S_CodecReadStream(mus_stream, MUSIC_BUFFER_SIZE, decode_buffer);
+
+ if(l == 0)
+ {
+ S_CodecCloseStream(mus_stream);
+ mus_stream = S_CodecOpenStream(s_backgroundLoop);
+ if(!mus_stream)
+ {
+ S_AL_StopBackgroundTrack();
+ return;
+ }
+
+ l = S_CodecReadStream(mus_stream, MUSIC_BUFFER_SIZE, decode_buffer);
+ }
+
+ format = S_AL_Format(mus_stream->info.width, mus_stream->info.channels);
+ qalBufferData(b, format, decode_buffer, l, mus_stream->info.rate);
+}
+
+/*
+=================
+S_AL_StartBackgroundTrack
+=================
+*/
+void S_AL_StartBackgroundTrack( const char *intro, const char *loop )
+{
+ int i;
+
+ // Stop any existing music that might be playing
+ S_AL_StopBackgroundTrack();
+
+ if ( !intro || !intro[0] ) {
+ intro = loop;
+ }
+ if ( !loop || !loop[0] ) {
+ loop = intro;
+ }
+
+ if((!intro || !intro[0]) && (!intro || !intro[0]))
+ return;
+
+ // Copy the loop over
+ strncpy( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
+
+ // Open the intro
+ mus_stream = S_CodecOpenStream(intro);
+
+ if(!mus_stream)
+ return;
+
+ // Allocate a musicSource
+ S_AL_MusicSourceGet();
+ if(musicSourceHandle == -1)
+ return;
+
+ // Generate the musicBuffers
+ qalGenBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
+
+ // Queue the musicBuffers up
+ for(i = 0; i < NUM_MUSIC_BUFFERS; i++)
+ S_AL_MusicProcess(musicBuffers[i]);
+ qalSourceQueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
+
+ // Start playing
+ qalSourcePlay(musicSource);
+
+ musicPlaying = qtrue;
+}
+
+/*
+=================
+S_AL_MusicUpdate
+=================
+*/
+void S_AL_MusicUpdate( void )
+{
+ int processed;
+ ALint state;
+
+ if(!musicPlaying)
+ return;
+
+ qalGetSourcei(musicSource, AL_BUFFERS_PROCESSED, &processed);
+ if(processed)
+ {
+ while(processed--)
+ {
+ ALuint b;
+ qalSourceUnqueueBuffers(musicSource, 1, &b);
+ S_AL_MusicProcess(b);
+ qalSourceQueueBuffers(musicSource, 1, &b);
+ }
+ }
+
+ // If it's not still playing, give it a kick
+ qalGetSourcei(musicSource, AL_SOURCE_STATE, &state);
+ if(state == AL_STOPPED)
+ {
+ Com_DPrintf( "Restarted OpenAL music musicSource\n");
+ qalSourcePlay(musicSource);
+ }
+
+ // Set the gain property
+ qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value);
+}
+
+
+//===========================================================================
+
+
+// Local state variables
+static ALCdevice *alDevice;
+static ALCcontext *alContext;
+
+#ifdef _WIN32
+#define ALDRIVER_DEFAULT "OpenAL32.dll"
+#else
+#define ALDRIVER_DEFAULT "libopenal.so"
+#endif
+
+/*
+=================
+S_AL_StopAllSounds
+=================
+*/
+void S_AL_StopAllSounds( void )
+{
+ S_AL_SrcShutup();
+ S_AL_StopBackgroundTrack();
+}
+
+/*
+=================
+S_AL_Respatialize
+=================
+*/
+void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater )
+{
+ // Axis[0] = Forward
+ // Axis[2] = Up
+ float velocity[] = {0.0f, 0.0f, 0.0f};
+ float orientation[] = {axis[0][0], axis[0][1], axis[0][2],
+ axis[2][0], axis[2][1], axis[2][2]};
+ vec3_t sorigin;
+
+ // Set OpenAL listener paramaters
+ VectorScale(origin, POSITION_SCALE, sorigin);
+ qalListenerfv(AL_POSITION, origin);
+ qalListenerfv(AL_VELOCITY, velocity);
+ qalListenerfv(AL_ORIENTATION, orientation);
+}
+
+/*
+=================
+S_AL_Update
+=================
+*/
+void S_AL_Update( void )
+{
+ // Update SFX channels
+ S_AL_SrcUpdate();
+
+ // Update streams
+ S_AL_StreamUpdate();
+ S_AL_MusicUpdate();
+
+ // Doppler
+ if(s_doppler->modified)
+ {
+ s_alDopplerFactor->modified = qtrue;
+ s_doppler->modified = qfalse;
+ }
+
+ // Doppler parameters
+ if(s_alDopplerFactor->modified)
+ {
+ if(s_doppler->integer)
+ qalDopplerFactor(s_alDopplerFactor->value);
+ else
+ qalDopplerFactor(0.0f);
+ s_alDopplerFactor->modified = qfalse;
+ }
+ if(s_alDopplerSpeed->modified)
+ {
+ qalDopplerVelocity(s_alDopplerSpeed->value);
+ s_alDopplerSpeed->modified = qfalse;
+ }
+
+ // Clear the modified flags on the other cvars
+ s_alGain->modified = qfalse;
+ s_volume->modified = qfalse;
+ s_musicVolume->modified = qfalse;
+ s_alMinDistance->modified = qfalse;
+ s_alRolloff->modified = qfalse;
+}
+
+/*
+=================
+S_AL_DisableSounds
+=================
+*/
+void S_AL_DisableSounds( void )
+{
+ S_AL_StopAllSounds();
+}
+
+/*
+=================
+S_AL_BeginRegistration
+=================
+*/
+void S_AL_BeginRegistration( void )
+{
+}
+
+/*
+=================
+S_AL_ClearSoundBuffer
+=================
+*/
+void S_AL_ClearSoundBuffer( void )
+{
+}
+
+/*
+=================
+S_AL_SoundList
+=================
+*/
+void S_AL_SoundList( void )
+{
+}
+
+/*
+=================
+S_AL_SoundInfo
+=================
+*/
+void S_AL_SoundInfo( void )
+{
+ Com_Printf( "OpenAL info:\n" );
+ Com_Printf( " Vendor: %s\n", qalGetString( AL_VENDOR ) );
+ Com_Printf( " Version: %s\n", qalGetString( AL_VERSION ) );
+ Com_Printf( " Renderer: %s\n", qalGetString( AL_RENDERER ) );
+ Com_Printf( " Extensions: %s\n", qalGetString( AL_EXTENSIONS ) );
+
+}
+
+/*
+=================
+S_AL_Shutdown
+=================
+*/
+void S_AL_Shutdown( void )
+{
+ // Shut down everything
+ S_AL_StreamDie( );
+ S_AL_StopBackgroundTrack( );
+ S_AL_SrcShutdown( );
+ S_AL_BufferShutdown( );
+
+ // Check for Linux shutdown race condition
+ // FIXME: this will probably not be necessary once OpenAL CVS
+ // from 11/11/05 is released and prevelant
+ if( Q_stricmp( qalGetString( AL_VENDOR ), "J. Valenzuela" ) ) {
+ qalcMakeContextCurrent( NULL );
+ }
+
+ qalcDestroyContext(alContext);
+ qalcCloseDevice(alDevice);
+
+ QAL_Shutdown();
+}
+
+#endif
+
+/*
+=================
+S_AL_Init
+=================
+*/
+qboolean S_AL_Init( soundInterface_t *si )
+{
+#if USE_OPENAL
+ if( !si ) {
+ return qfalse;
+ }
+
+ // New console variables
+ s_alPrecache = Cvar_Get( "s_alPrecache", "0", CVAR_ARCHIVE );
+ s_alGain = Cvar_Get( "s_alGain", "0.4", CVAR_ARCHIVE );
+ s_alSources = Cvar_Get( "s_alSources", "64", CVAR_ARCHIVE );
+ s_alDopplerFactor = Cvar_Get( "s_alDopplerFactor", "1.0", CVAR_ARCHIVE );
+ s_alDopplerSpeed = Cvar_Get( "s_alDopplerSpeed", "2200", CVAR_ARCHIVE );
+ s_alMinDistance = Cvar_Get( "s_alMinDistance", "80", CVAR_ARCHIVE );
+ s_alRolloff = Cvar_Get( "s_alRolloff", "0.25", CVAR_ARCHIVE );
+
+ s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE );
+
+ // Load QAL
+ if( !QAL_Init( s_alDriver->string ) )
+ {
+ Com_Printf( "Failed to load library: \"%s\".\n", s_alDriver->string );
+ return qfalse;
+ }
+
+ // Open default device
+ alDevice = qalcOpenDevice( NULL );
+ if( !alDevice )
+ {
+ QAL_Shutdown( );
+ Com_Printf( "Failed to open OpenAL device.\n" );
+ return qfalse;
+ }
+
+ // Create OpenAL context
+ alContext = qalcCreateContext( alDevice, NULL );
+ if( !alContext )
+ {
+ QAL_Shutdown( );
+ qalcCloseDevice( alDevice );
+ Com_Printf( "Failed to create OpenAL context.\n" );
+ return qfalse;
+ }
+ qalcMakeContextCurrent( alContext );
+
+ // Initialize sources, buffers, music
+ S_AL_BufferInit( );
+ S_AL_SrcInit( );
+
+ // Set up OpenAL parameters (doppler, etc)
+ qalDopplerFactor( s_alDopplerFactor->value );
+ qalDopplerVelocity( s_alDopplerSpeed->value );
+
+ si->Shutdown = S_AL_Shutdown;
+ si->StartSound = S_AL_StartSound;
+ si->StartLocalSound = S_AL_StartLocalSound;
+ si->StartBackgroundTrack = S_AL_StartBackgroundTrack;
+ si->StopBackgroundTrack = S_AL_StopBackgroundTrack;
+ si->RawSamples = S_AL_RawSamples;
+ si->StopAllSounds = S_AL_StopAllSounds;
+ si->ClearLoopingSounds = S_AL_ClearLoopingSounds;
+ si->AddLoopingSound = S_AL_AddLoopingSound;
+ si->AddRealLoopingSound = S_AL_AddRealLoopingSound;
+ si->StopLoopingSound = S_AL_StopLoopingSound;
+ si->Respatialize = S_AL_Respatialize;
+ si->UpdateEntityPosition = S_AL_UpdateEntityPosition;
+ si->Update = S_AL_Update;
+ si->DisableSounds = S_AL_DisableSounds;
+ si->BeginRegistration = S_AL_BeginRegistration;
+ si->RegisterSound = S_AL_RegisterSound;
+ si->ClearSoundBuffer = S_AL_ClearSoundBuffer;
+ si->SoundInfo = S_AL_SoundInfo;
+ si->SoundList = S_AL_SoundList;
+
+ return qtrue;
+#else
+ return qfalse;
+#endif
+}
+
diff --git a/code/macosx/Quake3.pbproj/project.pbxproj b/code/macosx/Quake3.pbproj/project.pbxproj
index 12c8dcd..955c91a 100644
--- a/code/macosx/Quake3.pbproj/project.pbxproj
+++ b/code/macosx/Quake3.pbproj/project.pbxproj
@@ -999,6 +999,13 @@
};
012AD9A500868211C697A10E = {
children = (
+ 92847F3509279B370056BC59,
+ 92847F3609279B370056BC59,
+ 92847F3709279B370056BC59,
+ 92847F3809279B370056BC59,
+ 92847F3909279B370056BC59,
+ 92847F3A09279B370056BC59,
+ 92847F3B09279B370056BC59,
9260378009101A6C0018EAE6,
012AD9A600868211C697A10E,
012AD9A700868211C697A10E,
@@ -6764,6 +6771,8 @@
9260377A09101A3B0018EAE6,
9260378109101A6C0018EAE6,
9292851809192BA800286DE9,
+ 92847F3D09279B370056BC59,
+ 92847F4009279B370056BC59,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@@ -6919,6 +6928,11 @@
4F23A81D08F4FA8F00CB90D3,
92603767091019D30018EAE6,
92603768091019D30018EAE6,
+ 92847F3C09279B370056BC59,
+ 92847F3E09279B370056BC59,
+ 92847F3F09279B370056BC59,
+ 92847F4109279B370056BC59,
+ 92847F4209279B370056BC59,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@@ -7775,6 +7789,8 @@
9260377B09101A3B0018EAE6,
9260378209101A6C0018EAE6,
9292851909192BA800286DE9,
+ 92847F4409279B370056BC59,
+ 92847F4709279B370056BC59,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@@ -8260,6 +8276,11 @@
4F23A81508F4FA8F00CB90D3,
9260376B091019D30018EAE6,
9260376C091019D30018EAE6,
+ 92847F4309279B370056BC59,
+ 92847F4509279B370056BC59,
+ 92847F4609279B370056BC59,
+ 92847F4809279B370056BC59,
+ 92847F4909279B370056BC59,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@@ -10932,6 +10953,146 @@
settings = {
};
};
+ 92847F3509279B370056BC59 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.c;
+ path = qal.c;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 92847F3609279B370056BC59 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = qal.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 92847F3709279B370056BC59 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.c;
+ path = snd_codec_wav.c;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 92847F3809279B370056BC59 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.c;
+ path = snd_codec.c;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 92847F3909279B370056BC59 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = snd_codec.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 92847F3A09279B370056BC59 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.c;
+ path = snd_main.c;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 92847F3B09279B370056BC59 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.c;
+ path = snd_openal.c;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 92847F3C09279B370056BC59 = {
+ fileRef = 92847F3509279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F3D09279B370056BC59 = {
+ fileRef = 92847F3609279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F3E09279B370056BC59 = {
+ fileRef = 92847F3709279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F3F09279B370056BC59 = {
+ fileRef = 92847F3809279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4009279B370056BC59 = {
+ fileRef = 92847F3909279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4109279B370056BC59 = {
+ fileRef = 92847F3A09279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4209279B370056BC59 = {
+ fileRef = 92847F3B09279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4309279B370056BC59 = {
+ fileRef = 92847F3509279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4409279B370056BC59 = {
+ fileRef = 92847F3609279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4509279B370056BC59 = {
+ fileRef = 92847F3709279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4609279B370056BC59 = {
+ fileRef = 92847F3809279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4709279B370056BC59 = {
+ fileRef = 92847F3909279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4809279B370056BC59 = {
+ fileRef = 92847F3A09279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 92847F4909279B370056BC59 = {
+ fileRef = 92847F3B09279B370056BC59;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
9292851709192BA800286DE9 = {
fileEncoding = 30;
isa = PBXFileReference;
diff --git a/code/unix/Makefile b/code/unix/Makefile
index 43083d1..4d2ab9b 100644
--- a/code/unix/Makefile
+++ b/code/unix/Makefile
@@ -47,6 +47,10 @@ ifndef USE_SDL
USE_SDL=1
endif
+ifndef USE_OPENAL
+USE_OPENAL=1
+endif
+
ifndef BUILD_CLIENT
BUILD_CLIENT=1
endif
@@ -115,6 +119,10 @@ ifeq ($(PLATFORM),linux)
BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes
+ ifeq ($(USE_OPENAL),1)
+ BASE_CFLAGS += -DUSE_OPENAL=1
+ endif
+
ifeq ($(USE_SDL),1)
BASE_CFLAGS += -DUSE_SDL_VIDEO=1 -DUSE_SDL_SOUND=1 $(shell sdl-config --cflags)
GL_CFLAGS =
@@ -214,6 +222,10 @@ ifeq ($(PLATFORM),mingw32)
BASE_CFLAGS = -Wall -fno-strict-aliasing -Wimplicit -Wstrict-prototypes
+ ifeq ($(USE_OPENAL),1)
+ BASE_CFLAGS += -DUSE_OPENAL=1
+ endif
+
DX_CFLAGS = -I$(DXSDK_DIR)/Include
GL_CFLAGS =
@@ -620,6 +632,13 @@ Q3OBJ = \
$(B)/client/snd_mix.o \
$(B)/client/snd_wavelet.o \
\
+ $(B)/client/snd_main.o \
+ $(B)/client/snd_codec.o \
+ $(B)/client/snd_codec_wav.o \
+ \
+ $(B)/client/qal.o \
+ $(B)/client/snd_openal.o \
+ \
$(B)/client/sv_bot.o \
$(B)/client/sv_ccmds.o \
$(B)/client/sv_client.o \
@@ -891,6 +910,14 @@ $(B)/client/snd_dma.o : $(CDIR)/snd_dma.c; $(DO_CC)
$(B)/client/snd_mem.o : $(CDIR)/snd_mem.c; $(DO_CC)
$(B)/client/snd_mix.o : $(CDIR)/snd_mix.c; $(DO_CC)
$(B)/client/snd_wavelet.o : $(CDIR)/snd_wavelet.c; $(DO_CC)
+
+$(B)/client/snd_main.o : $(CDIR)/snd_main.c; $(DO_CC)
+$(B)/client/snd_codec.o : $(CDIR)/snd_codec.c; $(DO_CC)
+$(B)/client/snd_codec_wav.o : $(CDIR)/snd_codec_wav.c; $(DO_CC)
+
+$(B)/client/qal.o : $(CDIR)/qal.c; $(DO_CC)
+$(B)/client/snd_openal.o : $(CDIR)/snd_openal.c; $(DO_CC)
+
$(B)/client/sv_bot.o : $(SDIR)/sv_bot.c; $(DO_CC)
$(B)/client/sv_client.o : $(SDIR)/sv_client.c; $(DO_CC)
$(B)/client/sv_ccmds.o : $(SDIR)/sv_ccmds.c; $(DO_CC)
diff --git a/code/unix/sdl_snd.c b/code/unix/sdl_snd.c
index 3caf85a..ac4a614 100644
--- a/code/unix/sdl_snd.c
+++ b/code/unix/sdl_snd.c
@@ -56,233 +56,285 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../qcommon/q_shared.h"
#include "../client/snd_local.h"
-int snd_inited=0;
+qboolean snd_inited = qfalse;
-cvar_t *sndbits;
-cvar_t *sndspeed;
-cvar_t *sndchannels;
-cvar_t *snddevice;
-cvar_t *sdldevsamps;
-cvar_t *sdlmixsamps;
+cvar_t *s_sdlBits;
+cvar_t *s_sdlSpeed;
+cvar_t *s_sdlChannels;
+cvar_t *s_sdlDevSamps;
+cvar_t *s_sdlMixSamps;
static qboolean use_custom_memset = qfalse;
-// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
+
+/*
+===============
+Snd_Memset
+
+https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
+
+<TTimo> some shitty mess with DMA buffers
+<TTimo> the mmap'ing permissions were write only
+<TTimo> and glibc optimized for mmx would do memcpy with a prefetch and a read
+<TTimo> causing segfaults
+<TTimo> some other systems would not let you mmap the DMA with read permissions
+<TTimo> so I think I ended up attempting opening with read/write, then try write only
+<TTimo> and use my own copy instead of the glibc crap
+===============
+*/
void Snd_Memset (void* dest, const int val, const size_t count)
{
- int *pDest;
- int i, iterate;
-
- if (!use_custom_memset)
- {
- Com_Memset(dest,val,count);
- return;
- }
- iterate = count / sizeof(int);
- pDest = (int*)dest;
- for(i=0; i<iterate; i++)
- {
- pDest[i] = val;
- }
+ int *pDest;
+ int i, iterate;
+
+ if (!use_custom_memset)
+ {
+ Com_Memset(dest,val,count);
+ return;
+ }
+ iterate = count / sizeof(int);
+ pDest = (int*)dest;
+ for(i=0; i<iterate; i++)
+ {
+ pDest[i] = val;
+ }
}
/* The audio callback. All the magic happens here. */
static int dmapos = 0;
static int dmasize = 0;
+
+/*
+===============
+sdl_audio_callback
+===============
+*/
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
{
- int pos = (dmapos * (dma.samplebits/8));
- if (pos >= dmasize)
- dmapos = pos = 0;
-
- if (!snd_inited) /* shouldn't happen, but just in case... */
- {
- memset(stream, '\0', len);
- return;
- }
- else
- {
- int tobufend = dmasize - pos; /* bytes to buffer's end. */
- int len1 = len;
- int len2 = 0;
-
- if (len1 > tobufend)
- {
- len1 = tobufend;
- len2 = len - len1;
- }
- memcpy(stream, dma.buffer + pos, len1);
- if (len2 <= 0)
- dmapos += (len1 / (dma.samplebits/8));
- else /* wraparound? */
- {
- memcpy(stream+len1, dma.buffer, len2);
- dmapos = (len2 / (dma.samplebits/8));
- }
- }
-
- if (dmapos >= dmasize)
- dmapos = 0;
+ int pos = (dmapos * (dma.samplebits/8));
+ if (pos >= dmasize)
+ dmapos = pos = 0;
+
+ if (!snd_inited) /* shouldn't happen, but just in case... */
+ {
+ memset(stream, '\0', len);
+ return;
+ }
+ else
+ {
+ int tobufend = dmasize - pos; /* bytes to buffer's end. */
+ int len1 = len;
+ int len2 = 0;
+
+ if (len1 > tobufend)
+ {
+ len1 = tobufend;
+ len2 = len - len1;
+ }
+ memcpy(stream, dma.buffer + pos, len1);
+ if (len2 <= 0)
+ dmapos += (len1 / (dma.samplebits/8));
+ else /* wraparound? */
+ {
+ memcpy(stream+len1, dma.buffer, len2);
+ dmapos = (len2 / (dma.samplebits/8));
+ }
+ }
+
+ if (dmapos >= dmasize)
+ dmapos = 0;
}
+static struct
+{
+ Uint16 enumFormat;
+ char *stringFormat;
+} formatToStringTable[ ] =
+{
+ { AUDIO_U8, "AUDIO_U8" },
+ { AUDIO_S8, "AUDIO_S8" },
+ { AUDIO_U16LSB, "AUDIO_U16LSB" },
+ { AUDIO_S16LSB, "AUDIO_S16LSB" },
+ { AUDIO_U16MSB, "AUDIO_U16MSB" },
+ { AUDIO_S16MSB, "AUDIO_S16MSB" }
+};
+
+static int formatToStringTableSize =
+ sizeof( formatToStringTable ) / sizeof( formatToStringTable[ 0 ] );
+
+/*
+===============
+print_audiospec
+===============
+*/
static void print_audiospec(const char *str, const SDL_AudioSpec *spec)
{
- Com_Printf("%s:\n", str);
-
- // I'm sorry this is nasty.
- #define PRINT_AUDIO_FMT(x) \
- if (spec->format == x) Com_Printf("Format: %s\n", #x); else
- PRINT_AUDIO_FMT(AUDIO_U8)
- PRINT_AUDIO_FMT(AUDIO_S8)
- PRINT_AUDIO_FMT(AUDIO_U16LSB)
- PRINT_AUDIO_FMT(AUDIO_S16LSB)
- PRINT_AUDIO_FMT(AUDIO_U16MSB)
- PRINT_AUDIO_FMT(AUDIO_S16MSB)
- Com_Printf("Format: UNKNOWN\n");
- #undef PRINT_AUDIO_FMT
-
- Com_Printf("Freq: %d\n", (int) spec->freq);
- Com_Printf("Samples: %d\n", (int) spec->samples);
- Com_Printf("Channels: %d\n", (int) spec->channels);
- Com_Printf("\n");
+ int i;
+ char *fmt = NULL;
+
+ Com_Printf("%s:\n", str);
+
+ for( i = 0; i < formatToStringTableSize; i++ ) {
+ if( spec->format == formatToStringTable[ i ].enumFormat ) {
+ fmt = formatToStringTable[ i ].stringFormat;
+ }
+ }
+
+ if( fmt ) {
+ Com_Printf( " Format: %s\n", fmt );
+ } else {
+ Com_Printf( " Format: " S_COLOR_RED "UNKNOWN\n", fmt );
+ }
+
+ Com_Printf( " Freq: %d\n", (int) spec->freq );
+ Com_Printf( " Samples: %d\n", (int) spec->samples );
+ Com_Printf( " Channels: %d\n", (int) spec->channels );
}
+/*
+===============
+SNDDMA_Init
+===============
+*/
qboolean SNDDMA_Init(void)
{
- char drivername[128];
- SDL_AudioSpec desired;
- SDL_AudioSpec obtained;
+ char drivername[128];
+ SDL_AudioSpec desired;
+ SDL_AudioSpec obtained;
int tmp;
if (snd_inited)
- return 1;
+ return qtrue;
- Com_Printf("SDL Audio driver initializing...\n");
+ Com_Printf("Initializing SDL audio driver...\n");
- if (!snddevice) {
- sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
- sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
- sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
- snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE);
- sdldevsamps = Cvar_Get("sdldevsamps", "0", CVAR_ARCHIVE);
- sdlmixsamps = Cvar_Get("sdlmixsamps", "0", CVAR_ARCHIVE);
+ if (!s_sdlBits) {
+ s_sdlBits = Cvar_Get("s_sdlBits", "16", CVAR_ARCHIVE);
+ s_sdlSpeed = Cvar_Get("s_sdlSpeed", "0", CVAR_ARCHIVE);
+ s_sdlChannels = Cvar_Get("s_sdlChannels", "2", CVAR_ARCHIVE);
+ s_sdlDevSamps = Cvar_Get("s_sdlDevSamps", "0", CVAR_ARCHIVE);
+ s_sdlMixSamps = Cvar_Get("s_sdlMixSamps", "0", CVAR_ARCHIVE);
}
- if (!SDL_WasInit(SDL_INIT_AUDIO))
- {
- Com_Printf("Calling SDL_Init(SDL_INIT_AUDIO)...\n");
- if (SDL_Init(SDL_INIT_AUDIO) == -1)
- {
- Com_Printf("SDL_Init(SDL_INIT_AUDIO) failed: %s\n", SDL_GetError());
- return qfalse;
- }
- Com_Printf("SDL_Init(SDL_INIT_AUDIO) passed.\n");
- }
-
- if (SDL_AudioDriverName(drivername, sizeof (drivername)) == NULL)
- strcpy(drivername, "(UNKNOWN)");
- Com_Printf("SDL audio driver is \"%s\"\n", drivername);
-
- memset(&desired, '\0', sizeof (desired));
- memset(&obtained, '\0', sizeof (obtained));
-
- tmp = ((int) sndbits->value);
- if ((tmp != 16) && (tmp != 8))
- tmp = 16;
-
- desired.freq = (int) sndspeed->value;
- if(!desired.freq) desired.freq = 22050;
- desired.format = ((tmp == 16) ? AUDIO_S16SYS : AUDIO_U8);
-
- // I dunno if this is the best idea, but I'll give it a try...
- // should probably check a cvar for this...
- if (sdldevsamps->value)
- desired.samples = sdldevsamps->value;
- else
- {
- // just pick a sane default.
- if (desired.freq <= 11025)
- desired.samples = 256;
- else if (desired.freq <= 22050)
- desired.samples = 512;
- else if (desired.freq <= 44100)
- desired.samples = 1024;
- else
- desired.samples = 2048; // (*shrug*)
- }
-
- desired.channels = (int) sndchannels->value;
- desired.callback = sdl_audio_callback;
-
- print_audiospec("Format we requested from SDL audio device", &desired);
-
- if (SDL_OpenAudio(&desired, &obtained) == -1)
- {
- Com_Printf("SDL_OpenAudio() failed: %s\n", SDL_GetError());
- SDL_QuitSubSystem(SDL_INIT_AUDIO);
- return qfalse;
- } // if
-
- print_audiospec("Format we actually got", &obtained);
-
- // dma.samples needs to be big, or id's mixer will just refuse to
- // work at all; we need to keep it significantly bigger than the
- // amount of SDL callback samples, and just copy a little each time
- // the callback runs.
- // 32768 is what the OSS driver filled in here on my system. I don't
- // know if it's a good value overall, but at least we know it's
- // reasonable...this is why I let the user override.
- tmp = sdlmixsamps->value;
- if (!tmp)
- tmp = (obtained.samples * obtained.channels) * 10;
-
- if (tmp & (tmp - 1)) // not a power of two? Seems to confuse something.
- {
- int val = 1;
- while (val < tmp)
- val <<= 1;
-
- Com_Printf("WARNING: sdlmixsamps wasn't a power of two (%d),"
- " so we made it one (%d).\n", tmp, val);
- tmp = val;
- }
-
- dmapos = 0;
+ if (!SDL_WasInit(SDL_INIT_AUDIO))
+ {
+ if (SDL_Init(SDL_INIT_AUDIO) == -1)
+ {
+ Com_Printf("SDL_Init(SDL_INIT_AUDIO) failed: %s\n", SDL_GetError());
+ return qfalse;
+ }
+ }
+
+ if (SDL_AudioDriverName(drivername, sizeof (drivername)) == NULL)
+ strcpy(drivername, "(UNKNOWN)");
+ Com_Printf("SDL audio driver is \"%s\".\n", drivername);
+
+ memset(&desired, '\0', sizeof (desired));
+ memset(&obtained, '\0', sizeof (obtained));
+
+ tmp = ((int) s_sdlBits->value);
+ if ((tmp != 16) && (tmp != 8))
+ tmp = 16;
+
+ desired.freq = (int) s_sdlSpeed->value;
+ if(!desired.freq) desired.freq = 22050;
+ desired.format = ((tmp == 16) ? AUDIO_S16SYS : AUDIO_U8);
+
+ // I dunno if this is the best idea, but I'll give it a try...
+ // should probably check a cvar for this...
+ if (s_sdlDevSamps->value)
+ desired.samples = s_sdlDevSamps->value;
+ else
+ {
+ // just pick a sane default.
+ if (desired.freq <= 11025)
+ desired.samples = 256;
+ else if (desired.freq <= 22050)
+ desired.samples = 512;
+ else if (desired.freq <= 44100)
+ desired.samples = 1024;
+ else
+ desired.samples = 2048; // (*shrug*)
+ }
+
+ desired.channels = (int) s_sdlChannels->value;
+ desired.callback = sdl_audio_callback;
+
+ if (SDL_OpenAudio(&desired, &obtained) == -1)
+ {
+ Com_Printf("SDL_OpenAudio() failed: %s\n", SDL_GetError());
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ return qfalse;
+ } // if
+
+ print_audiospec("SDL_AudioSpec", &obtained);
+
+ // dma.samples needs to be big, or id's mixer will just refuse to
+ // work at all; we need to keep it significantly bigger than the
+ // amount of SDL callback samples, and just copy a little each time
+ // the callback runs.
+ // 32768 is what the OSS driver filled in here on my system. I don't
+ // know if it's a good value overall, but at least we know it's
+ // reasonable...this is why I let the user override.
+ tmp = s_sdlMixSamps->value;
+ if (!tmp)
+ tmp = (obtained.samples * obtained.channels) * 10;
+
+ if (tmp & (tmp - 1)) // not a power of two? Seems to confuse something.
+ {
+ int val = 1;
+ while (val < tmp)
+ val <<= 1;
+
+ tmp = val;
+ }
+
+ dmapos = 0;
dma.samplebits = obtained.format & 0xFF; // first byte of format is bits.
- dma.channels = obtained.channels;
- dma.samples = tmp;
+ dma.channels = obtained.channels;
+ dma.samples = tmp;
dma.submission_chunk = 1;
dma.speed = obtained.freq;
- dmasize = (dma.samples * (dma.samplebits/8));
+ dmasize = (dma.samples * (dma.samplebits/8));
dma.buffer = calloc(1, dmasize);
- Com_Printf("Starting SDL audio callback...\n");
- SDL_PauseAudio(0); // start callback.
+ Com_Printf("Starting SDL audio callback...\n");
+ SDL_PauseAudio(0); // start callback.
- Com_Printf("SDL audio initialized.\n");
- snd_inited = 1;
+ Com_Printf("SDL audio initialized.\n");
+ snd_inited = qtrue;
return qtrue;
}
+/*
+===============
+SNDDMA_GetDMAPos
+===============
+*/
int SNDDMA_GetDMAPos(void)
{
- return dmapos;
+ return dmapos;
}
+/*
+===============
+SNDDMA_Shutdown
+===============
+*/
void SNDDMA_Shutdown(void)
{
- Com_Printf("Closing SDL audio device...\n");
- SDL_PauseAudio(1);
- SDL_CloseAudio();
- SDL_QuitSubSystem(SDL_INIT_AUDIO);
- free(dma.buffer);
- dma.buffer = NULL;
- dmapos = dmasize = 0;
- snd_inited = 0;
- Com_Printf("SDL audio device shut down.\n");
+ Com_Printf("Closing SDL audio device...\n");
+ SDL_PauseAudio(1);
+ SDL_CloseAudio();
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ free(dma.buffer);
+ dma.buffer = NULL;
+ dmapos = dmasize = 0;
+ snd_inited = qfalse;
+ Com_Printf("SDL audio device shut down.\n");
}
/*
-==============
+===============
SNDDMA_Submit
Send sound to device if buffer isn't really the dma buffer
@@ -290,15 +342,17 @@ Send sound to device if buffer isn't really the dma buffer
*/
void SNDDMA_Submit(void)
{
- SDL_UnlockAudio();
+ SDL_UnlockAudio();
}
+/*
+===============
+SNDDMA_BeginPainting
+===============
+*/
void SNDDMA_BeginPainting (void)
{
- SDL_LockAudio();
+ SDL_LockAudio();
}
#endif // USE_SDL_SOUND
-
-// end of linux_snd_sdl.c ...
-
diff --git a/i_o-q3-readme b/i_o-q3-readme
index 4da93b9..9ba1ae6 100644
--- a/i_o-q3-readme
+++ b/i_o-q3-readme
@@ -109,13 +109,16 @@ follows:
...and comment out/remove the duplicated code marked by '>'.
-4. If you didn't install the DirectX SDK in C:\DXSDK\, edit DXSDK_DIR in
- code/unix/Makefile to reflect the new location.
-5. Perform the usual precompilation sacrificial ritual.
-6. Open an MSys terminal, and follow the instructions for compiling on Linux.
-7. Steal underpants
-8. ????
-9. Profit!
+4. If you didn't install the DirectX SDK in C:\DXSDK\, add DXSDK_DIR to
+ code/unix/Makefile.local to reflect the new location.
+5. If you want OpenAL support, aquire some OpenAL headers and put them in
+ /include/AL/ beneath your MinGW dir. If not, add "USE_OPENAL=0" to
+ code/unix/Makefile.local.
+6. Perform the usual precompilation sacrificial ritual.
+7. Open an MSys terminal, and follow the instructions for compiling on Linux.
+8. Steal underpants
+9. ?????
+10. Profit!
Creating mods compatible with Q3 1.32b
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~