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