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