Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

snd_mem.c (9094B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 /*****************************************************************************
     24  * name:		snd_mem.c
     25  *
     26  * desc:		sound caching
     27  *
     28  * $Archive: /MissionPack/code/client/snd_mem.c $
     29  *
     30  *****************************************************************************/
     31 
     32 #include "snd_local.h"
     33 
     34 #define DEF_COMSOUNDMEGS "8"
     35 
     36 /*
     37 ===============================================================================
     38 
     39 memory management
     40 
     41 ===============================================================================
     42 */
     43 
     44 static	sndBuffer	*buffer = NULL;
     45 static	sndBuffer	*freelist = NULL;
     46 static	int inUse = 0;
     47 static	int totalInUse = 0;
     48 
     49 short *sfxScratchBuffer = NULL;
     50 sfx_t *sfxScratchPointer = NULL;
     51 int	   sfxScratchIndex = 0;
     52 
     53 void	SND_free(sndBuffer *v) {
     54 	*(sndBuffer **)v = freelist;
     55 	freelist = (sndBuffer*)v;
     56 	inUse += sizeof(sndBuffer);
     57 }
     58 
     59 sndBuffer*	SND_malloc() {
     60 	sndBuffer *v;
     61 redo:
     62 	if (freelist == NULL) {
     63 		S_FreeOldestSound();
     64 		goto redo;
     65 	}
     66 
     67 	inUse -= sizeof(sndBuffer);
     68 	totalInUse += sizeof(sndBuffer);
     69 
     70 	v = freelist;
     71 	freelist = *(sndBuffer **)freelist;
     72 	v->next = NULL;
     73 	return v;
     74 }
     75 
     76 void SND_setup() {
     77 	sndBuffer *p, *q;
     78 	cvar_t	*cv;
     79 	int scs;
     80 
     81 	cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE );
     82 
     83 	scs = (cv->integer*1536);
     84 
     85 	buffer = malloc(scs*sizeof(sndBuffer) );
     86 	// allocate the stack based hunk allocator
     87 	sfxScratchBuffer = malloc(SND_CHUNK_SIZE * sizeof(short) * 4);	//Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4);
     88 	sfxScratchPointer = NULL;
     89 
     90 	inUse = scs*sizeof(sndBuffer);
     91 	p = buffer;;
     92 	q = p + scs;
     93 	while (--q > p)
     94 		*(sndBuffer **)q = q-1;
     95 	
     96 	*(sndBuffer **)q = NULL;
     97 	freelist = p + scs - 1;
     98 
     99 	Com_Printf("Sound memory manager started\n");
    100 }
    101 
    102 /*
    103 ===============================================================================
    104 
    105 WAV loading
    106 
    107 ===============================================================================
    108 */
    109 
    110 static	byte	*data_p;
    111 static	byte 	*iff_end;
    112 static	byte 	*last_chunk;
    113 static	byte 	*iff_data;
    114 static	int 	iff_chunk_len;
    115 
    116 static short GetLittleShort(void)
    117 {
    118 	short val = 0;
    119 	val = *data_p;
    120 	val = val + (*(data_p+1)<<8);
    121 	data_p += 2;
    122 	return val;
    123 }
    124 
    125 static int GetLittleLong(void)
    126 {
    127 	int val = 0;
    128 	val = *data_p;
    129 	val = val + (*(data_p+1)<<8);
    130 	val = val + (*(data_p+2)<<16);
    131 	val = val + (*(data_p+3)<<24);
    132 	data_p += 4;
    133 	return val;
    134 }
    135 
    136 static void FindNextChunk(char *name)
    137 {
    138 	while (1)
    139 	{
    140 		data_p=last_chunk;
    141 
    142 		if (data_p >= iff_end)
    143 		{	// didn't find the chunk
    144 			data_p = NULL;
    145 			return;
    146 		}
    147 		
    148 		data_p += 4;
    149 		iff_chunk_len = GetLittleLong();
    150 		if (iff_chunk_len < 0)
    151 		{
    152 			data_p = NULL;
    153 			return;
    154 		}
    155 		data_p -= 8;
    156 		last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
    157 		if (!strncmp((char *)data_p, name, 4))
    158 			return;
    159 	}
    160 }
    161 
    162 static void FindChunk(char *name)
    163 {
    164 	last_chunk = iff_data;
    165 	FindNextChunk (name);
    166 }
    167 
    168 /*
    169 ============
    170 GetWavinfo
    171 ============
    172 */
    173 static wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
    174 {
    175 	wavinfo_t	info;
    176 
    177 	Com_Memset (&info, 0, sizeof(info));
    178 
    179 	if (!wav)
    180 		return info;
    181 		
    182 	iff_data = wav;
    183 	iff_end = wav + wavlength;
    184 
    185 // find "RIFF" chunk
    186 	FindChunk("RIFF");
    187 	if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
    188 	{
    189 		Com_Printf("Missing RIFF/WAVE chunks\n");
    190 		return info;
    191 	}
    192 
    193 // get "fmt " chunk
    194 	iff_data = data_p + 12;
    195 // DumpChunks ();
    196 
    197 	FindChunk("fmt ");
    198 	if (!data_p)
    199 	{
    200 		Com_Printf("Missing fmt chunk\n");
    201 		return info;
    202 	}
    203 	data_p += 8;
    204 	info.format = GetLittleShort();
    205 	info.channels = GetLittleShort();
    206 	info.rate = GetLittleLong();
    207 	data_p += 4+2;
    208 	info.width = GetLittleShort() / 8;
    209 
    210 	if (info.format != 1)
    211 	{
    212 		Com_Printf("Microsoft PCM format only\n");
    213 		return info;
    214 	}
    215 
    216 
    217 // find data chunk
    218 	FindChunk("data");
    219 	if (!data_p)
    220 	{
    221 		Com_Printf("Missing data chunk\n");
    222 		return info;
    223 	}
    224 
    225 	data_p += 4;
    226 	info.samples = GetLittleLong () / info.width;
    227 	info.dataofs = data_p - wav;
    228 
    229 	return info;
    230 }
    231 
    232 
    233 /*
    234 ================
    235 ResampleSfx
    236 
    237 resample / decimate to the current source rate
    238 ================
    239 */
    240 static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
    241 	int		outcount;
    242 	int		srcsample;
    243 	float	stepscale;
    244 	int		i;
    245 	int		sample, samplefrac, fracstep;
    246 	int			part;
    247 	sndBuffer	*chunk;
    248 	
    249 	stepscale = (float)inrate / dma.speed;	// this is usually 0.5, 1, or 2
    250 
    251 	outcount = sfx->soundLength / stepscale;
    252 	sfx->soundLength = outcount;
    253 
    254 	samplefrac = 0;
    255 	fracstep = stepscale * 256;
    256 	chunk = sfx->soundData;
    257 
    258 	for (i=0 ; i<outcount ; i++)
    259 	{
    260 		srcsample = samplefrac >> 8;
    261 		samplefrac += fracstep;
    262 		if( inwidth == 2 ) {
    263 			sample = LittleShort ( ((short *)data)[srcsample] );
    264 		} else {
    265 			sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
    266 		}
    267 		part  = (i&(SND_CHUNK_SIZE-1));
    268 		if (part == 0) {
    269 			sndBuffer	*newchunk;
    270 			newchunk = SND_malloc();
    271 			if (chunk == NULL) {
    272 				sfx->soundData = newchunk;
    273 			} else {
    274 				chunk->next = newchunk;
    275 			}
    276 			chunk = newchunk;
    277 		}
    278 
    279 		chunk->sndChunk[part] = sample;
    280 	}
    281 }
    282 
    283 /*
    284 ================
    285 ResampleSfx
    286 
    287 resample / decimate to the current source rate
    288 ================
    289 */
    290 static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
    291 	int			outcount;
    292 	int			srcsample;
    293 	float		stepscale;
    294 	int			i;
    295 	int			sample, samplefrac, fracstep;
    296 	
    297 	stepscale = (float)inrate / dma.speed;	// this is usually 0.5, 1, or 2
    298 
    299 	outcount = samples / stepscale;
    300 
    301 	samplefrac = 0;
    302 	fracstep = stepscale * 256;
    303 
    304 	for (i=0 ; i<outcount ; i++)
    305 	{
    306 		srcsample = samplefrac >> 8;
    307 		samplefrac += fracstep;
    308 		if( inwidth == 2 ) {
    309 			sample = LittleShort ( ((short *)data)[srcsample] );
    310 		} else {
    311 			sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
    312 		}
    313 		sfx[i] = sample;
    314 	}
    315 	return outcount;
    316 }
    317 
    318 
    319 //=============================================================================
    320 
    321 /*
    322 ==============
    323 S_LoadSound
    324 
    325 The filename may be different than sfx->name in the case
    326 of a forced fallback of a player specific sound
    327 ==============
    328 */
    329 qboolean S_LoadSound( sfx_t *sfx )
    330 {
    331 	byte	*data;
    332 	short	*samples;
    333 	wavinfo_t	info;
    334 	int		size;
    335 
    336 	// player specific sounds are never directly loaded
    337 	if ( sfx->soundName[0] == '*') {
    338 		return qfalse;
    339 	}
    340 
    341 	// load it in
    342 	size = FS_ReadFile( sfx->soundName, (void **)&data );
    343 	if ( !data ) {
    344 		return qfalse;
    345 	}
    346 
    347 	info = GetWavinfo( sfx->soundName, data, size );
    348 	if ( info.channels != 1 ) {
    349 		Com_Printf ("%s is a stereo wav file\n", sfx->soundName);
    350 		FS_FreeFile (data);
    351 		return qfalse;
    352 	}
    353 
    354 	if ( info.width == 1 ) {
    355 		Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
    356 	}
    357 
    358 	if ( info.rate != 22050 ) {
    359 		Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
    360 	}
    361 
    362 	samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
    363 
    364 	sfx->lastTimeUsed = Com_Milliseconds()+1;
    365 
    366 	// each of these compression schemes works just fine
    367 	// but the 16bit quality is much nicer and with a local
    368 	// install assured we can rely upon the sound memory
    369 	// manager to do the right thing for us and page
    370 	// sound in as needed
    371 
    372 	if( sfx->soundCompressed == qtrue) {
    373 		sfx->soundCompressionMethod = 1;
    374 		sfx->soundData = NULL;
    375 		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
    376 		S_AdpcmEncodeSound(sfx, samples);
    377 #if 0
    378 	} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
    379 		sfx->soundCompressionMethod = 3;
    380 		sfx->soundData = NULL;
    381 		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
    382 		encodeMuLaw( sfx, samples);
    383 	} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
    384 		sfx->soundCompressionMethod = 2;
    385 		sfx->soundData = NULL;
    386 		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
    387 		encodeWavelet( sfx, samples);
    388 #endif
    389 	} else {
    390 		sfx->soundCompressionMethod = 0;
    391 		sfx->soundLength = info.samples;
    392 		sfx->soundData = NULL;
    393 		ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
    394 	}
    395 	
    396 	Hunk_FreeTempMemory(samples);
    397 	FS_FreeFile( data );
    398 
    399 	return qtrue;
    400 }
    401 
    402 void S_DisplayFreeMemory() {
    403 	Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
    404 }