/* =========================================================================== Return to Castle Wolfenstein single player GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). RTCW SP 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 3 of the License, or (at your option) any later version. RTCW SP 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 RTCW SP Source Code. If not, see . In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ /***************************************************************************** * name: snd_mem.c * * desc: sound caching * * $Archive: /Wolf5/src/client/snd_mem.c $ * *****************************************************************************/ #include "snd_local.h" #define DEF_COMSOUNDMEGS "24" // (SA) upped for GD /* =============================================================================== SOUND MEMORY MANAGENT =============================================================================== */ static sndBuffer *buffer = NULL; static sndBuffer *freelist = NULL; static int inUse = 0; static int totalInUse = 0; short *sfxScratchBuffer = NULL; const sfx_t *sfxScratchPointer = NULL; int sfxScratchIndex = 0; extern cvar_t *s_nocompressed; /* ================ SND_free ================ */ void SND_free( sndBuffer *v ) { *(sndBuffer **)v = freelist; freelist = (sndBuffer*)v; inUse += sizeof( sndBuffer ); } /* ================ SND_malloc ================ */ sndBuffer* SND_malloc() { sndBuffer *v; while ( freelist == NULL ) { S_FreeOldestSound(); } inUse -= sizeof( sndBuffer ); totalInUse += sizeof( sndBuffer ); v = freelist; freelist = *(sndBuffer **)freelist; v->next = NULL; return v; } /* ================ SND_setup ================ */ 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 * 512; 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; /* ================ GetLittleShort ================ */ static short GetLittleShort( void ) { short val = 0; val = *data_p; val = val + ( *( data_p + 1 ) << 8 ); data_p += 2; return val; } /* ================ GetLittleLong ================ */ 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; } /* ================ FindNextChunk ================ */ 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; } } } /* ================ FindChunk ================ */ 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 = Sys_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 ( s_nocompressed->value ) { sfx->soundCompressionMethod = 0; sfx->soundLength = info.samples; sfx->soundData = NULL; ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse ); } else 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 ); #ifdef COMPRESSION } 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; } /* ================ S_DisplayFreeMemory ================ */ void S_DisplayFreeMemory() { Com_Printf( "%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse ); }