ResampleSfx clean up and a bug fix on resampled stereo
[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, int inwidth, 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
92         {
93 // general case
94                 Con_DPrintf("ResampleSfx: resampling sound %s\n", sfx->name);
95                 samplefrac = 0;
96                 fracstep = stepscale*256;
97                 if (sc->stereo) // LordHavoc: stereo sound support
98                 {
99                         if (sc->width == 2)
100                         {
101                                 for (i=0 ; i<outcount*2 ;)
102                                 {
103                                         srcsample = samplefrac >> 8;
104                                         samplefrac += fracstep;
105                                         srcsample <<= 1;
106                                         ((short *)sc->data)[i++] = LittleShort ( ((short *)data)[srcsample++] ); // left
107                                         ((short *)sc->data)[i++] = LittleShort ( ((short *)data)[srcsample  ] ); // right
108                                 }
109                         }
110                         else
111                         {
112                                 for (i=0 ; i<outcount*2 ;)
113                                 {
114                                         srcsample = samplefrac >> 8;
115                                         samplefrac += fracstep;
116                                         srcsample <<= 1;
117                                         ((signed char *)sc->data)[i++] = ((int)( (unsigned char)(data[srcsample++]) - 128) << 8) >> 8; // left
118                                         ((signed char *)sc->data)[i++] = ((int)( (unsigned char)(data[srcsample  ]) - 128) << 8) >> 8; // right
119                                 }
120                         }
121                 }
122                 else
123                 {
124                         if (sc->width == 2)
125                         {
126                                 for (i=0 ; i<outcount ; i++)
127                                 {
128                                         srcsample = samplefrac >> 8;
129                                         samplefrac += fracstep;
130                                         ((short *)sc->data)[i] = LittleShort ( ((short *)data)[srcsample] );
131                                 }
132                         }
133                         else
134                         {
135                                 for (i=0 ; i<outcount ; i++)
136                                 {
137                                         srcsample = samplefrac >> 8;
138                                         samplefrac += fracstep;
139                                         ((signed char *)sc->data)[i] = ((int)( (unsigned char)(data[srcsample]) - 128) << 8) >> 8;
140                                 }
141                         }
142                 }
143         }
144 }
145
146 //=============================================================================
147
148 /*
149 ==============
150 S_LoadSound
151 ==============
152 */
153 sfxcache_t *S_LoadSound (sfx_t *s)
154 {
155     char        namebuffer[256];
156         byte    *data;
157         wavinfo_t       info;
158         int             len;
159         float   stepscale;
160         sfxcache_t      *sc;
161         byte    stackbuf[1*1024];               // avoid dirtying the cache heap
162
163 // see if still in memory
164         sc = Cache_Check (&s->cache);
165         if (sc)
166                 return sc;
167
168 //Con_Printf ("S_LoadSound: %x\n", (int)stackbuf);
169 // load it in
170         strcpy(namebuffer, "sound/");
171         strcat(namebuffer, s->name);
172
173 //      Con_Printf ("loading %s\n",namebuffer);
174
175         data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf), false);
176
177         if (!data)
178         {
179                 Con_Printf ("Couldn't load %s\n", namebuffer);
180                 return NULL;
181         }
182
183         info = GetWavinfo (s->name, data, com_filesize);
184         // LordHavoc: stereo sounds are now allowed (intended for music)
185         if (info.channels < 1 || info.channels > 2)
186         {
187                 Con_Printf ("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
188                 return NULL;
189         }
190         /*
191         if (info.channels != 1)
192         {
193                 Con_Printf ("%s is a stereo sample\n",s->name);
194                 return NULL;
195         }
196         */
197
198         stepscale = (float)info.rate / shm->speed;      
199         len = info.samples / stepscale;
200
201         len = len * info.width * info.channels;
202
203         sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name);
204         if (!sc)
205                 return NULL;
206         
207         sc->length = info.samples;
208         sc->loopstart = info.loopstart;
209         sc->speed = info.rate;
210         sc->width = info.width;
211         sc->stereo = info.channels == 2;
212
213         ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);
214
215         return sc;
216 }
217
218
219
220 /*
221 ===============================================================================
222
223 WAV loading
224
225 ===============================================================================
226 */
227
228
229 byte    *data_p;
230 byte    *iff_end;
231 byte    *last_chunk;
232 byte    *iff_data;
233 int     iff_chunk_len;
234
235
236 short GetLittleShort(void)
237 {
238         short val = 0;
239         val = *data_p;
240         val = val + (*(data_p+1)<<8);
241         data_p += 2;
242         return val;
243 }
244
245 int GetLittleLong(void)
246 {
247         int val = 0;
248         val = *data_p;
249         val = val + (*(data_p+1)<<8);
250         val = val + (*(data_p+2)<<16);
251         val = val + (*(data_p+3)<<24);
252         data_p += 4;
253         return val;
254 }
255
256 void FindNextChunk(char *name)
257 {
258         while (1)
259         {
260                 data_p=last_chunk;
261
262                 if (data_p >= iff_end)
263                 {       // didn't find the chunk
264                         data_p = NULL;
265                         return;
266                 }
267                 
268                 data_p += 4;
269                 iff_chunk_len = GetLittleLong();
270                 if (iff_chunk_len < 0)
271                 {
272                         data_p = NULL;
273                         return;
274                 }
275 //              if (iff_chunk_len > 1024*1024)
276 //                      Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
277                 data_p -= 8;
278                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
279                 if (!strncmp(data_p, name, 4))
280                         return;
281         }
282 }
283
284 void FindChunk(char *name)
285 {
286         last_chunk = iff_data;
287         FindNextChunk (name);
288 }
289
290
291 void DumpChunks(void)
292 {
293         char    str[5];
294         
295         str[4] = 0;
296         data_p=iff_data;
297         do
298         {
299                 memcpy (str, data_p, 4);
300                 data_p += 4;
301                 iff_chunk_len = GetLittleLong();
302                 Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
303                 data_p += (iff_chunk_len + 1) & ~1;
304         } while (data_p < iff_end);
305 }
306
307 /*
308 ============
309 GetWavinfo
310 ============
311 */
312 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
313 {
314         wavinfo_t       info;
315         int     i;
316         int     format;
317         int             samples;
318
319         memset (&info, 0, sizeof(info));
320
321         if (!wav)
322                 return info;
323                 
324         iff_data = wav;
325         iff_end = wav + wavlength;
326
327 // find "RIFF" chunk
328         FindChunk("RIFF");
329         if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
330         {
331                 Con_Printf("Missing RIFF/WAVE chunks\n");
332                 return info;
333         }
334
335 // get "fmt " chunk
336         iff_data = data_p + 12;
337 // DumpChunks ();
338
339         FindChunk("fmt ");
340         if (!data_p)
341         {
342                 Con_Printf("Missing fmt chunk\n");
343                 return info;
344         }
345         data_p += 8;
346         format = GetLittleShort();
347         if (format != 1)
348         {
349                 Con_Printf("Microsoft PCM format only\n");
350                 return info;
351         }
352
353         info.channels = GetLittleShort();
354         info.rate = GetLittleLong();
355         data_p += 4+2;
356         info.width = GetLittleShort() / 8;
357
358 // get cue chunk
359         FindChunk("cue ");
360         if (data_p)
361         {
362                 data_p += 32;
363                 info.loopstart = GetLittleLong();
364 //              Con_Printf("loopstart=%d\n", sfx->loopstart);
365
366         // if the next chunk is a LIST chunk, look for a cue length marker
367                 FindNextChunk ("LIST");
368                 if (data_p)
369                 {
370                         if (!strncmp (data_p + 28, "mark", 4))
371                         {       // this is not a proper parse, but it works with cooledit...
372                                 data_p += 24;
373                                 i = GetLittleLong ();   // samples in loop
374                                 info.samples = info.loopstart + i;
375 //                              Con_Printf("looped length: %i\n", i);
376                         }
377                 }
378         }
379         else
380                 info.loopstart = -1;
381
382 // find data chunk
383         FindChunk("data");
384         if (!data_p)
385         {
386                 Con_Printf("Missing data chunk\n");
387                 return info;
388         }
389
390         data_p += 4;
391         samples = GetLittleLong () / info.width;
392
393         if (info.samples)
394         {
395                 if (samples < info.samples)
396                         Host_Error ("Sound %s has a bad loop length", name);
397         }
398         else
399                 info.samples = samples;
400
401         info.dataofs = data_p - wav;
402         
403         return info;
404 }
405