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