From 84c4f210824f2fdd5f4153685d18b038213c7358 Mon Sep 17 00:00:00 2001 From: Tim Angus Date: Sun, 13 Nov 2005 18:58:14 +0000 Subject: [PATCH] * OpenAL support, from BlackAura aka Stuart Dalton + 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 --- code/client/qal.c | 344 +++++ code/client/qal.h | 134 ++ code/client/snd_codec.c | 226 +++ code/client/snd_codec.h | 97 ++ code/client/snd_codec_wav.c | 307 ++++ code/client/snd_dma.c | 531 +++---- code/client/snd_local.h | 51 +- code/client/snd_main.c | 433 ++++++ code/client/snd_mem.c | 153 +- code/client/snd_openal.c | 1567 +++++++++++++++++++++ code/macosx/Quake3.pbproj/project.pbxproj | 161 +++ code/unix/Makefile | 27 + code/unix/sdl_snd.c | 468 +++--- i_o-q3-readme | 17 +- 14 files changed, 3813 insertions(+), 703 deletions(-) create mode 100644 code/client/qal.c create mode 100644 code/client/qal.h create mode 100644 code/client/snd_codec.c create mode 100644 code/client/snd_codec.h create mode 100644 code/client/snd_codec_wav.c create mode 100644 code/client/snd_main.c create mode 100644 code/client/snd_openal.c diff --git a/code/client/qal.c b/code/client/qal.c new file mode 100644 index 00000000..a01c9155 --- /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 +#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 +#include +#include +#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 00000000..b3592db4 --- /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 +#include + +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 00000000..de35255e --- /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 00000000..75f35b34 --- /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 00000000..bc9c00c3 --- /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 + // 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 e807d907..39777034 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; +void S_Base_SoundList( void ) { + int i; + sfx_t *sfx; + int size, total; + char type[4][16]; + char mem[2][16]; - 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; + 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 ; isoundLength; + total += size; + Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], + sfx->soundName, mem[sfx->inMemory] ); } - - 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 (); - - S_SoundInfo_f(); - } - + 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,32 +301,11 @@ 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 @@ -385,7 +313,7 @@ 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 [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 ; isoundLength; - 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; + if(s_backgroundStream) + { + S_CodecCloseStream(s_backgroundStream); + s_backgroundStream = NULL; } - // - // 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 ); + // 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; } - // 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; - 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 ); } - - // 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_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 49fcbac4..3b45a997 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 00000000..cbb87f5c --- /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 [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 8f994ba7..77b370dd 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" @@ -99,137 +100,6 @@ void SND_setup(void) { Com_Printf("Sound memory manager started\n"); } -/* -=============================================================================== - -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 ) { + data = S_CodecLoad(sfx->soundName, &info); + 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); - 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 00000000..16a8bf97 --- /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 12c8dcda..955c91ac 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 = ""; + }; + 92847F3609279B370056BC59 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + path = qal.h; + refType = 4; + sourceTree = ""; + }; + 92847F3709279B370056BC59 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + path = snd_codec_wav.c; + refType = 4; + sourceTree = ""; + }; + 92847F3809279B370056BC59 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + path = snd_codec.c; + refType = 4; + sourceTree = ""; + }; + 92847F3909279B370056BC59 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + path = snd_codec.h; + refType = 4; + sourceTree = ""; + }; + 92847F3A09279B370056BC59 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + path = snd_main.c; + refType = 4; + sourceTree = ""; + }; + 92847F3B09279B370056BC59 = { + fileEncoding = 30; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + path = snd_openal.c; + refType = 4; + sourceTree = ""; + }; + 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 43083d11..4d2ab9b7 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 3caf85aa..ac4a614d 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 + + some shitty mess with DMA buffers + the mmap'ing permissions were write only + and glibc optimized for mmx would do memcpy with a prefetch and a read + causing segfaults + some other systems would not let you mmap the DMA with read permissions + so I think I ended up attempting opening with read/write, then try write only + 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; + 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= dmasize) - dmapos = pos = 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 (!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 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"); -} - -qboolean SNDDMA_Init(void) -{ - char drivername[128]; - SDL_AudioSpec desired; - SDL_AudioSpec obtained; - int tmp; - - if (snd_inited) - return 1; - - Com_Printf("SDL Audio driver initializing...\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 (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 (!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; - dma.samplebits = obtained.format & 0xFF; // first byte of format is bits. - dma.channels = obtained.channels; - dma.samples = tmp; - dma.submission_chunk = 1; - dma.speed = obtained.freq; - 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("SDL audio initialized.\n"); - snd_inited = 1; - return qtrue; + if (dmapos >= dmasize) + dmapos = 0; } -int SNDDMA_GetDMAPos(void) +static struct { - return dmapos; -} + 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" } +}; -void SNDDMA_Shutdown(void) +static int formatToStringTableSize = + sizeof( formatToStringTable ) / sizeof( formatToStringTable[ 0 ] ); + +/* +=============== +print_audiospec +=============== +*/ +static void print_audiospec(const char *str, const SDL_AudioSpec *spec) { - 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"); + 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; + int tmp; + + if (snd_inited) + return qtrue; + + Com_Printf("Initializing SDL audio driver...\n"); + + 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)) + { + 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.submission_chunk = 1; + dma.speed = obtained.freq; + 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("SDL audio initialized.\n"); + snd_inited = qtrue; + return qtrue; +} + +/* +=============== +SNDDMA_GetDMAPos +=============== +*/ +int SNDDMA_GetDMAPos(void) +{ + 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 = 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 4da93b98..9ba1ae64 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~