* OpenAL support, from BlackAura aka Stuart Dalton <badcdev@gmail.com>

+ An abstract codec system, simplifying support for new formats
  + Changes versus BlackAura's patch:
    o Consolidated the OpenAL parts into one file
    o Changed the function naming scheme to more closely resemble Q3
    o Changed the interface to fall back on the "base" sound system
      if loading OpenAL fails
  + This is enabled on Linux and MinGW for now, but should work on the
    other *nixs with appropriate additions to the Makefile
  + NOT enabled on OS X or MSVC Windows builds
  + Probably breaks the Windows build again
* Retabulated sdl_snd.c and made the messages less verbose since
  there do not seem to be many having problems with SDL sound now
This commit is contained in:
Tim Angus 2005-11-13 18:58:14 +00:00
parent 79ceef93cc
commit 84c4f21082
14 changed files with 3813 additions and 703 deletions

344
code/client/qal.c Normal file
View file

@ -0,0 +1,344 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// Dynamically loads OpenAL
#if USE_OPENAL
#include "qal.h"
#if defined _WIN32
#include <windows.h>
#define OBJTYPE HMODULE
#define OBJLOAD(x) LoadLibrary(x)
#define SYMLOAD(x,y) GetProcAddress(x,y)
#define OBJFREE(x) FreeLibrary(x)
#elif defined __linux__ || defined __FreeBSD__
#include <unistd.h>
#include <sys/types.h>
#include <dlfcn.h>
#define OBJTYPE void *
#define OBJLOAD(x) dlopen(x, RTLD_LAZY | RTLD_GLOBAL)
#define SYMLOAD(x,y) dlsym(x,y)
#define OBJFREE(x) dlclose(x)
#else
#error "Your platform has no lib loading code or it is disabled"
#endif
LPALENABLE qalEnable;
LPALDISABLE qalDisable;
LPALISENABLED qalIsEnabled;
LPALGETSTRING qalGetString;
LPALGETBOOLEANV qalGetBooleanv;
LPALGETINTEGERV qalGetIntegerv;
LPALGETFLOATV qalGetFloatv;
LPALGETDOUBLEV qalGetDoublev;
LPALGETBOOLEAN qalGetBoolean;
LPALGETINTEGER qalGetInteger;
LPALGETFLOAT qalGetFloat;
LPALGETDOUBLE qalGetDouble;
LPALGETERROR qalGetError;
LPALISEXTENSIONPRESENT qalIsExtensionPresent;
LPALGETPROCADDRESS qalGetProcAddress;
LPALGETENUMVALUE qalGetEnumValue;
LPALLISTENERF qalListenerf;
LPALLISTENER3F qalListener3f;
LPALLISTENERFV qalListenerfv;
LPALLISTENERI qalListeneri;
LPALGETLISTENERF qalGetListenerf;
LPALGETLISTENER3F qalGetListener3f;
LPALGETLISTENERFV qalGetListenerfv;
LPALGETLISTENERI qalGetListeneri;
LPALGENSOURCES qalGenSources;
LPALDELETESOURCES qalDeleteSources;
LPALISSOURCE qalIsSource;
LPALSOURCEF qalSourcef;
LPALSOURCE3F qalSource3f;
LPALSOURCEFV qalSourcefv;
LPALSOURCEI qalSourcei;
LPALGETSOURCEF qalGetSourcef;
LPALGETSOURCE3F qalGetSource3f;
LPALGETSOURCEFV qalGetSourcefv;
LPALGETSOURCEI qalGetSourcei;
LPALSOURCEPLAYV qalSourcePlayv;
LPALSOURCESTOPV qalSourceStopv;
LPALSOURCEREWINDV qalSourceRewindv;
LPALSOURCEPAUSEV qalSourcePausev;
LPALSOURCEPLAY qalSourcePlay;
LPALSOURCESTOP qalSourceStop;
LPALSOURCEREWIND qalSourceRewind;
LPALSOURCEPAUSE qalSourcePause;
LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
LPALGENBUFFERS qalGenBuffers;
LPALDELETEBUFFERS qalDeleteBuffers;
LPALISBUFFER qalIsBuffer;
LPALBUFFERDATA qalBufferData;
LPALGETBUFFERF qalGetBufferf;
LPALGETBUFFERI qalGetBufferi;
LPALDOPPLERFACTOR qalDopplerFactor;
LPALDOPPLERVELOCITY qalDopplerVelocity;
LPALDISTANCEMODEL qalDistanceModel;
LPALCCREATECONTEXT qalcCreateContext;
LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent;
LPALCPROCESSCONTEXT qalcProcessContext;
LPALCSUSPENDCONTEXT qalcSuspendContext;
LPALCDESTROYCONTEXT qalcDestroyContext;
LPALCGETCURRENTCONTEXT qalcGetCurrentContext;
LPALCGETCONTEXTSDEVICE qalcGetContextsDevice;
LPALCOPENDEVICE qalcOpenDevice;
LPALCCLOSEDEVICE qalcCloseDevice;
LPALCGETERROR qalcGetError;
LPALCISEXTENSIONPRESENT qalcIsExtensionPresent;
LPALCGETPROCADDRESS qalcGetProcAddress;
LPALCGETENUMVALUE qalcGetEnumValue;
LPALCGETSTRING qalcGetString;
LPALCGETINTEGERV qalcGetIntegerv;
static OBJTYPE OpenALLib = NULL;
static qboolean alinit_fail = qfalse;
/*
=================
GPA
=================
*/
static void *GPA(char *str)
{
void *rv;
rv = SYMLOAD(OpenALLib, str);
if(!rv)
{
Com_Printf( " Can't load symbol %s\n", str);
alinit_fail = qtrue;
return NULL;
}
else
{
Com_DPrintf( " Loaded symbol %s (0x%08X)\n", str, rv);
return rv;
}
}
/*
=================
QAL_Init
=================
*/
qboolean QAL_Init(const char *libname)
{
if(OpenALLib)
return qtrue;
Com_Printf( "Loading \"%s\"...\n", libname);
if( (OpenALLib = OBJLOAD(libname)) == 0 )
{
#ifdef _WIN32
return qfalse;
#else
char fn[1024];
getcwd(fn, sizeof(fn));
strncat(fn, "/", sizeof(fn));
strncat(fn, libname, sizeof(fn));
if( (OpenALLib = OBJLOAD(fn)) == 0 )
{
return qfalse;
}
#endif
}
alinit_fail = qfalse;
qalEnable = GPA("alEnable");
qalDisable = GPA("alDisable");
qalIsEnabled = GPA("alIsEnabled");
qalGetString = GPA("alGetString");
qalGetBooleanv = GPA("alGetBooleanv");
qalGetIntegerv = GPA("alGetIntegerv");
qalGetFloatv = GPA("alGetFloatv");
qalGetDoublev = GPA("alGetDoublev");
qalGetBoolean = GPA("alGetBoolean");
qalGetInteger = GPA("alGetInteger");
qalGetFloat = GPA("alGetFloat");
qalGetDouble = GPA("alGetDouble");
qalGetError = GPA("alGetError");
qalIsExtensionPresent = GPA("alIsExtensionPresent");
qalGetProcAddress = GPA("alGetProcAddress");
qalGetEnumValue = GPA("alGetEnumValue");
qalListenerf = GPA("alListenerf");
qalListener3f = GPA("alListener3f");
qalListenerfv = GPA("alListenerfv");
qalListeneri = GPA("alListeneri");
qalGetListenerf = GPA("alGetListenerf");
qalGetListener3f = GPA("alGetListener3f");
qalGetListenerfv = GPA("alGetListenerfv");
qalGetListeneri = GPA("alGetListeneri");
qalGenSources = GPA("alGenSources");
qalDeleteSources = GPA("alDeleteSources");
qalIsSource = GPA("alIsSource");
qalSourcef = GPA("alSourcef");
qalSource3f = GPA("alSource3f");
qalSourcefv = GPA("alSourcefv");
qalSourcei = GPA("alSourcei");
qalGetSourcef = GPA("alGetSourcef");
qalGetSource3f = GPA("alGetSource3f");
qalGetSourcefv = GPA("alGetSourcefv");
qalGetSourcei = GPA("alGetSourcei");
qalSourcePlayv = GPA("alSourcePlayv");
qalSourceStopv = GPA("alSourceStopv");
qalSourceRewindv = GPA("alSourceRewindv");
qalSourcePausev = GPA("alSourcePausev");
qalSourcePlay = GPA("alSourcePlay");
qalSourceStop = GPA("alSourceStop");
qalSourceRewind = GPA("alSourceRewind");
qalSourcePause = GPA("alSourcePause");
qalSourceQueueBuffers = GPA("alSourceQueueBuffers");
qalSourceUnqueueBuffers = GPA("alSourceUnqueueBuffers");
qalGenBuffers = GPA("alGenBuffers");
qalDeleteBuffers = GPA("alDeleteBuffers");
qalIsBuffer = GPA("alIsBuffer");
qalBufferData = GPA("alBufferData");
qalGetBufferf = GPA("alGetBufferf");
qalGetBufferi = GPA("alGetBufferi");
qalDopplerFactor = GPA("alDopplerFactor");
qalDopplerVelocity = GPA("alDopplerVelocity");
qalDistanceModel = GPA("alDistanceModel");
qalcCreateContext = GPA("alcCreateContext");
qalcMakeContextCurrent = GPA("alcMakeContextCurrent");
qalcProcessContext = GPA("alcProcessContext");
qalcSuspendContext = GPA("alcSuspendContext");
qalcDestroyContext = GPA("alcDestroyContext");
qalcGetCurrentContext = GPA("alcGetCurrentContext");
qalcGetContextsDevice = GPA("alcGetContextsDevice");
qalcOpenDevice = GPA("alcOpenDevice");
qalcCloseDevice = GPA("alcCloseDevice");
qalcGetError = GPA("alcGetError");
qalcIsExtensionPresent = GPA("alcIsExtensionPresent");
qalcGetProcAddress = GPA("alcGetProcAddress");
qalcGetEnumValue = GPA("alcGetEnumValue");
qalcGetString = GPA("alcGetString");
qalcGetIntegerv = GPA("alcGetIntegerv");
if(alinit_fail)
{
QAL_Shutdown();
Com_Printf( " One or more symbols not found\n");
return qfalse;
}
return qtrue;
}
/*
=================
QAL_Shutdown
=================
*/
void QAL_Shutdown( void )
{
if(OpenALLib)
{
OBJFREE(OpenALLib);
OpenALLib = NULL;
}
qalEnable = NULL;
qalDisable = NULL;
qalIsEnabled = NULL;
qalGetString = NULL;
qalGetBooleanv = NULL;
qalGetIntegerv = NULL;
qalGetFloatv = NULL;
qalGetDoublev = NULL;
qalGetBoolean = NULL;
qalGetInteger = NULL;
qalGetFloat = NULL;
qalGetDouble = NULL;
qalGetError = NULL;
qalIsExtensionPresent = NULL;
qalGetProcAddress = NULL;
qalGetEnumValue = NULL;
qalListenerf = NULL;
qalListener3f = NULL;
qalListenerfv = NULL;
qalListeneri = NULL;
qalGetListenerf = NULL;
qalGetListener3f = NULL;
qalGetListenerfv = NULL;
qalGetListeneri = NULL;
qalGenSources = NULL;
qalDeleteSources = NULL;
qalIsSource = NULL;
qalSourcef = NULL;
qalSource3f = NULL;
qalSourcefv = NULL;
qalSourcei = NULL;
qalGetSourcef = NULL;
qalGetSource3f = NULL;
qalGetSourcefv = NULL;
qalGetSourcei = NULL;
qalSourcePlayv = NULL;
qalSourceStopv = NULL;
qalSourceRewindv = NULL;
qalSourcePausev = NULL;
qalSourcePlay = NULL;
qalSourceStop = NULL;
qalSourceRewind = NULL;
qalSourcePause = NULL;
qalSourceQueueBuffers = NULL;
qalSourceUnqueueBuffers = NULL;
qalGenBuffers = NULL;
qalDeleteBuffers = NULL;
qalIsBuffer = NULL;
qalBufferData = NULL;
qalGetBufferf = NULL;
qalGetBufferi = NULL;
qalDopplerFactor = NULL;
qalDopplerVelocity = NULL;
qalDistanceModel = NULL;
qalcCreateContext = NULL;
qalcMakeContextCurrent = NULL;
qalcProcessContext = NULL;
qalcSuspendContext = NULL;
qalcDestroyContext = NULL;
qalcGetCurrentContext = NULL;
qalcGetContextsDevice = NULL;
qalcOpenDevice = NULL;
qalcCloseDevice = NULL;
qalcGetError = NULL;
qalcIsExtensionPresent = NULL;
qalcGetProcAddress = NULL;
qalcGetEnumValue = NULL;
qalcGetString = NULL;
qalcGetIntegerv = NULL;
}
#endif

134
code/client/qal.h Normal file
View file

@ -0,0 +1,134 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#ifndef __QAL_H__
#define __QAL_H__
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#define AL_NO_PROTOTYPES
#define ALC_NO_PROTOTYPES
#include <AL/al.h>
#include <AL/alc.h>
extern LPALENABLE qalEnable;
extern LPALDISABLE qalDisable;
extern LPALISENABLED qalIsEnabled;
extern LPALGETSTRING qalGetString;
extern LPALGETBOOLEANV qalGetBooleanv;
extern LPALGETINTEGERV qalGetIntegerv;
extern LPALGETFLOATV qalGetFloatv;
extern LPALGETDOUBLEV qalGetDoublev;
extern LPALGETBOOLEAN qalGetBoolean;
extern LPALGETINTEGER qalGetInteger;
extern LPALGETFLOAT qalGetFloat;
extern LPALGETDOUBLE qalGetDouble;
extern LPALGETERROR qalGetError;
extern LPALISEXTENSIONPRESENT qalIsExtensionPresent;
extern LPALGETPROCADDRESS qalGetProcAddress;
extern LPALGETENUMVALUE qalGetEnumValue;
extern LPALLISTENERF qalListenerf;
extern LPALLISTENER3F qalListener3f;
extern LPALLISTENERFV qalListenerfv;
extern LPALLISTENERI qalListeneri;
extern LPALLISTENER3I qalListener3i;
extern LPALLISTENERIV qalListeneriv;
extern LPALGETLISTENERF qalGetListenerf;
extern LPALGETLISTENER3F qalGetListener3f;
extern LPALGETLISTENERFV qalGetListenerfv;
extern LPALGETLISTENERI qalGetListeneri;
extern LPALGETLISTENER3I qalGetListener3i;
extern LPALGETLISTENERIV qalGetListeneriv;
extern LPALGENSOURCES qalGenSources;
extern LPALDELETESOURCES qalDeleteSources;
extern LPALISSOURCE qalIsSource;
extern LPALSOURCEF qalSourcef;
extern LPALSOURCE3F qalSource3f;
extern LPALSOURCEFV qalSourcefv;
extern LPALSOURCEI qalSourcei;
extern LPALSOURCE3I qalSource3i;
extern LPALSOURCEIV qalSourceiv;
extern LPALGETSOURCEF qalGetSourcef;
extern LPALGETSOURCE3F qalGetSource3f;
extern LPALGETSOURCEFV qalGetSourcefv;
extern LPALGETSOURCEI qalGetSourcei;
extern LPALGETSOURCE3I qalGetSource3i;
extern LPALGETSOURCEIV qalGetSourceiv;
extern LPALSOURCEPLAYV qalSourcePlayv;
extern LPALSOURCESTOPV qalSourceStopv;
extern LPALSOURCEREWINDV qalSourceRewindv;
extern LPALSOURCEPAUSEV qalSourcePausev;
extern LPALSOURCEPLAY qalSourcePlay;
extern LPALSOURCESTOP qalSourceStop;
extern LPALSOURCEREWIND qalSourceRewind;
extern LPALSOURCEPAUSE qalSourcePause;
extern LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
extern LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
extern LPALGENBUFFERS qalGenBuffers;
extern LPALDELETEBUFFERS qalDeleteBuffers;
extern LPALISBUFFER qalIsBuffer;
extern LPALBUFFERDATA qalBufferData;
extern LPALBUFFERF qalBufferf;
extern LPALBUFFER3F qalBuffer3f;
extern LPALBUFFERFV qalBufferfv;
extern LPALBUFFERF qalBufferi;
extern LPALBUFFER3F qalBuffer3i;
extern LPALBUFFERFV qalBufferiv;
extern LPALGETBUFFERF qalGetBufferf;
extern LPALGETBUFFER3F qalGetBuffer3f;
extern LPALGETBUFFERFV qalGetBufferfv;
extern LPALGETBUFFERI qalGetBufferi;
extern LPALGETBUFFER3I qalGetBuffer3i;
extern LPALGETBUFFERIV qalGetBufferiv;
extern LPALDOPPLERFACTOR qalDopplerFactor;
extern LPALDOPPLERVELOCITY qalDopplerVelocity;
extern LPALSPEEDOFSOUND qalSpeedOfSound;
extern LPALDISTANCEMODEL qalDistanceModel;
extern LPALCCREATECONTEXT qalcCreateContext;
extern LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent;
extern LPALCPROCESSCONTEXT qalcProcessContext;
extern LPALCSUSPENDCONTEXT qalcSuspendContext;
extern LPALCDESTROYCONTEXT qalcDestroyContext;
extern LPALCGETCURRENTCONTEXT qalcGetCurrentContext;
extern LPALCGETCONTEXTSDEVICE qalcGetContextsDevice;
extern LPALCOPENDEVICE qalcOpenDevice;
extern LPALCCLOSEDEVICE qalcCloseDevice;
extern LPALCGETERROR qalcGetError;
extern LPALCISEXTENSIONPRESENT qalcIsExtensionPresent;
extern LPALCGETPROCADDRESS qalcGetProcAddress;
extern LPALCGETENUMVALUE qalcGetEnumValue;
extern LPALCGETSTRING qalcGetString;
extern LPALCGETINTEGERV qalcGetIntegerv;
extern LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice;
extern LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice;
extern LPALCCAPTURESTART qalcCaptureStart;
extern LPALCCAPTURESTOP qalcCaptureStop;
extern LPALCCAPTURESAMPLES qalcCaptureSamples;
qboolean QAL_Init(const char *libname);
void QAL_Shutdown( void );
#endif // __QAL_H__

226
code/client/snd_codec.c Normal file
View file

@ -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);
}

97
code/client/snd_codec.h Normal file
View file

@ -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_

307
code/client/snd_codec_wav.c Normal file
View file

@ -0,0 +1,307 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "client.h"
#include "snd_codec.h"
/*
=================
FGetLittleLong
=================
*/
static int FGetLittleLong( fileHandle_t f ) {
int v;
FS_Read( &v, sizeof(v), f );
return LittleLong( v);
}
/*
=================
FGetLittleShort
=================
*/
static int FGetLittleShort( fileHandle_t f ) {
short v;
FS_Read( &v, sizeof(v), f );
return LittleShort( v);
}
/*
=================
S_ReadChunkInfo
=================
*/
static int S_ReadChunkInfo(fileHandle_t f, char *name)
{
int len, r;
name[4] = 0;
r = FS_Read(name, 4, f);
if(r != 4)
return 0;
len = FGetLittleLong(f);
if(len < 0 || len > 0xffffffff)
return 0;
//FIXME: 11/11/05 <tim@ngus.net>
// I'm not sure I understand why this needs to be padded.
// Surely this results in reading past the end of the data?
//len = (len + 1 ) & ~1; // pad to word boundary
return len;
}
/*
=================
S_SkipChunk
=================
*/
static void S_SkipChunk(fileHandle_t f, int length)
{
byte buffer[32*1024];
while(length > 0)
{
int toread = length;
if(toread > sizeof(buffer))
toread = sizeof(buffer);
FS_Read(buffer, toread, f);
length -= toread;
}
}
/*
=================
S_FindWavChunk
Returns the length of the data in the chunk, or 0 if not found
=================
*/
static int S_FindWavChunk( fileHandle_t f, char *chunk ) {
char name[5];
int len;
// This is a bit dangerous...
while(1)
{
len = S_ReadChunkInfo(f, name);
// Read failure?
if(len == 0)
return 0;
// If this is the right chunk, return
if(!strcmp(name, chunk))
return len;
// Not the right chunk - skip it
S_SkipChunk(f, len);
}
}
/*
=================
S_ByteSwapRawSamples
=================
*/
static void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
int i;
if ( width != 2 ) {
return;
}
if ( LittleShort( 256 ) == 256 ) {
return;
}
if ( s_channels == 2 ) {
samples <<= 1;
}
for ( i = 0 ; i < samples ; i++ ) {
((short *)data)[i] = LittleShort( ((short *)data)[i] );
}
}
/*
=================
S_ReadWavHeader
=================
*/
static qboolean S_ReadWavHeader(fileHandle_t file, snd_info_t *info)
{
char dump[16];
int wav_format;
int fmtlen = 0;
// skip the riff wav header
FS_Read(dump, 12, file);
// Scan for the format chunk
if((fmtlen = S_FindWavChunk(file, "fmt ")) == 0)
{
Com_Printf("No fmt chunk\n");
return qfalse;
}
// Save the parameters
wav_format = FGetLittleShort(file);
info->channels = FGetLittleShort(file);
info->rate = FGetLittleLong(file);
FGetLittleLong(file);
FGetLittleShort(file);
info->width = FGetLittleShort(file) / 8;
info->dataofs = 0;
// Skip the rest of the format chunk if required
if(fmtlen > 16)
{
fmtlen -= 16;
S_SkipChunk(file, fmtlen);
}
// Scan for the data chunk
if( (info->size = S_FindWavChunk(file, "data")) == 0)
{
Com_Printf("No data chunk\n");
return qfalse;
}
info->samples = (info->size / info->width) / info->channels;
return qtrue;
}
// WAV codec
snd_codec_t wav_codec =
{
".wav",
S_WAV_CodecLoad,
S_WAV_CodecOpenStream,
S_WAV_CodecReadStream,
S_WAV_CodecCloseStream,
NULL
};
/*
=================
S_WAV_CodecLoad
=================
*/
void *S_WAV_CodecLoad(const char *filename, snd_info_t *info)
{
fileHandle_t file;
void *buffer;
// Try to open the file
FS_FOpenFileRead(filename, &file, qtrue);
if(!file)
{
Com_Printf("Can't read sound file %s\n", filename);
return NULL;
}
// Read the RIFF header
if(!S_ReadWavHeader(file, info))
{
FS_FCloseFile(file);
Com_Printf("Can't understand wav file %s\n", filename);
return NULL;
}
// Allocate some memory
buffer = Z_Malloc(info->size);
if(!buffer)
{
FS_FCloseFile(file);
Com_Printf("Out of memory reading %s\n", filename);
return NULL;
}
// Read, byteswap
FS_Read(buffer, info->size, file);
S_ByteSwapRawSamples(info->samples, info->width, info->channels, (byte *)buffer);
// Close and return
FS_FCloseFile(file);
return buffer;
}
/*
=================
S_WAV_CodecOpenStream
=================
*/
snd_stream_t *S_WAV_CodecOpenStream(const char *filename)
{
snd_stream_t *rv;
// Open
rv = S_CodecUtilOpen(filename, &wav_codec);
if(!rv)
return NULL;
// Read the RIFF header
if(!S_ReadWavHeader(rv->file, &rv->info))
{
S_CodecUtilClose(rv);
return NULL;
}
return rv;
}
/*
=================
S_WAV_CodecCloseStream
=================
*/
void S_WAV_CodecCloseStream(snd_stream_t *stream)
{
S_CodecUtilClose(stream);
}
/*
=================
S_WAV_CodecReadStream
=================
*/
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
int remaining = stream->info.size - stream->pos;
int samples;
if(remaining <= 0)
return 0;
if(bytes > remaining)
bytes = remaining;
stream->pos += bytes;
samples = (bytes / stream->info.width) / stream->info.channels;
FS_Read(buffer, bytes, stream->file);
S_ByteSwapRawSamples(samples, stream->info.width, stream->info.channels, buffer);
return bytes;
}

View file

@ -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,62 +120,35 @@ 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 ; i<s_numSfx ; i++, sfx++) {
size = sfx->soundLength;
total += size;
Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod],
sfx->soundName, mem[sfx->inMemory] );
}
Com_Printf ("Total resident: %i\n", total);
S_DisplayFreeMemory();
}
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();
}
}
void S_ChannelFree(channel_t *v) {
@ -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<Cmd_Argc() ) {
if ( !Q_strrchr(Cmd_Argv(i), '.') ) {
Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
} else {
Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
}
h = S_RegisterSound( name, qfalse );
if( h ) {
S_StartLocalSound( h, CHAN_LOCAL_SOUND );
}
i++;
}
}
void S_Music_f( void ) {
int c;
c = Cmd_Argc();
if ( c == 2 ) {
S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(1) );
s_backgroundLoop[0] = 0;
} else if ( c == 3 ) {
S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) );
} else {
Com_Printf ("music <musicfile> [loopfile]\n");
return;
}
}
void S_SoundList_f( void ) {
int i;
sfx_t *sfx;
int size, total;
char type[4][16];
char mem[2][16];
strcpy(type[0], "16bit");
strcpy(type[1], "adpcm");
strcpy(type[2], "daub4");
strcpy(type[3], "mulaw");
strcpy(mem[0], "paged out");
strcpy(mem[1], "resident ");
total = 0;
for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++) {
size = sfx->soundLength;
total += size;
Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], sfx->soundName, mem[sfx->inMemory] );
}
Com_Printf ("Total resident: %i\n", total);
S_DisplayFreeMemory();
}
/*
@ -1366,61 +1245,16 @@ background music functions
===============================================================================
*/
int FGetLittleLong( fileHandle_t f ) {
int v;
FS_Read( &v, sizeof(v), f );
return LittleLong( v);
}
int FGetLittleShort( fileHandle_t f ) {
short v;
FS_Read( &v, sizeof(v), f );
return LittleShort( v);
}
// returns the length of the data in the chunk, or 0 if not found
int S_FindWavChunk( fileHandle_t f, char *chunk ) {
char name[5];
int len;
int r;
name[4] = 0;
len = 0;
r = FS_Read( name, 4, f );
if ( r != 4 ) {
return 0;
}
len = FGetLittleLong( f );
if ( len < 0 || len > 0xfffffff ) {
len = 0;
return 0;
}
len = (len + 1 ) & ~1; // pad to word boundary
// s_nextWavChunk += len + 8;
if ( strcmp( name, chunk ) ) {
return 0;
}
return len;
}
/*
======================
S_StopBackgroundTrack
======================
*/
void S_StopBackgroundTrack( void ) {
if ( !s_backgroundFile ) {
void S_Base_StopBackgroundTrack( void ) {
if(!s_backgroundStream)
return;
}
Sys_EndStreamedFile( s_backgroundFile );
FS_FCloseFile( s_backgroundFile );
s_backgroundFile = 0;
S_CodecCloseStream(s_backgroundStream);
s_backgroundStream = NULL;
s_rawend = 0;
}
@ -1429,11 +1263,7 @@ void S_StopBackgroundTrack( void ) {
S_StartBackgroundTrack
======================
*/
void S_StartBackgroundTrack( const char *intro, const char *loop ){
int len;
char dump[16];
char name[MAX_QPATH];
void S_Base_StartBackgroundTrack( const char *intro, const char *loop ){
if ( !intro ) {
intro = "";
}
@ -1442,77 +1272,34 @@ void S_StartBackgroundTrack( const char *intro, const char *loop ){
}
Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop );
Q_strncpyz( name, intro, sizeof( name ) - 4 );
COM_DefaultExtension( name, sizeof( name ), ".wav" );
if ( !intro[0] ) {
return;
}
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 );
if(r > 0)
{
// add to raw buffer
S_RawSamples( fileSamples, s_backgroundInfo.rate,
s_backgroundInfo.width, s_backgroundInfo.channels, raw, musicVolume );
s_backgroundSamples -= fileSamples;
if ( !s_backgroundSamples ) {
// 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
S_Base_RawSamples( fileSamples, s_backgroundStream->info.rate,
s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, musicVolume );
}
} else {
s_backgroundFile = 0;
else
{
// loop
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;
}

View file

@ -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;
/*
====================================================================
@ -158,13 +183,10 @@ extern dma_t dma;
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_musicVolume;
extern cvar_t *s_doppler;
extern cvar_t *s_testsound;
extern cvar_t *s_separation;
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 );

433
code/client/snd_main.c Normal file
View file

@ -0,0 +1,433 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "client.h"
#include "snd_codec.h"
#include "snd_local.h"
#include "snd_public.h"
cvar_t *s_volume;
cvar_t *s_musicVolume;
cvar_t *s_doppler;
static soundInterface_t si;
/*
=================
S_ValidateInterface
=================
*/
static qboolean S_ValidSoundInterface( soundInterface_t *si )
{
if( !si->Shutdown ) return qfalse;
if( !si->StartSound ) return qfalse;
if( !si->StartLocalSound ) return qfalse;
if( !si->StartBackgroundTrack ) return qfalse;
if( !si->StopBackgroundTrack ) return qfalse;
if( !si->RawSamples ) return qfalse;
if( !si->StopAllSounds ) return qfalse;
if( !si->ClearLoopingSounds ) return qfalse;
if( !si->AddLoopingSound ) return qfalse;
if( !si->AddRealLoopingSound ) return qfalse;
if( !si->StopLoopingSound ) return qfalse;
if( !si->Respatialize ) return qfalse;
if( !si->UpdateEntityPosition ) return qfalse;
if( !si->Update ) return qfalse;
if( !si->DisableSounds ) return qfalse;
if( !si->BeginRegistration ) return qfalse;
if( !si->RegisterSound ) return qfalse;
if( !si->ClearSoundBuffer ) return qfalse;
if( !si->SoundInfo ) return qfalse;
if( !si->SoundList ) return qfalse;
return qtrue;
}
/*
=================
S_StartSound
=================
*/
void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx )
{
if( si.StartSound ) {
si.StartSound( origin, entnum, entchannel, sfx );
}
}
/*
=================
S_StartLocalSound
=================
*/
void S_StartLocalSound( sfxHandle_t sfx, int channelNum )
{
if( si.StartLocalSound ) {
si.StartLocalSound( sfx, channelNum );
}
}
/*
=================
S_StartBackgroundTrack
=================
*/
void S_StartBackgroundTrack( const char *intro, const char *loop )
{
if( si.StartBackgroundTrack ) {
si.StartBackgroundTrack( intro, loop );
}
}
/*
=================
S_StopBackgroundTrack
=================
*/
void S_StopBackgroundTrack( void )
{
if( si.StopBackgroundTrack ) {
si.StopBackgroundTrack( );
}
}
/*
=================
S_RawSamples
=================
*/
void S_RawSamples (int samples, int rate, int width, int channels,
const byte *data, float volume)
{
if( si.RawSamples ) {
si.RawSamples( samples, rate, width, channels, data, volume );
}
}
/*
=================
S_StopAllSounds
=================
*/
void S_StopAllSounds( void )
{
if( si.StopAllSounds ) {
si.StopAllSounds( );
}
}
/*
=================
S_ClearLoopingSounds
=================
*/
void S_ClearLoopingSounds( qboolean killall )
{
if( si.ClearLoopingSounds ) {
si.ClearLoopingSounds( killall );
}
}
/*
=================
S_AddLoopingSound
=================
*/
void S_AddLoopingSound( int entityNum, const vec3_t origin,
const vec3_t velocity, sfxHandle_t sfx )
{
if( si.AddLoopingSound ) {
si.AddLoopingSound( entityNum, origin, velocity, sfx );
}
}
/*
=================
S_AddRealLoopingSound
=================
*/
void S_AddRealLoopingSound( int entityNum, const vec3_t origin,
const vec3_t velocity, sfxHandle_t sfx )
{
if( si.AddRealLoopingSound ) {
si.AddRealLoopingSound( entityNum, origin, velocity, sfx );
}
}
/*
=================
S_StopLoopingSound
=================
*/
void S_StopLoopingSound( int entityNum )
{
if( si.StopLoopingSound ) {
si.StopLoopingSound( entityNum );
}
}
/*
=================
S_Respatialize
=================
*/
void S_Respatialize( int entityNum, const vec3_t origin,
vec3_t axis[3], int inwater )
{
if( si.Respatialize ) {
si.Respatialize( entityNum, origin, axis, inwater );
}
}
/*
=================
S_UpdateEntityPosition
=================
*/
void S_UpdateEntityPosition( int entityNum, const vec3_t origin )
{
if( si.UpdateEntityPosition ) {
si.UpdateEntityPosition( entityNum, origin );
}
}
/*
=================
S_Update
=================
*/
void S_Update( void )
{
if( si.Update ) {
si.Update( );
}
}
/*
=================
S_DisableSounds
=================
*/
void S_DisableSounds( void )
{
if( si.DisableSounds ) {
si.DisableSounds( );
}
}
/*
=================
S_BeginRegistration
=================
*/
void S_BeginRegistration( void )
{
if( si.BeginRegistration ) {
si.BeginRegistration( );
}
}
/*
=================
S_RegisterSound
=================
*/
sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed )
{
if( si.RegisterSound ) {
return si.RegisterSound( sample, compressed );
} else {
return 0;
}
}
/*
=================
S_ClearSoundBuffer
=================
*/
void S_ClearSoundBuffer( void )
{
if( si.ClearSoundBuffer ) {
si.ClearSoundBuffer( );
}
}
/*
=================
S_SoundInfo
=================
*/
void S_SoundInfo( void )
{
if( si.SoundInfo ) {
si.SoundInfo( );
}
}
/*
=================
S_SoundList
=================
*/
void S_SoundList( void )
{
if( si.SoundList ) {
si.SoundList( );
}
}
//=============================================================================
/*
=================
S_Play_f
=================
*/
void S_Play_f( void ) {
int i;
sfxHandle_t h;
char name[256];
if( !si.RegisterSound || !si.StartLocalSound ) {
return;
}
i = 1;
while ( i<Cmd_Argc() ) {
if ( !Q_strrchr(Cmd_Argv(i), '.') ) {
Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
} else {
Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
}
h = si.RegisterSound( name, qfalse );
if( h ) {
si.StartLocalSound( h, CHAN_LOCAL_SOUND );
}
i++;
}
}
/*
=================
S_Music_f
=================
*/
void S_Music_f( void ) {
int c;
if( !si.StartBackgroundTrack ) {
return;
}
c = Cmd_Argc();
if ( c == 2 ) {
si.StartBackgroundTrack( Cmd_Argv(1), NULL );
} else if ( c == 3 ) {
si.StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) );
} else {
Com_Printf ("music <musicfile> [loopfile]\n");
return;
}
}
//=============================================================================
/*
=================
S_Init
=================
*/
void S_Init( void )
{
cvar_t *cv;
qboolean started = qfalse;
Com_Printf( "------ Initializing Sound ------\n" );
s_volume = Cvar_Get( "s_volume", "0.8", CVAR_ARCHIVE );
s_musicVolume = Cvar_Get( "s_musicvolume", "0.25", CVAR_ARCHIVE );
s_doppler = Cvar_Get( "s_doppler", "1", CVAR_ARCHIVE );
cv = Cvar_Get( "s_initsound", "1", 0 );
if( !cv->integer ) {
Com_Printf( "Sound disabled.\n" );
} else {
S_CodecInit( );
Cmd_AddCommand( "play", S_Play_f );
Cmd_AddCommand( "music", S_Music_f );
Cmd_AddCommand( "s_list", S_SoundList );
Cmd_AddCommand( "s_stop", S_StopAllSounds );
Cmd_AddCommand( "s_info", S_SoundInfo );
cv = Cvar_Get( "s_useOpenAL", "1", CVAR_ARCHIVE );
if( cv->integer ) {
//OpenAL
started = S_AL_Init( &si );
}
if( !started ) {
started = S_Base_Init( &si );
}
if( started ) {
if( !S_ValidSoundInterface( &si ) ) {
Com_Error( ERR_FATAL, "Sound interface invalid." );
}
S_SoundInfo( );
Com_Printf( "Sound intialization successful.\n" );
} else {
Com_Printf( "Sound intialization failed.\n" );
}
}
Com_Printf( "--------------------------------\n");
}
/*
=================
S_Shutdown
=================
*/
void S_Shutdown( void )
{
if( si.Shutdown ) {
si.Shutdown( );
}
Com_Memset( &si, 0, sizeof( soundInterface_t ) );
Cmd_RemoveCommand( "play" );
Cmd_RemoveCommand( "music");
Cmd_RemoveCommand( "s_list" );
Cmd_RemoveCommand( "s_stop" );
Cmd_RemoveCommand( "s_info" );
S_CodecShutdown( );
}

View file

@ -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;
}

1567
code/client/snd_openal.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -999,6 +999,13 @@
};
012AD9A500868211C697A10E = {
children = (
92847F3509279B370056BC59,
92847F3609279B370056BC59,
92847F3709279B370056BC59,
92847F3809279B370056BC59,
92847F3909279B370056BC59,
92847F3A09279B370056BC59,
92847F3B09279B370056BC59,
9260378009101A6C0018EAE6,
012AD9A600868211C697A10E,
012AD9A700868211C697A10E,
@ -6764,6 +6771,8 @@
9260377A09101A3B0018EAE6,
9260378109101A6C0018EAE6,
9292851809192BA800286DE9,
92847F3D09279B370056BC59,
92847F4009279B370056BC59,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@ -6919,6 +6928,11 @@
4F23A81D08F4FA8F00CB90D3,
92603767091019D30018EAE6,
92603768091019D30018EAE6,
92847F3C09279B370056BC59,
92847F3E09279B370056BC59,
92847F3F09279B370056BC59,
92847F4109279B370056BC59,
92847F4209279B370056BC59,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@ -7775,6 +7789,8 @@
9260377B09101A3B0018EAE6,
9260378209101A6C0018EAE6,
9292851909192BA800286DE9,
92847F4409279B370056BC59,
92847F4709279B370056BC59,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@ -8260,6 +8276,11 @@
4F23A81508F4FA8F00CB90D3,
9260376B091019D30018EAE6,
9260376C091019D30018EAE6,
92847F4309279B370056BC59,
92847F4509279B370056BC59,
92847F4609279B370056BC59,
92847F4809279B370056BC59,
92847F4909279B370056BC59,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
@ -10932,6 +10953,146 @@
settings = {
};
};
92847F3509279B370056BC59 = {
fileEncoding = 30;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.c;
path = qal.c;
refType = 4;
sourceTree = "<group>";
};
92847F3609279B370056BC59 = {
fileEncoding = 30;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = qal.h;
refType = 4;
sourceTree = "<group>";
};
92847F3709279B370056BC59 = {
fileEncoding = 30;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.c;
path = snd_codec_wav.c;
refType = 4;
sourceTree = "<group>";
};
92847F3809279B370056BC59 = {
fileEncoding = 30;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.c;
path = snd_codec.c;
refType = 4;
sourceTree = "<group>";
};
92847F3909279B370056BC59 = {
fileEncoding = 30;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
path = snd_codec.h;
refType = 4;
sourceTree = "<group>";
};
92847F3A09279B370056BC59 = {
fileEncoding = 30;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.c;
path = snd_main.c;
refType = 4;
sourceTree = "<group>";
};
92847F3B09279B370056BC59 = {
fileEncoding = 30;
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.c;
path = snd_openal.c;
refType = 4;
sourceTree = "<group>";
};
92847F3C09279B370056BC59 = {
fileRef = 92847F3509279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F3D09279B370056BC59 = {
fileRef = 92847F3609279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F3E09279B370056BC59 = {
fileRef = 92847F3709279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F3F09279B370056BC59 = {
fileRef = 92847F3809279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4009279B370056BC59 = {
fileRef = 92847F3909279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4109279B370056BC59 = {
fileRef = 92847F3A09279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4209279B370056BC59 = {
fileRef = 92847F3B09279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4309279B370056BC59 = {
fileRef = 92847F3509279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4409279B370056BC59 = {
fileRef = 92847F3609279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4509279B370056BC59 = {
fileRef = 92847F3709279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4609279B370056BC59 = {
fileRef = 92847F3809279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4709279B370056BC59 = {
fileRef = 92847F3909279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4809279B370056BC59 = {
fileRef = 92847F3A09279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
92847F4909279B370056BC59 = {
fileRef = 92847F3B09279B370056BC59;
isa = PBXBuildFile;
settings = {
};
};
9292851709192BA800286DE9 = {
fileEncoding = 30;
isa = PBXFileReference;

View file

@ -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)

View file

@ -56,17 +56,31 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../qcommon/q_shared.h"
#include "../client/snd_local.h"
int snd_inited=0;
qboolean snd_inited = qfalse;
cvar_t *sndbits;
cvar_t *sndspeed;
cvar_t *sndchannels;
cvar_t *snddevice;
cvar_t *sdldevsamps;
cvar_t *sdlmixsamps;
cvar_t *s_sdlBits;
cvar_t *s_sdlSpeed;
cvar_t *s_sdlChannels;
cvar_t *s_sdlDevSamps;
cvar_t *s_sdlMixSamps;
static qboolean use_custom_memset = qfalse;
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
/*
===============
Snd_Memset
https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
<TTimo> some shitty mess with DMA buffers
<TTimo> the mmap'ing permissions were write only
<TTimo> and glibc optimized for mmx would do memcpy with a prefetch and a read
<TTimo> causing segfaults
<TTimo> some other systems would not let you mmap the DMA with read permissions
<TTimo> so I think I ended up attempting opening with read/write, then try write only
<TTimo> and use my own copy instead of the glibc crap
===============
*/
void Snd_Memset (void* dest, const int val, const size_t count)
{
int *pDest;
@ -88,6 +102,12 @@ void Snd_Memset (void* dest, const int val, const size_t count)
/* The audio callback. All the magic happens here. */
static int dmapos = 0;
static int dmasize = 0;
/*
===============
sdl_audio_callback
===============
*/
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
{
int pos = (dmapos * (dma.samplebits/8));
@ -124,28 +144,57 @@ static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
dmapos = 0;
}
static struct
{
Uint16 enumFormat;
char *stringFormat;
} formatToStringTable[ ] =
{
{ AUDIO_U8, "AUDIO_U8" },
{ AUDIO_S8, "AUDIO_S8" },
{ AUDIO_U16LSB, "AUDIO_U16LSB" },
{ AUDIO_S16LSB, "AUDIO_S16LSB" },
{ AUDIO_U16MSB, "AUDIO_U16MSB" },
{ AUDIO_S16MSB, "AUDIO_S16MSB" }
};
static int formatToStringTableSize =
sizeof( formatToStringTable ) / sizeof( formatToStringTable[ 0 ] );
/*
===============
print_audiospec
===============
*/
static void print_audiospec(const char *str, const SDL_AudioSpec *spec)
{
int i;
char *fmt = NULL;
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
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 );
Com_Printf("\n");
}
/*
===============
SNDDMA_Init
===============
*/
qboolean SNDDMA_Init(void)
{
char drivername[128];
@ -154,49 +203,46 @@ qboolean SNDDMA_Init(void)
int tmp;
if (snd_inited)
return 1;
return qtrue;
Com_Printf("SDL Audio driver initializing...\n");
Com_Printf("Initializing SDL audio driver...\n");
if (!snddevice) {
sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE);
sdldevsamps = Cvar_Get("sdldevsamps", "0", CVAR_ARCHIVE);
sdlmixsamps = Cvar_Get("sdlmixsamps", "0", CVAR_ARCHIVE);
if (!s_sdlBits) {
s_sdlBits = Cvar_Get("s_sdlBits", "16", CVAR_ARCHIVE);
s_sdlSpeed = Cvar_Get("s_sdlSpeed", "0", CVAR_ARCHIVE);
s_sdlChannels = Cvar_Get("s_sdlChannels", "2", CVAR_ARCHIVE);
s_sdlDevSamps = Cvar_Get("s_sdlDevSamps", "0", CVAR_ARCHIVE);
s_sdlMixSamps = Cvar_Get("s_sdlMixSamps", "0", CVAR_ARCHIVE);
}
if (!SDL_WasInit(SDL_INIT_AUDIO))
{
Com_Printf("Calling SDL_Init(SDL_INIT_AUDIO)...\n");
if (SDL_Init(SDL_INIT_AUDIO) == -1)
{
Com_Printf("SDL_Init(SDL_INIT_AUDIO) failed: %s\n", SDL_GetError());
return qfalse;
}
Com_Printf("SDL_Init(SDL_INIT_AUDIO) passed.\n");
}
if (SDL_AudioDriverName(drivername, sizeof (drivername)) == NULL)
strcpy(drivername, "(UNKNOWN)");
Com_Printf("SDL audio driver is \"%s\"\n", drivername);
Com_Printf("SDL audio driver is \"%s\".\n", drivername);
memset(&desired, '\0', sizeof (desired));
memset(&obtained, '\0', sizeof (obtained));
tmp = ((int) sndbits->value);
tmp = ((int) s_sdlBits->value);
if ((tmp != 16) && (tmp != 8))
tmp = 16;
desired.freq = (int) sndspeed->value;
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 (sdldevsamps->value)
desired.samples = sdldevsamps->value;
if (s_sdlDevSamps->value)
desired.samples = s_sdlDevSamps->value;
else
{
// just pick a sane default.
@ -210,11 +256,9 @@ qboolean SNDDMA_Init(void)
desired.samples = 2048; // (*shrug*)
}
desired.channels = (int) sndchannels->value;
desired.channels = (int) s_sdlChannels->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());
@ -222,7 +266,7 @@ qboolean SNDDMA_Init(void)
return qfalse;
} // if
print_audiospec("Format we actually got", &obtained);
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
@ -231,7 +275,7 @@ qboolean SNDDMA_Init(void)
// 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;
tmp = s_sdlMixSamps->value;
if (!tmp)
tmp = (obtained.samples * obtained.channels) * 10;
@ -241,8 +285,6 @@ qboolean SNDDMA_Init(void)
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;
}
@ -259,15 +301,25 @@ qboolean SNDDMA_Init(void)
SDL_PauseAudio(0); // start callback.
Com_Printf("SDL audio initialized.\n");
snd_inited = 1;
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");
@ -277,12 +329,12 @@ void SNDDMA_Shutdown(void)
free(dma.buffer);
dma.buffer = NULL;
dmapos = dmasize = 0;
snd_inited = 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
@ -293,12 +345,14 @@ void SNDDMA_Submit(void)
SDL_UnlockAudio();
}
/*
===============
SNDDMA_BeginPainting
===============
*/
void SNDDMA_BeginPainting (void)
{
SDL_LockAudio();
}
#endif // USE_SDL_SOUND
// end of linux_snd_sdl.c ...

View file

@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~