44.1khz sound, interpolated resampling, killed cvar loadas8bit
[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                         samplefrac >> 8;
109                         srcsample = 0;
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                         if (sc->width == 2)
158                         {
159                                 short *out = (void *)sc->data, *in = (void *)data;
160                                 if (sc->stereo) // LordHavoc: stereo sound support
161                                 {
162                                         for (i=0 ; i<outcount ;)
163                                         {
164                                                 srcsample = (samplefrac >> 7) & ~1;
165                                                 samplefrac += fracstep;
166                                                 *out++ = (short) ((int) ((LittleShort (in[srcsample  ]) * (256 - (samplefrac & 255)) + LittleShort (in[srcsample+2]) * (samplefrac & 255)) >> 8));
167                                                 *out++ = (short) ((int) ((LittleShort (in[srcsample+1]) * (256 - (samplefrac & 255)) + LittleShort (in[srcsample+3]) * (samplefrac & 255)) >> 8));
168                                         }
169                                 }
170                                 else
171                                 {
172                                         for (i=0 ; i<outcount ; i++)
173                                         {
174                                                 srcsample = samplefrac >> 8;
175                                                 samplefrac += fracstep;
176                                                 *out++ = (short) ((int) ((LittleShort (in[srcsample]  ) * (256 - (samplefrac & 255)) + LittleShort (in[srcsample+1]) * (samplefrac & 255)) >> 8));
177                                         }
178                                 }
179                         }
180                         else
181                         {
182                                 signed char *out = (void *)sc->data, *in = (void *)data;
183                                 if (sc->stereo) // LordHavoc: stereo sound support
184                                 {
185                                         for (i=0 ; i<outcount ;)
186                                         {
187                                                 srcsample = (samplefrac >> 7) & ~1;
188                                                 samplefrac += fracstep;
189                                                 *out++ = (signed char) ((int) (((((unsigned char) in[srcsample  ] - 128) * (256 - (samplefrac & 255))) + (((unsigned char) in[srcsample+2] - 128) * (samplefrac & 255))) >> 8));
190                                                 *out++ = (signed char) ((int) (((((unsigned char) in[srcsample+1] - 128) * (256 - (samplefrac & 255))) + (((unsigned char) in[srcsample+3] - 128) * (samplefrac & 255))) >> 8));
191                                         }
192                                 }
193                                 else
194                                 {
195                                         for (i=0 ; i<outcount ; i++)
196                                         {
197                                                 srcsample = samplefrac >> 8;
198                                                 samplefrac += fracstep;
199                                                 *out++ = (signed char) ((int) (((((unsigned char) in[srcsample  ] - 128) * (256 - (samplefrac & 255))) + (((unsigned char) in[srcsample+1] - 128) * (samplefrac & 255))) >> 8));
200                                         }
201                                 }
202                         }
203                 }
204         }
205 }
206
207 //=============================================================================
208
209 /*
210 ==============
211 S_LoadSound
212 ==============
213 */
214 sfxcache_t *S_LoadSound (sfx_t *s)
215 {
216     char        namebuffer[256];
217         byte    *data;
218         wavinfo_t       info;
219         int             len;
220         float   stepscale;
221         sfxcache_t      *sc;
222         byte    stackbuf[1*1024];               // avoid dirtying the cache heap
223
224 // see if still in memory
225         sc = Cache_Check (&s->cache);
226         if (sc)
227                 return sc;
228
229 //Con_Printf ("S_LoadSound: %x\n", (int)stackbuf);
230 // load it in
231         strcpy(namebuffer, "sound/");
232         strcat(namebuffer, s->name);
233
234 //      Con_Printf ("loading %s\n",namebuffer);
235
236         data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf), false);
237
238         if (!data)
239         {
240                 Con_Printf ("Couldn't load %s\n", namebuffer);
241                 return NULL;
242         }
243
244         info = GetWavinfo (s->name, data, com_filesize);
245         // LordHavoc: stereo sounds are now allowed (intended for music)
246         if (info.channels < 1 || info.channels > 2)
247         {
248                 Con_Printf ("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
249                 return NULL;
250         }
251         /*
252         if (info.channels != 1)
253         {
254                 Con_Printf ("%s is a stereo sample\n",s->name);
255                 return NULL;
256         }
257         */
258
259         stepscale = (float)info.rate / shm->speed;      
260         len = info.samples / stepscale;
261
262         len = len * info.width * info.channels;
263
264         sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name);
265         if (!sc)
266                 return NULL;
267         
268         sc->length = info.samples;
269         sc->loopstart = info.loopstart;
270         sc->speed = info.rate;
271         sc->width = info.width;
272         sc->stereo = info.channels == 2;
273
274         ResampleSfx (s, sc->speed, data + info.dataofs);
275
276         return sc;
277 }
278
279
280
281 /*
282 ===============================================================================
283
284 WAV loading
285
286 ===============================================================================
287 */
288
289
290 byte    *data_p;
291 byte    *iff_end;
292 byte    *last_chunk;
293 byte    *iff_data;
294 int     iff_chunk_len;
295
296
297 short GetLittleShort(void)
298 {
299         short val = 0;
300         val = *data_p;
301         val = val + (*(data_p+1)<<8);
302         data_p += 2;
303         return val;
304 }
305
306 int GetLittleLong(void)
307 {
308         int val = 0;
309         val = *data_p;
310         val = val + (*(data_p+1)<<8);
311         val = val + (*(data_p+2)<<16);
312         val = val + (*(data_p+3)<<24);
313         data_p += 4;
314         return val;
315 }
316
317 void FindNextChunk(char *name)
318 {
319         while (1)
320         {
321                 data_p=last_chunk;
322
323                 if (data_p >= iff_end)
324                 {       // didn't find the chunk
325                         data_p = NULL;
326                         return;
327                 }
328                 
329                 data_p += 4;
330                 iff_chunk_len = GetLittleLong();
331                 if (iff_chunk_len < 0)
332                 {
333                         data_p = NULL;
334                         return;
335                 }
336 //              if (iff_chunk_len > 1024*1024)
337 //                      Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
338                 data_p -= 8;
339                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
340                 if (!strncmp(data_p, name, 4))
341                         return;
342         }
343 }
344
345 void FindChunk(char *name)
346 {
347         last_chunk = iff_data;
348         FindNextChunk (name);
349 }
350
351
352 void DumpChunks(void)
353 {
354         char    str[5];
355         
356         str[4] = 0;
357         data_p=iff_data;
358         do
359         {
360                 memcpy (str, data_p, 4);
361                 data_p += 4;
362                 iff_chunk_len = GetLittleLong();
363                 Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
364                 data_p += (iff_chunk_len + 1) & ~1;
365         } while (data_p < iff_end);
366 }
367
368 /*
369 ============
370 GetWavinfo
371 ============
372 */
373 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
374 {
375         wavinfo_t       info;
376         int     i;
377         int     format;
378         int             samples;
379
380         memset (&info, 0, sizeof(info));
381
382         if (!wav)
383                 return info;
384                 
385         iff_data = wav;
386         iff_end = wav + wavlength;
387
388 // find "RIFF" chunk
389         FindChunk("RIFF");
390         if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
391         {
392                 Con_Printf("Missing RIFF/WAVE chunks\n");
393                 return info;
394         }
395
396 // get "fmt " chunk
397         iff_data = data_p + 12;
398 // DumpChunks ();
399
400         FindChunk("fmt ");
401         if (!data_p)
402         {
403                 Con_Printf("Missing fmt chunk\n");
404                 return info;
405         }
406         data_p += 8;
407         format = GetLittleShort();
408         if (format != 1)
409         {
410                 Con_Printf("Microsoft PCM format only\n");
411                 return info;
412         }
413
414         info.channels = GetLittleShort();
415         info.rate = GetLittleLong();
416         data_p += 4+2;
417         info.width = GetLittleShort() / 8;
418
419 // get cue chunk
420         FindChunk("cue ");
421         if (data_p)
422         {
423                 data_p += 32;
424                 info.loopstart = GetLittleLong();
425 //              Con_Printf("loopstart=%d\n", sfx->loopstart);
426
427         // if the next chunk is a LIST chunk, look for a cue length marker
428                 FindNextChunk ("LIST");
429                 if (data_p)
430                 {
431                         if (!strncmp (data_p + 28, "mark", 4))
432                         {       // this is not a proper parse, but it works with cooledit...
433                                 data_p += 24;
434                                 i = GetLittleLong ();   // samples in loop
435                                 info.samples = info.loopstart + i;
436 //                              Con_Printf("looped length: %i\n", i);
437                         }
438                 }
439         }
440         else
441                 info.loopstart = -1;
442
443 // find data chunk
444         FindChunk("data");
445         if (!data_p)
446         {
447                 Con_Printf("Missing data chunk\n");
448                 return info;
449         }
450
451         data_p += 4;
452         samples = GetLittleLong () / info.width;
453
454         if (info.samples)
455         {
456                 if (samples < info.samples)
457                         Host_Error ("Sound %s has a bad loop length", name);
458         }
459         else
460                 info.samples = samples;
461
462         info.dataofs = data_p - wav;
463         
464         return info;
465 }
466