Added a mempool parameter to FS_LoadFile
[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 "snd_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] = ((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++ = in[srcsample  ];
78                                                 *out++ = in[srcsample+1];
79                                                 srcsample += fracstep;
80                                         }
81                                 }
82                                 else
83                                 {
84                                         for (i=0 ; i<outcount ; i++)
85                                         {
86                                                 *out++ = 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, tempmempool, 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         Mem_FreePool(&s->mempool);
246         s->mempool = Mem_AllocPool(s->name);
247         sc = s->sfxcache = Mem_Alloc(s->mempool, len + sizeof(sfxcache_t));
248         if (!sc)
249         {
250                 Con_Printf("failed to allocate memory for sound \"%s\"\n", s->name);
251                 Mem_FreePool(&s->mempool);
252                 Mem_Free(data);
253                 return NULL;
254         }
255
256         sc->length = info.samples;
257         sc->loopstart = info.loopstart;
258         sc->speed = info.rate;
259         sc->width = info.width;
260         sc->stereo = info.channels == 2;
261
262 #if BYTE_ORDER != LITTLE_ENDIAN
263         // We must convert the WAV data from little endian
264         // to the machine endianess before resampling it
265         if (info.width == 2)
266         {
267                 int i;
268                 short* ptr;
269
270                 len = info.samples * info.channels;
271                 ptr = (short*)(data + info.dataofs);
272                 for (i = 0; i < len; i++)
273                         ptr[i] = LittleShort (ptr[i]);
274         }
275 #endif
276
277         ResampleSfx(sc, data + info.dataofs, s->name);
278
279         Mem_Free(data);
280         return sc;
281 }
282
283
284 /*
285 ==============
286 S_LoadSound
287 ==============
288 */
289 sfxcache_t *S_LoadSound (sfx_t *s, int complain)
290 {
291         char namebuffer[MAX_QPATH];
292         size_t len;
293         sfxcache_t *sc;
294         qboolean modified_name = false;
295
296         // see if still in memory
297         if (!shm || !shm->speed)
298                 return NULL;
299         if (s->sfxcache && (s->sfxcache->speed == shm->speed))
300                 return s->sfxcache;
301
302         len = snprintf (namebuffer, sizeof (namebuffer), "sound/%s", s->name);
303         if (len >= sizeof (namebuffer))
304                 return NULL;
305
306         // Try to load it as a WAV file
307         sc = S_LoadWavFile (namebuffer, s);
308         if (sc != NULL)
309                 return sc;
310
311         // Else, try to load it as an Ogg Vorbis file
312         if (!strcasecmp (namebuffer + len - 4, ".wav"))
313         {
314                 strcpy (namebuffer + len - 3, "ogg");
315                 modified_name = true;
316         }
317         sc = OGG_LoadVorbisFile (namebuffer, s);
318         if (sc != NULL)
319                 return sc;
320
321         // Can't load the sound!
322         if (!complain)
323                 s->flags |= SFXFLAG_SILENTLYMISSING;
324         else
325                 s->flags &= ~SFXFLAG_SILENTLYMISSING;
326         if (complain)
327         {
328                 if (modified_name)
329                         strcpy (namebuffer + len - 3, "wav");
330                 Con_Printf("Couldn't load %s\n", namebuffer);
331         }
332         return NULL;
333 }
334
335 void S_UnloadSound(sfx_t *s)
336 {
337         if (s->sfxcache)
338         {
339                 s->sfxcache = NULL;
340                 Mem_FreePool(&s->mempool);
341         }
342 }
343
344
345 /*
346 ===============================================================================
347
348 WAV loading
349
350 ===============================================================================
351 */
352
353
354 static qbyte *data_p;
355 static qbyte *iff_end;
356 static qbyte *last_chunk;
357 static qbyte *iff_data;
358 static int iff_chunk_len;
359
360
361 short GetLittleShort(void)
362 {
363         short val;
364
365         val = BuffLittleShort (data_p);
366         data_p += 2;
367
368         return val;
369 }
370
371 int GetLittleLong(void)
372 {
373         int val = 0;
374
375         val = BuffLittleLong (data_p);
376         data_p += 4;
377
378         return val;
379 }
380
381 void FindNextChunk(char *name)
382 {
383         while (1)
384         {
385                 data_p=last_chunk;
386
387                 if (data_p >= iff_end)
388                 {       // didn't find the chunk
389                         data_p = NULL;
390                         return;
391                 }
392
393                 data_p += 4;
394                 iff_chunk_len = GetLittleLong();
395                 if (iff_chunk_len < 0)
396                 {
397                         data_p = NULL;
398                         return;
399                 }
400                 data_p -= 8;
401                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
402                 if (!strncmp(data_p, name, 4))
403                         return;
404         }
405 }
406
407 void FindChunk(char *name)
408 {
409         last_chunk = iff_data;
410         FindNextChunk (name);
411 }
412
413
414 void DumpChunks(void)
415 {
416         char str[5];
417
418         str[4] = 0;
419         data_p=iff_data;
420         do
421         {
422                 memcpy (str, data_p, 4);
423                 data_p += 4;
424                 iff_chunk_len = GetLittleLong();
425                 Con_Printf("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
426                 data_p += (iff_chunk_len + 1) & ~1;
427         } while (data_p < iff_end);
428 }
429
430 /*
431 ============
432 GetWavinfo
433 ============
434 */
435 wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength)
436 {
437         wavinfo_t info;
438         int i;
439         int format;
440         int samples;
441
442         memset (&info, 0, sizeof(info));
443
444         if (!wav)
445                 return info;
446
447         iff_data = wav;
448         iff_end = wav + wavlength;
449
450         // find "RIFF" chunk
451         FindChunk("RIFF");
452         if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
453         {
454                 Con_Print("Missing RIFF/WAVE chunks\n");
455                 return info;
456         }
457
458         // get "fmt " chunk
459         iff_data = data_p + 12;
460         //DumpChunks ();
461
462         FindChunk("fmt ");
463         if (!data_p)
464         {
465                 Con_Print("Missing fmt chunk\n");
466                 return info;
467         }
468         data_p += 8;
469         format = GetLittleShort();
470         if (format != 1)
471         {
472                 Con_Print("Microsoft PCM format only\n");
473                 return info;
474         }
475
476         info.channels = GetLittleShort();
477         info.rate = GetLittleLong();
478         data_p += 4+2;
479         info.width = GetLittleShort() / 8;
480
481         // get cue chunk
482         FindChunk("cue ");
483         if (data_p)
484         {
485                 data_p += 32;
486                 info.loopstart = GetLittleLong();
487
488                 // if the next chunk is a LIST chunk, look for a cue length marker
489                 FindNextChunk ("LIST");
490                 if (data_p)
491                 {
492                         if (!strncmp (data_p + 28, "mark", 4))
493                         {       // this is not a proper parse, but it works with cooledit...
494                                 data_p += 24;
495                                 i = GetLittleLong ();   // samples in loop
496                                 info.samples = info.loopstart + i;
497                         }
498                 }
499         }
500         else
501                 info.loopstart = -1;
502
503         // find data chunk
504         FindChunk("data");
505         if (!data_p)
506         {
507                 Con_Print("Missing data chunk\n");
508                 return info;
509         }
510
511         data_p += 4;
512         samples = GetLittleLong () / info.width / info.channels;
513
514         if (info.samples)
515         {
516                 if (samples < info.samples)
517                         Host_Error ("Sound %s has a bad loop length", name);
518         }
519         else
520                 info.samples = samples;
521
522         info.dataofs = data_p - wav;
523
524         return info;
525 }
526