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