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