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