optimized AngleVectors calls (pass NULL for vectors that should not be generated)
[divverent/darkplaces.git] / snd_mem.c
1 /*
2 Copyright (C) 1996-1997 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 "quakedef.h"
23
24 int                     cache_full_cycle;
25
26 byte *S_Alloc (int size);
27
28 /*
29 ================
30 ResampleSfx
31 ================
32 */
33 void ResampleSfx (sfx_t *sfx, int inrate, byte *data, char *name)
34 {
35         int             outcount;
36         int             srcsample, srclength;
37         float   stepscale;
38         int             i;
39         int             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         srclength = sc->length << sc->stereo;
49
50         outcount = sc->length / stepscale;
51         sc->length = outcount;
52         if (sc->loopstart != -1)
53                 sc->loopstart = sc->loopstart / stepscale;
54
55         sc->speed = shm->speed;
56 //      if (loadas8bit.value)
57 //              sc->width = 1;
58 //      else
59 //              sc->width = inwidth;
60 //      sc->stereo = 0;
61
62 // resample / decimate to the current source rate
63
64         if (stepscale == 1/* && inwidth == 1*/ && sc->width == 1)
65         {
66 // fast special case
67                 /*
68                 // LordHavoc: I do not serve the readability gods...
69                 int *indata, *outdata;
70                 int count4, count1;
71                 count1 = outcount << sc->stereo;
72                 count4 = count1 >> 2;
73                 indata = (void *)data;
74                 outdata = (void *)sc->data;
75                 while (count4--)
76                         *outdata++ = *indata++ ^ 0x80808080;
77                 if (count1 & 2)
78                         ((short*)outdata)[0] = ((short*)indata)[0] ^ 0x8080;
79                 if (count1 & 1)
80                         ((char*)outdata)[2] = ((char*)indata)[2] ^ 0x80;
81                 */
82                 if (sc->stereo) // LordHavoc: stereo sound support
83                         outcount *= 2;
84                 for (i=0 ; i<outcount ; i++)
85                         ((signed char *)sc->data)[i] = ((unsigned char *)data)[i] - 128;
86         }
87         else if (stepscale == 1/* && inwidth == 2*/ && sc->width == 2) // LordHavoc: quick case for 16bit
88         {
89                 if (sc->stereo) // LordHavoc: stereo sound support
90                         outcount *= 2;
91                 for (i=0 ; i<outcount ;i++)
92                         ((short *)sc->data)[i] = LittleShort (((short *)data)[i]);
93         }
94         else
95         {
96 // general case
97                 Con_DPrintf("ResampleSfx: resampling sound %s\n", sfx->name);
98                 samplefrac = 0;
99                 fracstep = stepscale*256;
100                 if ((fracstep & 255) == 0) // skipping points on perfect multiple
101                 {
102                         srcsample = 0;
103                         fracstep >>= 8;
104                         if (sc->width == 2)
105                         {
106                                 short *out = (void *)sc->data, *in = (void *)data;
107                                 if (sc->stereo) // LordHavoc: stereo sound support
108                                 {
109                                         fracstep <<= 1;
110                                         for (i=0 ; i<outcount ; i++)
111                                         {
112                                                 *out++ = LittleShort (in[srcsample  ]);
113                                                 *out++ = LittleShort (in[srcsample+1]);
114                                                 srcsample += fracstep;
115                                         }
116                                 }
117                                 else
118                                 {
119                                         for (i=0 ; i<outcount ; i++)
120                                         {
121                                                 *out++ = LittleShort (in[srcsample  ]);
122                                                 srcsample += fracstep;
123                                         }
124                                 }
125                         }
126                         else
127                         {
128                                 signed char *out = (void *)sc->data;
129                                 unsigned char *in = (void *)data;
130                                 if (sc->stereo) // LordHavoc: stereo sound support
131                                 {
132                                         fracstep <<= 1;
133                                         for (i=0 ; i<outcount ; i++)
134                                         {
135                                                 *out++ = in[srcsample  ] - 128;
136                                                 *out++ = in[srcsample+1] - 128;
137                                                 srcsample += fracstep;
138                                         }
139                                 }
140                                 else
141                                 {
142                                         for (i=0 ; i<outcount ; i++)
143                                         {
144                                                 *out++ = in[srcsample  ] - 128;
145                                                 srcsample += fracstep;
146                                         }
147                                 }
148                         }
149                 }
150                 else
151                 {
152                         int sample;
153                         int a, b;
154                         if (sc->width == 2)
155                         {
156                                 short *out = (void *)sc->data, *in = (void *)data;
157                                 if (sc->stereo) // LordHavoc: stereo sound support
158                                 {
159                                         for (i=0 ; i<outcount ; i++)
160                                         {
161                                                 srcsample = (samplefrac >> 8) << 1;
162                                                 a = in[srcsample  ];
163                                                 if (srcsample+2 >= srclength)
164                                                         b = 0;
165                                                 else
166                                                         b = in[srcsample+2];
167                                                 sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
168                                                 *out++ = (short) sample;
169                                                 a = in[srcsample+1];
170                                                 if (srcsample+2 >= srclength)
171                                                         b = 0;
172                                                 else
173                                                         b = in[srcsample+3];
174                                                 sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
175                                                 *out++ = (short) sample;
176                                                 samplefrac += fracstep;
177                                         }
178                                 }
179                                 else
180                                 {
181                                         for (i=0 ; i<outcount ; i++)
182                                         {
183                                                 srcsample = samplefrac >> 8;
184                                                 a = in[srcsample  ];
185                                                 if (srcsample+1 >= srclength)
186                                                         b = 0;
187                                                 else
188                                                         b = in[srcsample+1];
189                                                 sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
190                                                 *out++ = (short) sample;
191                                                 samplefrac += fracstep;
192                                         }
193                                 }
194                         }
195                         else
196                         {
197                                 signed char *out = (void *)sc->data;
198                                 unsigned char *in = (void *)data;
199                                 if (sc->stereo) // LordHavoc: stereo sound support
200                                 {
201                                         for (i=0 ; i<outcount ; i++)
202                                         {
203                                                 srcsample = (samplefrac >> 8) << 1;
204                                                 a = (int) in[srcsample  ] - 128;
205                                                 if (srcsample+2 >= srclength)
206                                                         b = 0;
207                                                 else
208                                                         b = (int) in[srcsample+2] - 128;
209                                                 sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
210                                                 *out++ = (signed char) sample;
211                                                 a = (int) in[srcsample+1] - 128;
212                                                 if (srcsample+2 >= srclength)
213                                                         b = 0;
214                                                 else
215                                                         b = (int) in[srcsample+3] - 128;
216                                                 sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
217                                                 *out++ = (signed char) sample;
218                                                 samplefrac += fracstep;
219                                         }
220                                 }
221                                 else
222                                 {
223                                         for (i=0 ; i<outcount ; i++)
224                                         {
225                                                 srcsample = samplefrac >> 8;
226                                                 a = (int) in[srcsample  ] - 128;
227                                                 if (srcsample+1 >= srclength)
228                                                         b = 0;
229                                                 else
230                                                         b = (int) in[srcsample+1] - 128;
231                                                 sample = (((b - a) * (samplefrac & 255)) >> 8) + a;
232                                                 *out++ = (signed char) sample;
233                                                 samplefrac += fracstep;
234                                         }
235                                 }
236                         }
237                 }
238         }
239
240         // LordHavoc: use this for testing if it ever becomes useful again
241 #if 0
242         COM_WriteFile (va("sound/%s.pcm", name), sc->data, (sc->length << sc->stereo) * sc->width);
243 #endif
244 }
245
246 //=============================================================================
247
248 /*
249 ==============
250 S_LoadSound
251 ==============
252 */
253 sfxcache_t *S_LoadSound (sfx_t *s)
254 {
255     char        namebuffer[256];
256         byte    *data;
257         wavinfo_t       info;
258         int             len;
259         float   stepscale;
260         sfxcache_t      *sc;
261
262 // see if still in memory
263         sc = Cache_Check (&s->cache);
264         if (sc)
265                 return sc;
266
267 //Con_Printf ("S_LoadSound: %x\n", (int)stackbuf);
268 // load it in
269         strcpy(namebuffer, "sound/");
270         strcat(namebuffer, s->name);
271
272 //      Con_Printf ("loading %s\n",namebuffer);
273
274         data = COM_LoadMallocFile(namebuffer, false);
275
276         if (!data)
277         {
278                 Con_Printf ("Couldn't load %s\n", namebuffer);
279                 return NULL;
280         }
281
282         info = GetWavinfo (s->name, data, com_filesize);
283         // LordHavoc: stereo sounds are now allowed (intended for music)
284         if (info.channels < 1 || info.channels > 2)
285         {
286                 Con_Printf ("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
287                 qfree(data);
288                 return NULL;
289         }
290         /*
291         if (info.channels != 1)
292         {
293                 Con_Printf ("%s is a stereo sample\n",s->name);
294                 return NULL;
295         }
296         */
297
298         stepscale = (float)info.rate / shm->speed;      
299         len = info.samples / stepscale;
300
301         len = len * info.width * info.channels;
302
303         sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name);
304         if (!sc)
305         {
306                 qfree(data);
307                 return NULL;
308         }
309         
310         sc->length = info.samples;
311         sc->loopstart = info.loopstart;
312         sc->speed = info.rate;
313         sc->width = info.width;
314         sc->stereo = info.channels == 2;
315
316         ResampleSfx (s, sc->speed, data + info.dataofs, s->name);
317
318         qfree(data);
319         return sc;
320 }
321
322
323
324 /*
325 ===============================================================================
326
327 WAV loading
328
329 ===============================================================================
330 */
331
332
333 byte    *data_p;
334 byte    *iff_end;
335 byte    *last_chunk;
336 byte    *iff_data;
337 int     iff_chunk_len;
338
339
340 short GetLittleShort(void)
341 {
342         short val = 0;
343         val = *data_p;
344         val = val + (*(data_p+1)<<8);
345         data_p += 2;
346         return val;
347 }
348
349 int GetLittleLong(void)
350 {
351         int val = 0;
352         val = *data_p;
353         val = val + (*(data_p+1)<<8);
354         val = val + (*(data_p+2)<<16);
355         val = val + (*(data_p+3)<<24);
356         data_p += 4;
357         return val;
358 }
359
360 void FindNextChunk(char *name)
361 {
362         while (1)
363         {
364                 data_p=last_chunk;
365
366                 if (data_p >= iff_end)
367                 {       // didn't find the chunk
368                         data_p = NULL;
369                         return;
370                 }
371                 
372                 data_p += 4;
373                 iff_chunk_len = GetLittleLong();
374                 if (iff_chunk_len < 0)
375                 {
376                         data_p = NULL;
377                         return;
378                 }
379 //              if (iff_chunk_len > 1024*1024)
380 //                      Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
381                 data_p -= 8;
382                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
383                 if (!strncmp(data_p, name, 4))
384                         return;
385         }
386 }
387
388 void FindChunk(char *name)
389 {
390         last_chunk = iff_data;
391         FindNextChunk (name);
392 }
393
394
395 void DumpChunks(void)
396 {
397         char    str[5];
398         
399         str[4] = 0;
400         data_p=iff_data;
401         do
402         {
403                 memcpy (str, data_p, 4);
404                 data_p += 4;
405                 iff_chunk_len = GetLittleLong();
406                 Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
407                 data_p += (iff_chunk_len + 1) & ~1;
408         } while (data_p < iff_end);
409 }
410
411 /*
412 ============
413 GetWavinfo
414 ============
415 */
416 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
417 {
418         wavinfo_t       info;
419         int     i;
420         int     format;
421         int             samples;
422
423         memset (&info, 0, sizeof(info));
424
425         if (!wav)
426                 return info;
427                 
428         iff_data = wav;
429         iff_end = wav + wavlength;
430
431 // find "RIFF" chunk
432         FindChunk("RIFF");
433         if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
434         {
435                 Con_Printf("Missing RIFF/WAVE chunks\n");
436                 return info;
437         }
438
439 // get "fmt " chunk
440         iff_data = data_p + 12;
441 // DumpChunks ();
442
443         FindChunk("fmt ");
444         if (!data_p)
445         {
446                 Con_Printf("Missing fmt chunk\n");
447                 return info;
448         }
449         data_p += 8;
450         format = GetLittleShort();
451         if (format != 1)
452         {
453                 Con_Printf("Microsoft PCM format only\n");
454                 return info;
455         }
456
457         info.channels = GetLittleShort();
458         info.rate = GetLittleLong();
459         data_p += 4+2;
460         info.width = GetLittleShort() / 8;
461
462 // get cue chunk
463         FindChunk("cue ");
464         if (data_p)
465         {
466                 data_p += 32;
467                 info.loopstart = GetLittleLong();
468 //              Con_Printf("loopstart=%d\n", sfx->loopstart);
469
470         // if the next chunk is a LIST chunk, look for a cue length marker
471                 FindNextChunk ("LIST");
472                 if (data_p)
473                 {
474                         if (!strncmp (data_p + 28, "mark", 4))
475                         {       // this is not a proper parse, but it works with cooledit...
476                                 data_p += 24;
477                                 i = GetLittleLong ();   // samples in loop
478                                 info.samples = info.loopstart + i;
479 //                              Con_Printf("looped length: %i\n", i);
480                         }
481                 }
482         }
483         else
484                 info.loopstart = -1;
485
486 // find data chunk
487         FindChunk("data");
488         if (!data_p)
489         {
490                 Con_Printf("Missing data chunk\n");
491                 return info;
492         }
493
494         data_p += 4;
495         samples = GetLittleLong () / info.width;
496
497         if (info.samples)
498         {
499                 if (samples < info.samples)
500                         Host_Error ("Sound %s has a bad loop length", name);
501         }
502         else
503                 info.samples = samples;
504
505         info.dataofs = data_p - wav;
506         
507         return info;
508 }
509