Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

snd_mem.c (6844B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // snd_mem.c: sound caching
     21 
     22 #include "client.h"
     23 #include "snd_loc.h"
     24 
     25 int			cache_full_cycle;
     26 
     27 byte *S_Alloc (int size);
     28 
     29 /*
     30 ================
     31 ResampleSfx
     32 ================
     33 */
     34 void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
     35 {
     36 	int		outcount;
     37 	int		srcsample;
     38 	float	stepscale;
     39 	int		i;
     40 	int		sample, samplefrac, fracstep;
     41 	sfxcache_t	*sc;
     42 	
     43 	sc = sfx->cache;
     44 	if (!sc)
     45 		return;
     46 
     47 	stepscale = (float)inrate / dma.speed;	// this is usually 0.5, 1, or 2
     48 
     49 	outcount = sc->length / stepscale;
     50 	sc->length = outcount;
     51 	if (sc->loopstart != -1)
     52 		sc->loopstart = sc->loopstart / stepscale;
     53 
     54 	sc->speed = dma.speed;
     55 	if (s_loadas8bit->value)
     56 		sc->width = 1;
     57 	else
     58 		sc->width = inwidth;
     59 	sc->stereo = 0;
     60 
     61 // resample / decimate to the current source rate
     62 
     63 	if (stepscale == 1 && inwidth == 1 && sc->width == 1)
     64 	{
     65 // fast special case
     66 		for (i=0 ; i<outcount ; i++)
     67 			((signed char *)sc->data)[i]
     68 			= (int)( (unsigned char)(data[i]) - 128);
     69 	}
     70 	else
     71 	{
     72 // general case
     73 		samplefrac = 0;
     74 		fracstep = stepscale*256;
     75 		for (i=0 ; i<outcount ; i++)
     76 		{
     77 			srcsample = samplefrac >> 8;
     78 			samplefrac += fracstep;
     79 			if (inwidth == 2)
     80 				sample = LittleShort ( ((short *)data)[srcsample] );
     81 			else
     82 				sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
     83 			if (sc->width == 2)
     84 				((short *)sc->data)[i] = sample;
     85 			else
     86 				((signed char *)sc->data)[i] = sample >> 8;
     87 		}
     88 	}
     89 }
     90 
     91 //=============================================================================
     92 
     93 /*
     94 ==============
     95 S_LoadSound
     96 ==============
     97 */
     98 sfxcache_t *S_LoadSound (sfx_t *s)
     99 {
    100     char	namebuffer[MAX_QPATH];
    101 	byte	*data;
    102 	wavinfo_t	info;
    103 	int		len;
    104 	float	stepscale;
    105 	sfxcache_t	*sc;
    106 	int		size;
    107 	char	*name;
    108 
    109 	if (s->name[0] == '*')
    110 		return NULL;
    111 
    112 // see if still in memory
    113 	sc = s->cache;
    114 	if (sc)
    115 		return sc;
    116 
    117 //Com_Printf ("S_LoadSound: %x\n", (int)stackbuf);
    118 // load it in
    119 	if (s->truename)
    120 		name = s->truename;
    121 	else
    122 		name = s->name;
    123 
    124 	if (name[0] == '#')
    125 		strcpy(namebuffer, &name[1]);
    126 	else
    127 		Com_sprintf (namebuffer, sizeof(namebuffer), "sound/%s", name);
    128 
    129 //	Com_Printf ("loading %s\n",namebuffer);
    130 
    131 	size = FS_LoadFile (namebuffer, (void **)&data);
    132 
    133 	if (!data)
    134 	{
    135 		Com_DPrintf ("Couldn't load %s\n", namebuffer);
    136 		return NULL;
    137 	}
    138 
    139 	info = GetWavinfo (s->name, data, size);
    140 	if (info.channels != 1)
    141 	{
    142 		Com_Printf ("%s is a stereo sample\n",s->name);
    143 		FS_FreeFile (data);
    144 		return NULL;
    145 	}
    146 
    147 	stepscale = (float)info.rate / dma.speed;	
    148 	len = info.samples / stepscale;
    149 
    150 	len = len * info.width * info.channels;
    151 
    152 	sc = s->cache = Z_Malloc (len + sizeof(sfxcache_t));
    153 	if (!sc)
    154 	{
    155 		FS_FreeFile (data);
    156 		return NULL;
    157 	}
    158 	
    159 	sc->length = info.samples;
    160 	sc->loopstart = info.loopstart;
    161 	sc->speed = info.rate;
    162 	sc->width = info.width;
    163 	sc->stereo = info.channels;
    164 
    165 	ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);
    166 
    167 	FS_FreeFile (data);
    168 
    169 	return sc;
    170 }
    171 
    172 
    173 
    174 /*
    175 ===============================================================================
    176 
    177 WAV loading
    178 
    179 ===============================================================================
    180 */
    181 
    182 
    183 byte	*data_p;
    184 byte 	*iff_end;
    185 byte 	*last_chunk;
    186 byte 	*iff_data;
    187 int 	iff_chunk_len;
    188 
    189 
    190 short GetLittleShort(void)
    191 {
    192 	short val = 0;
    193 	val = *data_p;
    194 	val = val + (*(data_p+1)<<8);
    195 	data_p += 2;
    196 	return val;
    197 }
    198 
    199 int GetLittleLong(void)
    200 {
    201 	int val = 0;
    202 	val = *data_p;
    203 	val = val + (*(data_p+1)<<8);
    204 	val = val + (*(data_p+2)<<16);
    205 	val = val + (*(data_p+3)<<24);
    206 	data_p += 4;
    207 	return val;
    208 }
    209 
    210 void FindNextChunk(char *name)
    211 {
    212 	while (1)
    213 	{
    214 		data_p=last_chunk;
    215 
    216 		if (data_p >= iff_end)
    217 		{	// didn't find the chunk
    218 			data_p = NULL;
    219 			return;
    220 		}
    221 		
    222 		data_p += 4;
    223 		iff_chunk_len = GetLittleLong();
    224 		if (iff_chunk_len < 0)
    225 		{
    226 			data_p = NULL;
    227 			return;
    228 		}
    229 //		if (iff_chunk_len > 1024*1024)
    230 //			Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
    231 		data_p -= 8;
    232 		last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
    233 		if (!strncmp(data_p, name, 4))
    234 			return;
    235 	}
    236 }
    237 
    238 void FindChunk(char *name)
    239 {
    240 	last_chunk = iff_data;
    241 	FindNextChunk (name);
    242 }
    243 
    244 
    245 void DumpChunks(void)
    246 {
    247 	char	str[5];
    248 	
    249 	str[4] = 0;
    250 	data_p=iff_data;
    251 	do
    252 	{
    253 		memcpy (str, data_p, 4);
    254 		data_p += 4;
    255 		iff_chunk_len = GetLittleLong();
    256 		Com_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
    257 		data_p += (iff_chunk_len + 1) & ~1;
    258 	} while (data_p < iff_end);
    259 }
    260 
    261 /*
    262 ============
    263 GetWavinfo
    264 ============
    265 */
    266 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
    267 {
    268 	wavinfo_t	info;
    269 	int     i;
    270 	int     format;
    271 	int		samples;
    272 
    273 	memset (&info, 0, sizeof(info));
    274 
    275 	if (!wav)
    276 		return info;
    277 		
    278 	iff_data = wav;
    279 	iff_end = wav + wavlength;
    280 
    281 // find "RIFF" chunk
    282 	FindChunk("RIFF");
    283 	if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
    284 	{
    285 		Com_Printf("Missing RIFF/WAVE chunks\n");
    286 		return info;
    287 	}
    288 
    289 // get "fmt " chunk
    290 	iff_data = data_p + 12;
    291 // DumpChunks ();
    292 
    293 	FindChunk("fmt ");
    294 	if (!data_p)
    295 	{
    296 		Com_Printf("Missing fmt chunk\n");
    297 		return info;
    298 	}
    299 	data_p += 8;
    300 	format = GetLittleShort();
    301 	if (format != 1)
    302 	{
    303 		Com_Printf("Microsoft PCM format only\n");
    304 		return info;
    305 	}
    306 
    307 	info.channels = GetLittleShort();
    308 	info.rate = GetLittleLong();
    309 	data_p += 4+2;
    310 	info.width = GetLittleShort() / 8;
    311 
    312 // get cue chunk
    313 	FindChunk("cue ");
    314 	if (data_p)
    315 	{
    316 		data_p += 32;
    317 		info.loopstart = GetLittleLong();
    318 //		Com_Printf("loopstart=%d\n", sfx->loopstart);
    319 
    320 	// if the next chunk is a LIST chunk, look for a cue length marker
    321 		FindNextChunk ("LIST");
    322 		if (data_p)
    323 		{
    324 			if (!strncmp (data_p + 28, "mark", 4))
    325 			{	// this is not a proper parse, but it works with cooledit...
    326 				data_p += 24;
    327 				i = GetLittleLong ();	// samples in loop
    328 				info.samples = info.loopstart + i;
    329 //				Com_Printf("looped length: %i\n", i);
    330 			}
    331 		}
    332 	}
    333 	else
    334 		info.loopstart = -1;
    335 
    336 // find data chunk
    337 	FindChunk("data");
    338 	if (!data_p)
    339 	{
    340 		Com_Printf("Missing data chunk\n");
    341 		return info;
    342 	}
    343 
    344 	data_p += 4;
    345 	samples = GetLittleLong () / info.width;
    346 
    347 	if (info.samples)
    348 	{
    349 		if (samples < info.samples)
    350 			Com_Error (ERR_DROP, "Sound %s has a bad loop length", name);
    351 	}
    352 	else
    353 		info.samples = samples;
    354 
    355 	info.dataofs = data_p - wav;
    356 	
    357 	return info;
    358 }
    359