]> icculus.org git repositories - divverent/darkplaces.git/blob - snd_mem.c
sorry Mathieu, can't use va() in filesystem code, not safe. (this was causing the...
[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         info = GetWavinfo (s->name, data, fs_filesize);
229         // Stereo sounds are allowed (intended for music)
230         if (info.channels < 1 || info.channels > 2)
231         {
232                 Con_Printf("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
233                 Mem_Free(data);
234                 return NULL;
235         }
236
237         // calculate resampled length
238         len = (int) ((double) info.samples * (double) shm->speed / (double) info.rate);
239         len = len * info.width * info.channels;
240
241         // FIXME: add S_UnloadSounds or something?
242         Mem_FreePool(&s->mempool);
243         s->mempool = Mem_AllocPool(s->name);
244         sc = s->sfxcache = Mem_Alloc(s->mempool, len + sizeof(sfxcache_t));
245         if (!sc)
246         {
247                 Con_Printf("failed to allocate memory for sound \"%s\"\n", s->name);
248                 Mem_FreePool(&s->mempool);
249                 Mem_Free(data);
250                 return NULL;
251         }
252
253         sc->length = info.samples;
254         sc->loopstart = info.loopstart;
255         sc->speed = info.rate;
256         sc->width = info.width;
257         sc->stereo = info.channels == 2;
258
259         ResampleSfx(sc, data + info.dataofs, s->name);
260
261         Mem_Free(data);
262         return sc;
263 }
264
265
266 /*
267 ==============
268 S_LoadSound
269 ==============
270 */
271 sfxcache_t *S_LoadSound (sfx_t *s, int complain)
272 {
273         char namebuffer[MAX_QPATH];
274         size_t len;
275         sfxcache_t *sc;
276         qboolean modified_name = false;
277
278         // see if still in memory
279         if (!shm || !shm->speed)
280                 return NULL;
281         if (s->sfxcache && (s->sfxcache->speed == shm->speed))
282                 return s->sfxcache;
283
284         s->silentlymissing = !complain;
285
286         len = snprintf (namebuffer, sizeof (namebuffer), "sound/%s", s->name);
287         if (len >= sizeof (namebuffer))
288                 return NULL;
289
290         // Try to load it as a WAV file
291         sc = S_LoadWavFile (namebuffer, s);
292         if (sc != NULL)
293                 return sc;
294
295         // Else, try to load it as an Ogg Vorbis file
296         if (!strcasecmp (namebuffer + len - 4, ".wav"))
297         {
298                 strcpy (namebuffer + len - 3, "ogg");
299                 modified_name = true;
300         }
301         sc = OGG_LoadVorbisFile (namebuffer, s);
302         if (sc != NULL)
303                 return sc;
304
305         // Can't load the sound!
306         if (complain)
307         {
308                 if (modified_name)
309                         strcpy (namebuffer + len - 3, "wav");
310                 Con_Printf ("Couldn't load %s\n", namebuffer);
311         }
312         return NULL;
313 }
314
315 void S_UnloadSound(sfx_t *s)
316 {
317         if (s->sfxcache)
318         {
319                 s->sfxcache = NULL;
320                 Mem_FreePool(&s->mempool);
321         }
322 }
323
324
325 /*
326 ===============================================================================
327
328 WAV loading
329
330 ===============================================================================
331 */
332
333
334 qbyte *data_p;
335 qbyte *iff_end;
336 qbyte *last_chunk;
337 qbyte *iff_data;
338 int iff_chunk_len;
339
340
341 short GetLittleShort(void)
342 {
343         short val = 0;
344         val = *data_p;
345         val = val + (*(data_p+1)<<8);
346         data_p += 2;
347         return val;
348 }
349
350 int GetLittleLong(void)
351 {
352         int val = 0;
353         val = *data_p;
354         val = val + (*(data_p+1)<<8);
355         val = val + (*(data_p+2)<<16);
356         val = val + (*(data_p+3)<<24);
357         data_p += 4;
358         return val;
359 }
360
361 void FindNextChunk(char *name)
362 {
363         while (1)
364         {
365                 data_p=last_chunk;
366
367                 if (data_p >= iff_end)
368                 {       // didn't find the chunk
369                         data_p = NULL;
370                         return;
371                 }
372
373                 data_p += 4;
374                 iff_chunk_len = GetLittleLong();
375                 if (iff_chunk_len < 0)
376                 {
377                         data_p = NULL;
378                         return;
379                 }
380                 data_p -= 8;
381                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
382                 if (!strncmp(data_p, name, 4))
383                         return;
384         }
385 }
386
387 void FindChunk(char *name)
388 {
389         last_chunk = iff_data;
390         FindNextChunk (name);
391 }
392
393
394 void DumpChunks(void)
395 {
396         char str[5];
397
398         str[4] = 0;
399         data_p=iff_data;
400         do
401         {
402                 memcpy (str, data_p, 4);
403                 data_p += 4;
404                 iff_chunk_len = GetLittleLong();
405                 Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
406                 data_p += (iff_chunk_len + 1) & ~1;
407         } while (data_p < iff_end);
408 }
409
410 /*
411 ============
412 GetWavinfo
413 ============
414 */
415 wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength)
416 {
417         wavinfo_t info;
418         int i;
419         int format;
420         int samples;
421
422         memset (&info, 0, sizeof(info));
423
424         if (!wav)
425                 return info;
426
427         iff_data = wav;
428         iff_end = wav + wavlength;
429
430         // find "RIFF" chunk
431         FindChunk("RIFF");
432         if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
433         {
434                 Con_Printf("Missing RIFF/WAVE chunks\n");
435                 return info;
436         }
437
438         // get "fmt " chunk
439         iff_data = data_p + 12;
440         //DumpChunks ();
441
442         FindChunk("fmt ");
443         if (!data_p)
444         {
445                 Con_Printf("Missing fmt chunk\n");
446                 return info;
447         }
448         data_p += 8;
449         format = GetLittleShort();
450         if (format != 1)
451         {
452                 Con_Printf("Microsoft PCM format only\n");
453                 return info;
454         }
455
456         info.channels = GetLittleShort();
457         info.rate = GetLittleLong();
458         data_p += 4+2;
459         info.width = GetLittleShort() / 8;
460
461         // get cue chunk
462         FindChunk("cue ");
463         if (data_p)
464         {
465                 data_p += 32;
466                 info.loopstart = GetLittleLong();
467
468                 // if the next chunk is a LIST chunk, look for a cue length marker
469                 FindNextChunk ("LIST");
470                 if (data_p)
471                 {
472                         if (!strncmp (data_p + 28, "mark", 4))
473                         {       // this is not a proper parse, but it works with cooledit...
474                                 data_p += 24;
475                                 i = GetLittleLong ();   // samples in loop
476                                 info.samples = info.loopstart + i;
477                         }
478                 }
479         }
480         else
481                 info.loopstart = -1;
482
483         // find data chunk
484         FindChunk("data");
485         if (!data_p)
486         {
487                 Con_Printf("Missing data chunk\n");
488                 return info;
489         }
490
491         data_p += 4;
492         samples = GetLittleLong () / info.width / info.channels;
493
494         if (info.samples)
495         {
496                 if (samples < info.samples)
497                         Host_Error ("Sound %s has a bad loop length", name);
498         }
499         else
500                 info.samples = samples;
501
502         info.dataofs = data_p - wav;
503
504         return info;
505 }
506