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 }