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 = (sfxcache_t*) 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 = (int) (sc->length / stepscale); 49 sc->length = outcount; 50 if (sc->loopstart != -1) 51 sc->loopstart = (int) (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 sc->data.sc[i] 67 = (int)( (unsigned char)(data[i]) - 128); 68 } 69 else 70 { 71// general case 72 samplefrac = 0; 73 fracstep = (int) (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 sc->data.s[i] = sample; 84 else 85 sc->data.sc[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 = (sfxcache_t*) 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 = (int) (info.samples / stepscale); 136 137 len = len * info.width * info.channels; 138 139 sc = (sfxcache_t*) 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(const 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(const char *name) 221{ 222 last_chunk = iff_data; 223 FindNextChunk (name); 224} 225 226 227void DumpChunks(void) 228{ 229 char str[5]; 230 231 str[4] = 0; 232 data_p=iff_data; 233 do 234 { 235 memcpy (str, data_p, 4); 236 data_p += 4; 237 iff_chunk_len = GetLittleLong(); 238 Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); 239 data_p += (iff_chunk_len + 1) & ~1; 240 } while (data_p < iff_end); 241} 242 243/* 244============ 245GetWavinfo 246============ 247*/ 248wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) 249{ 250 wavinfo_t info; 251 int i; 252 int format; 253 int samples; 254 255 memset (&info, 0, sizeof(info)); 256 257 if (!wav) 258 return info; 259 260 iff_data = wav; 261 iff_end = wav + wavlength; 262 263// find "RIFF" chunk 264 FindChunk("RIFF"); 265 if (!(data_p && !Q_strncmp((char*) (data_p+8), "WAVE", 4))) 266 { 267 Con_Printf("Missing RIFF/WAVE chunks\n"); 268 return info; 269 } 270 271// get "fmt " chunk 272 iff_data = data_p + 12; 273// DumpChunks (); 274 275 FindChunk("fmt "); 276 if (!data_p) 277 { 278 Con_Printf("Missing fmt chunk\n"); 279 return info; 280 } 281 data_p += 8; 282 format = GetLittleShort(); 283 if (format != 1) 284 { 285 Con_Printf("Microsoft PCM format only\n"); 286 return info; 287 } 288 289 info.channels = GetLittleShort(); 290 info.rate = GetLittleLong(); 291 data_p += 4+2; 292 info.width = GetLittleShort() / 8; 293 294// get cue chunk 295 FindChunk("cue "); 296 if (data_p) 297 { 298 data_p += 32; 299 info.loopstart = GetLittleLong(); 300// Con_Printf("loopstart=%d\n", sfx->loopstart); 301 302 // if the next chunk is a LIST chunk, look for a cue length marker 303 FindNextChunk ("LIST"); 304 if (data_p) 305 { 306 if (!strncmp ((char*) (data_p + 28), "mark", 4)) 307 { // this is not a proper parse, but it works with cooledit... 308 data_p += 24; 309 i = GetLittleLong (); // samples in loop 310 info.samples = info.loopstart + i; 311// Con_Printf("looped length: %i\n", i); 312 } 313 } 314 } 315 else 316 info.loopstart = -1; 317 318// find data chunk 319 FindChunk("data"); 320 if (!data_p) 321 { 322 Con_Printf("Missing data chunk\n"); 323 return info; 324 } 325 326 data_p += 4; 327 samples = GetLittleLong () / info.width; 328 329 if (info.samples) 330 { 331 if (samples < info.samples) 332 Sys_Error ("Sound %s has a bad loop length", name); 333 } 334 else 335 info.samples = samples; 336 337 info.dataofs = data_p - wav; 338 339 return info; 340} 341 342