quake3/code/client/snd_mem.c
2005-08-19 00:00:00 +00:00

404 lines
9.3 KiB
C
Executable file

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
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
===========================================================================
*/
/*****************************************************************************
* name: snd_mem.c
*
* desc: sound caching
*
* $Archive: /MissionPack/code/client/snd_mem.c $
*
*****************************************************************************/
#include "snd_local.h"
#define DEF_COMSOUNDMEGS "8"
/*
===============================================================================
memory management
===============================================================================
*/
static sndBuffer *buffer = NULL;
static sndBuffer *freelist = NULL;
static int inUse = 0;
static int totalInUse = 0;
short *sfxScratchBuffer = NULL;
sfx_t *sfxScratchPointer = NULL;
int sfxScratchIndex = 0;
void SND_free(sndBuffer *v) {
*(sndBuffer **)v = freelist;
freelist = (sndBuffer*)v;
inUse += sizeof(sndBuffer);
}
sndBuffer* SND_malloc() {
sndBuffer *v;
redo:
if (freelist == NULL) {
S_FreeOldestSound();
goto redo;
}
inUse -= sizeof(sndBuffer);
totalInUse += sizeof(sndBuffer);
v = freelist;
freelist = *(sndBuffer **)freelist;
v->next = NULL;
return v;
}
void SND_setup() {
sndBuffer *p, *q;
cvar_t *cv;
int scs;
cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE );
scs = (cv->integer*1536);
buffer = malloc(scs*sizeof(sndBuffer) );
// allocate the stack based hunk allocator
sfxScratchBuffer = malloc(SND_CHUNK_SIZE * sizeof(short) * 4); //Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4);
sfxScratchPointer = NULL;
inUse = scs*sizeof(sndBuffer);
p = buffer;;
q = p + scs;
while (--q > p)
*(sndBuffer **)q = q-1;
*(sndBuffer **)q = NULL;
freelist = p + scs - 1;
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
resample / decimate to the current source rate
================
*/
static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
int outcount;
int srcsample;
float stepscale;
int i;
int sample, samplefrac, fracstep;
int part;
sndBuffer *chunk;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = sfx->soundLength / stepscale;
sfx->soundLength = outcount;
samplefrac = 0;
fracstep = stepscale * 256;
chunk = sfx->soundData;
for (i=0 ; i<outcount ; i++)
{
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if( inwidth == 2 ) {
sample = LittleShort ( ((short *)data)[srcsample] );
} else {
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
}
part = (i&(SND_CHUNK_SIZE-1));
if (part == 0) {
sndBuffer *newchunk;
newchunk = SND_malloc();
if (chunk == NULL) {
sfx->soundData = newchunk;
} else {
chunk->next = newchunk;
}
chunk = newchunk;
}
chunk->sndChunk[part] = sample;
}
}
/*
================
ResampleSfx
resample / decimate to the current source rate
================
*/
static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
int outcount;
int srcsample;
float stepscale;
int i;
int sample, samplefrac, fracstep;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = samples / stepscale;
samplefrac = 0;
fracstep = stepscale * 256;
for (i=0 ; i<outcount ; i++)
{
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if( inwidth == 2 ) {
sample = LittleShort ( ((short *)data)[srcsample] );
} else {
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
}
sfx[i] = sample;
}
return outcount;
}
//=============================================================================
/*
==============
S_LoadSound
The filename may be different than sfx->name in the case
of a forced fallback of a player specific sound
==============
*/
qboolean S_LoadSound( sfx_t *sfx )
{
byte *data;
short *samples;
wavinfo_t info;
int size;
// player specific sounds are never directly loaded
if ( sfx->soundName[0] == '*') {
return qfalse;
}
// load it in
size = FS_ReadFile( sfx->soundName, (void **)&data );
if ( !data ) {
return qfalse;
}
info = GetWavinfo( sfx->soundName, data, size );
if ( info.channels != 1 ) {
Com_Printf ("%s is a stereo wav file\n", sfx->soundName);
FS_FreeFile (data);
return qfalse;
}
if ( info.width == 1 ) {
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
}
if ( info.rate != 22050 ) {
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
}
samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
sfx->lastTimeUsed = Com_Milliseconds()+1;
// each of these compression schemes works just fine
// but the 16bit quality is much nicer and with a local
// install assured we can rely upon the sound memory
// manager to do the right thing for us and page
// sound in as needed
if( sfx->soundCompressed == qtrue) {
sfx->soundCompressionMethod = 1;
sfx->soundData = NULL;
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) {
sfx->soundCompressionMethod = 3;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
encodeMuLaw( sfx, samples);
} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
sfx->soundCompressionMethod = 2;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
encodeWavelet( sfx, samples);
#endif
} else {
sfx->soundCompressionMethod = 0;
sfx->soundLength = info.samples;
sfx->soundData = NULL;
ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
}
Hunk_FreeTempMemory(samples);
FS_FreeFile( data );
return qtrue;
}
void S_DisplayFreeMemory() {
Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
}