summaryrefslogtreecommitdiff
path: root/source/snd_mem.c
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
committerAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
commitf294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch)
treee8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/snd_mem.c
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/snd_mem.c')
-rw-r--r--source/snd_mem.c479
1 files changed, 479 insertions, 0 deletions
diff --git a/source/snd_mem.c b/source/snd_mem.c
new file mode 100644
index 0000000..3d90d39
--- /dev/null
+++ b/source/snd_mem.c
@@ -0,0 +1,479 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// snd_mem.c: sound caching
+
+#include "cl_local.h"
+#include "snd_local.h"
+
+/*
+================
+ResampleSfx
+
+code from QFusion (thanks Vic)
+================
+*/
+static void ResampleSfx (sfx_t *sfx, byte *data)
+{
+ int i, outcount, srcsample, srclength, samplefrac, fracstep;
+ sfxcache_t *sc = sfx->cache;
+
+ // this is usually 0.5 (128), 1 (256), or 2 (512)
+ fracstep = ((double) sc->speed / (double) dma.speed) * 256.0;
+
+ srclength = sc->length * sc->channels;
+ outcount = (double) sc->length * (double) dma.speed / (double) sc->speed;
+
+ sc->length = outcount;
+ if (sc->loopstart != -1)
+ sc->loopstart = (double) sc->loopstart * (double) dma.speed / (double) sc->speed;
+
+ sc->speed = dma.speed;
+
+// resample / decimate to the current source rate
+ if (fracstep == 256)
+ {
+ if (sc->width == 1) // 8bit
+ for (i = 0;i < srclength;i++)
+ ((signed char *)sc->data)[i] = ((unsigned char *)data)[i] - 128;
+ else
+ for (i = 0;i < srclength;i++)
+ ((short *)sc->data)[i] = LittleShort (((short *)data)[i]);
+ }
+ else
+ {
+// general case
+ Com_DPrintf("ResampleSfx: resampling sound %s\n", sfx->name);
+ samplefrac = 0;
+
+ if ((fracstep & 255) == 0) // skipping points on perfect multiple
+ {
+ srcsample = 0;
+ fracstep >>= 8;
+ if (sc->width == 2)
+ {
+ short *out = (void *)sc->data, *in = (void *)data;
+ if (sc->channels == 2)
+ {
+ fracstep <<= 1;
+ for (i=0 ; i<outcount ; i++)
+ {
+ *out++ = LittleShort (in[srcsample ]);
+ *out++ = LittleShort (in[srcsample+1]);
+ srcsample += fracstep;
+ }
+ }
+ else
+ {
+ for (i=0 ; i<outcount ; i++)
+ {
+ *out++ = LittleShort (in[srcsample ]);
+ srcsample += fracstep;
+ }
+ }
+ }
+ else
+ {
+ signed char *out = (void *)sc->data;
+ unsigned char *in = (void *)data;
+
+ if (sc->channels == 2)
+ {
+ fracstep <<= 1;
+ for (i=0 ; i<outcount ; i++)
+ {
+ *out++ = in[srcsample ] - 128;
+ *out++ = in[srcsample+1] - 128;
+ srcsample += fracstep;
+ }
+ }
+ else
+ {
+ for (i=0 ; i<outcount ; i++)
+ {
+ *out++ = in[srcsample ] - 128;
+ srcsample += fracstep;
+ }
+ }
+ }
+ }
+ else
+ {
+ int sample;
+ int a, b;
+ if (sc->width == 2)
+ {
+ short *out = (void *)sc->data, *in = (void *)data;
+
+ if (sc->channels == 2)
+ {
+ for (i=0 ; i<outcount ; i++)
+ {
+ srcsample = (samplefrac >> 8) << 1;
+ a = in[srcsample ];
+ if (srcsample+2 >= srclength)
+ b = 0;
+ else
+ b = in[srcsample+2];
+ sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
+ *out++ = (short) sample;
+ a = in[srcsample+1];
+ if (srcsample+2 >= srclength)
+ b = 0;
+ else
+ b = in[srcsample+3];
+ sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
+ *out++ = (short) sample;
+ samplefrac += fracstep;
+ }
+ }
+ else
+ {
+ for (i=0 ; i<outcount ; i++)
+ {
+ srcsample = samplefrac >> 8;
+ a = in[srcsample ];
+ if (srcsample+1 >= srclength)
+ b = 0;
+ else
+ b = in[srcsample+1];
+ sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
+ *out++ = (short) sample;
+ samplefrac += fracstep;
+ }
+ }
+ }
+ else
+ {
+ signed char *out = (void *)sc->data;
+ unsigned char *in = (void *)data;
+ if (sc->channels == 2)
+ {
+ for (i=0 ; i<outcount ; i++)
+ {
+ srcsample = (samplefrac >> 8) << 1;
+ a = (int) in[srcsample ] - 128;
+ if (srcsample+2 >= srclength)
+ b = 0;
+ else
+ b = (int) in[srcsample+2] - 128;
+ sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
+ *out++ = (signed char) sample;
+ a = (int) in[srcsample+1] - 128;
+ if (srcsample+2 >= srclength)
+ b = 0;
+ else
+ b = (int) in[srcsample+3] - 128;
+ sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
+ *out++ = (signed char) sample;
+ samplefrac += fracstep;
+ }
+ }
+ else
+ {
+ for (i=0 ; i<outcount ; i++)
+ {
+ srcsample = samplefrac >> 8;
+ a = (int) in[srcsample ] - 128;
+ if (srcsample+1 >= srclength)
+ b = 0;
+ else
+ b = (int) in[srcsample+1] - 128;
+ sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
+ *out++ = (signed char) sample;
+ samplefrac += fracstep;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+===============================================================================
+
+WAV loading
+
+===============================================================================
+*/
+
+typedef struct wavinfo_s {
+ int rate;
+ int width;
+ int channels;
+ int loopstart;
+ int samples;
+} wavinfo_t;
+
+static byte *data_p;
+static byte *iff_end;
+static byte *iff_data;
+static uint32 iff_chunk_len;
+
+static int GetLittleShort(void) {
+ int val;
+
+ if( data_p + 2 > iff_end ) {
+ return -1;
+ }
+
+ val = data_p[0];
+ val |= data_p[1] << 8;
+ data_p += 2;
+ return val;
+}
+
+static int GetLittleLong(void) {
+ int val;
+
+ if( data_p + 4 > iff_end ) {
+ return -1;
+ }
+
+ val = data_p[0];
+ val |= data_p[1] << 8;
+ val |= data_p[2] << 16;
+ val |= data_p[3] << 24;
+ data_p += 4;
+ return val;
+}
+
+static void FindNextChunk( uint32 name ) {
+ uint32 chunk, length;
+
+ while( 1 ) {
+ if( data_p >= iff_end ) {
+ // didn't find the chunk
+ data_p = NULL;
+ return;
+ }
+
+ chunk = GetLittleLong();
+ iff_chunk_len = GetLittleLong();
+ if( data_p + iff_chunk_len > iff_end ) {
+ Com_DPrintf( "FindNextChunk: oversize chunk %#x\n", chunk );
+ data_p = NULL;
+ return;
+ }
+ if( chunk == name ) {
+ return;
+ }
+ length = ( iff_chunk_len + 1 ) & ~1;
+ data_p += length;
+ }
+}
+
+static void FindChunk( uint32 name ) {
+ data_p = iff_data;
+ FindNextChunk( name );
+}
+
+#define TAG_RIFF MakeLong( 'R', 'I', 'F', 'F' )
+#define TAG_WAVE MakeLong( 'W', 'A', 'V', 'E' )
+#define TAG_fmt MakeLong( 'f', 'm', 't', ' ' )
+#define TAG_cue MakeLong( 'c', 'u', 'e', ' ' )
+#define TAG_LIST MakeLong( 'L', 'I', 'S', 'T' )
+#define TAG_MARK MakeLong( 'M', 'A', 'R', 'K' )
+#define TAG_data MakeLong( 'd', 'a', 't', 'a' )
+
+/*
+============
+GetWavinfo
+============
+*/
+static qboolean GetWavinfo( const char *name, wavinfo_t *info ) {
+ int format;
+ int samples, width;
+ uint32 chunk;
+
+ memset (info, 0, sizeof(*info));
+
+// find "RIFF" chunk
+ FindChunk( TAG_RIFF );
+ if( !data_p ) {
+ Com_DPrintf( "%s has missing/invalid RIFF chunk\n", name );
+ return qfalse;
+ }
+ chunk = GetLittleLong();
+ if( chunk != TAG_WAVE ) {
+ Com_DPrintf( "%s has missing/invalid WAVE chunk\n", name );
+ return qfalse;
+ }
+
+ iff_data = data_p;
+
+// get "fmt " chunk
+ FindChunk( TAG_fmt );
+ if( !data_p ) {
+ Com_DPrintf("%s has missing/invalid fmt chunk\n", name );
+ return qfalse;
+ }
+ format = GetLittleShort();
+ if (format != 1) {
+ Com_DPrintf("%s has non-Microsoft PCM format\n", name);
+ return qfalse;
+ }
+
+ info->channels = GetLittleShort();
+ if (info->channels != 1 && info->channels != 2) {
+ Com_DPrintf( "%s has bad number of channels\n", name );
+ return qfalse;
+ }
+
+ info->rate = GetLittleLong();
+ if( info->rate <= 0 ) {
+ Com_DPrintf( "%s has bad rate\n", name );
+ return qfalse;
+ }
+
+ data_p += 4+2;
+
+ width = GetLittleShort();
+ switch( width ) {
+ case 8:
+ info->width = 1;
+ break;
+ case 16:
+ info->width = 2;
+ break;
+ default:
+ Com_DPrintf( "%s has bad width\n", name );
+ return qfalse;
+ }
+
+// get cue chunk
+ FindChunk( TAG_cue );
+ if( data_p ) {
+ data_p += 24;
+ info->loopstart = GetLittleLong();
+
+ FindNextChunk( TAG_LIST );
+ if( data_p ) {
+ data_p += 20;
+ chunk = GetLittleLong();
+ if ( chunk == TAG_MARK ) {
+ // this is not a proper parse, but it works with cooledit...
+ data_p += 16;
+ samples = GetLittleLong(); // samples in loop
+ info->samples = info->loopstart + samples;
+ }
+ }
+ } else {
+ info->loopstart = -1;
+ }
+
+// find data chunk
+ FindChunk( TAG_data );
+ if( !data_p ) {
+ Com_DPrintf( "%s has missing/invalid data chunk\n", name );
+ return qfalse;
+ }
+
+ samples = iff_chunk_len / info->width;
+ if( !samples ) {
+ Com_DPrintf( "%s has zero length\n", name );
+ return qfalse;
+ }
+
+ if ( info->samples ) {
+ if (samples < info->samples) {
+ Com_DPrintf( "%s has bad loop length\n", name);
+ return qfalse;
+ }
+ } else {
+ info->samples = samples;
+ }
+
+ return qtrue;
+}
+
+/*
+==============
+S_LoadSound
+==============
+*/
+sfxcache_t *S_LoadSound (sfx_t *s)
+{
+ char namebuffer[MAX_QPATH];
+ byte *data;
+ wavinfo_t info;
+ int len;
+ float stepscale;
+ sfxcache_t *sc;
+ int size;
+ char *name;
+
+ if (s->name[0] == '*')
+ return NULL;
+
+// see if still in memory
+ sc = s->cache;
+ if (sc)
+ return sc;
+
+// load it in
+ if (s->truename)
+ name = s->truename;
+ else
+ name = s->name;
+
+ if (name[0] == '#')
+ Q_strncpyz( namebuffer, name + 1, sizeof( namebuffer ) );
+ else
+ Com_sprintf (namebuffer, sizeof(namebuffer), "sound/%s", name);
+
+ size = FS_LoadFile (namebuffer, (void **)&data);
+
+ if (!data)
+ {
+ Com_DPrintf ("Couldn't load %s\n", namebuffer);
+ return NULL;
+ }
+
+ iff_data = data;
+ iff_end = data + size;
+ if( !GetWavinfo( name, &info ) ) {
+ FS_FreeFile (data);
+ return NULL;
+ }
+
+ stepscale = ( float )info.rate / dma.speed;
+ len = info.samples / stepscale;
+ if( !len ) {
+ Com_DPrintf( "%s resampled to zero length\n", name );
+ FS_FreeFile (data);
+ return NULL;
+ }
+
+ sc = s->cache = Z_TagMalloc( len * info.width + sizeof( sfxcache_t ) - 1,
+ TAG_SOUND );
+
+ sc->length = info.samples / info.channels;
+ sc->loopstart = info.loopstart;
+ sc->speed = info.rate;
+ sc->width = info.width;
+ sc->channels = info.channels;
+
+ ResampleSfx( s, data_p );
+
+ FS_FreeFile (data);
+
+ return sc;
+}
+