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