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