every malloc/calloc/free converted to qmalloc/qfree with tracking (memstats command...
[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
230 // see if still in memory
231         sc = Cache_Check (&s->cache);
232         if (sc)
233                 return sc;
234
235 //Con_Printf ("S_LoadSound: %x\n", (int)stackbuf);
236 // load it in
237         strcpy(namebuffer, "sound/");
238         strcat(namebuffer, s->name);
239
240 //      Con_Printf ("loading %s\n",namebuffer);
241
242         data = COM_LoadMallocFile(namebuffer, false);
243
244         if (!data)
245         {
246                 Con_Printf ("Couldn't load %s\n", namebuffer);
247                 return NULL;
248         }
249
250         info = GetWavinfo (s->name, data, com_filesize);
251         // LordHavoc: stereo sounds are now allowed (intended for music)
252         if (info.channels < 1 || info.channels > 2)
253         {
254                 Con_Printf ("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
255                 qfree(data);
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         {
274                 qfree(data);
275                 return NULL;
276         }
277         
278         sc->length = info.samples;
279         sc->loopstart = info.loopstart;
280         sc->speed = info.rate;
281         sc->width = info.width;
282         sc->stereo = info.channels == 2;
283
284         ResampleSfx (s, sc->speed, data + info.dataofs);
285
286         qfree(data);
287         return sc;
288 }
289
290
291
292 /*
293 ===============================================================================
294
295 WAV loading
296
297 ===============================================================================
298 */
299
300
301 byte    *data_p;
302 byte    *iff_end;
303 byte    *last_chunk;
304 byte    *iff_data;
305 int     iff_chunk_len;
306
307
308 short GetLittleShort(void)
309 {
310         short val = 0;
311         val = *data_p;
312         val = val + (*(data_p+1)<<8);
313         data_p += 2;
314         return val;
315 }
316
317 int GetLittleLong(void)
318 {
319         int val = 0;
320         val = *data_p;
321         val = val + (*(data_p+1)<<8);
322         val = val + (*(data_p+2)<<16);
323         val = val + (*(data_p+3)<<24);
324         data_p += 4;
325         return val;
326 }
327
328 void FindNextChunk(char *name)
329 {
330         while (1)
331         {
332                 data_p=last_chunk;
333
334                 if (data_p >= iff_end)
335                 {       // didn't find the chunk
336                         data_p = NULL;
337                         return;
338                 }
339                 
340                 data_p += 4;
341                 iff_chunk_len = GetLittleLong();
342                 if (iff_chunk_len < 0)
343                 {
344                         data_p = NULL;
345                         return;
346                 }
347 //              if (iff_chunk_len > 1024*1024)
348 //                      Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
349                 data_p -= 8;
350                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
351                 if (!strncmp(data_p, name, 4))
352                         return;
353         }
354 }
355
356 void FindChunk(char *name)
357 {
358         last_chunk = iff_data;
359         FindNextChunk (name);
360 }
361
362
363 void DumpChunks(void)
364 {
365         char    str[5];
366         
367         str[4] = 0;
368         data_p=iff_data;
369         do
370         {
371                 memcpy (str, data_p, 4);
372                 data_p += 4;
373                 iff_chunk_len = GetLittleLong();
374                 Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
375                 data_p += (iff_chunk_len + 1) & ~1;
376         } while (data_p < iff_end);
377 }
378
379 /*
380 ============
381 GetWavinfo
382 ============
383 */
384 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
385 {
386         wavinfo_t       info;
387         int     i;
388         int     format;
389         int             samples;
390
391         memset (&info, 0, sizeof(info));
392
393         if (!wav)
394                 return info;
395                 
396         iff_data = wav;
397         iff_end = wav + wavlength;
398
399 // find "RIFF" chunk
400         FindChunk("RIFF");
401         if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
402         {
403                 Con_Printf("Missing RIFF/WAVE chunks\n");
404                 return info;
405         }
406
407 // get "fmt " chunk
408         iff_data = data_p + 12;
409 // DumpChunks ();
410
411         FindChunk("fmt ");
412         if (!data_p)
413         {
414                 Con_Printf("Missing fmt chunk\n");
415                 return info;
416         }
417         data_p += 8;
418         format = GetLittleShort();
419         if (format != 1)
420         {
421                 Con_Printf("Microsoft PCM format only\n");
422                 return info;
423         }
424
425         info.channels = GetLittleShort();
426         info.rate = GetLittleLong();
427         data_p += 4+2;
428         info.width = GetLittleShort() / 8;
429
430 // get cue chunk
431         FindChunk("cue ");
432         if (data_p)
433         {
434                 data_p += 32;
435                 info.loopstart = GetLittleLong();
436 //              Con_Printf("loopstart=%d\n", sfx->loopstart);
437
438         // if the next chunk is a LIST chunk, look for a cue length marker
439                 FindNextChunk ("LIST");
440                 if (data_p)
441                 {
442                         if (!strncmp (data_p + 28, "mark", 4))
443                         {       // this is not a proper parse, but it works with cooledit...
444                                 data_p += 24;
445                                 i = GetLittleLong ();   // samples in loop
446                                 info.samples = info.loopstart + i;
447 //                              Con_Printf("looped length: %i\n", i);
448                         }
449                 }
450         }
451         else
452                 info.loopstart = -1;
453
454 // find data chunk
455         FindChunk("data");
456         if (!data_p)
457         {
458                 Con_Printf("Missing data chunk\n");
459                 return info;
460         }
461
462         data_p += 4;
463         samples = GetLittleLong () / info.width;
464
465         if (info.samples)
466         {
467                 if (samples < info.samples)
468                         Host_Error ("Sound %s has a bad loop length", name);
469         }
470         else
471                 info.samples = samples;
472
473         info.dataofs = data_p - wav;
474         
475         return info;
476 }
477