1 /*
2 Copyright (C) 1997-2001 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 "client.h"
23 #include "snd_loc.h"
24
25 int cache_full_cycle;
26
27 byte *S_Alloc (int size);
28
29 /*
30 ================
31 ResampleSfx
32 ================
33 */
ResampleSfx(sfx_t * sfx,int inrate,int inwidth,byte * data)34 void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
35 {
36 int outcount;
37 int srcsample;
38 float stepscale;
39 int i;
40 int sample, samplefrac, fracstep;
41 sfxcache_t *sc;
42
43 sc = sfx->cache;
44 if (!sc)
45 return;
46
47 stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
48
49 outcount = sc->length / stepscale;
50 sc->length = outcount;
51 if (sc->loopstart != -1)
52 sc->loopstart = sc->loopstart / stepscale;
53
54 sc->speed = dma.speed;
55 if (s_loadas8bit->value)
56 sc->width = 1;
57 else
58 sc->width = inwidth;
59 sc->stereo = 0;
60
61 // resample / decimate to the current source rate
62
63 if (stepscale == 1 && inwidth == 1 && sc->width == 1)
64 {
65 // fast special case
66 for (i=0 ; i<outcount ; i++)
67 ((signed char *)sc->data)[i]
68 = (int)( (unsigned char)(data[i]) - 128);
69 }
70 else
71 {
72 // general case
73 samplefrac = 0;
74 fracstep = stepscale*256;
75 for (i=0 ; i<outcount ; i++)
76 {
77 srcsample = samplefrac >> 8;
78 samplefrac += fracstep;
79 if (inwidth == 2)
80 sample = LittleShort ( ((short *)data)[srcsample] );
81 else
82 sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
83 if (sc->width == 2)
84 ((short *)sc->data)[i] = sample;
85 else
86 ((signed char *)sc->data)[i] = sample >> 8;
87 }
88 }
89 }
90
91 //=============================================================================
92
93 /*
94 ==============
95 S_LoadSound
96 ==============
97 */
S_LoadSound(sfx_t * s)98 sfxcache_t *S_LoadSound (sfx_t *s)
99 {
100 char namebuffer[MAX_QPATH];
101 byte *data;
102 wavinfo_t info;
103 int len;
104 float stepscale;
105 sfxcache_t *sc;
106 int size;
107 char *name;
108
109 if (s->name[0] == '*')
110 return NULL;
111
112 // see if still in memory
113 sc = s->cache;
114 if (sc)
115 return sc;
116
117 //Com_Printf ("S_LoadSound: %x\n", (int)stackbuf);
118 // load it in
119 if (s->truename)
120 name = s->truename;
121 else
122 name = s->name;
123
124 if (name[0] == '#')
125 strcpy(namebuffer, &name[1]);
126 else
127 Com_sprintf (namebuffer, sizeof(namebuffer), "sound/%s", name);
128
129 // Com_Printf ("loading %s\n",namebuffer);
130
131 size = FS_LoadFile (namebuffer, (void **)&data);
132
133 if (!data)
134 {
135 Com_DPrintf ("Couldn't load %s\n", namebuffer);
136 return NULL;
137 }
138
139 info = GetWavinfo (s->name, data, size);
140 if (info.channels != 1)
141 {
142 Com_Printf ("%s is a stereo sample\n",s->name);
143 FS_FreeFile (data);
144 return NULL;
145 }
146
147 stepscale = (float)info.rate / dma.speed;
148 len = info.samples / stepscale;
149
150 len = len * info.width * info.channels;
151
152 sc = s->cache = Z_Malloc (len + sizeof(sfxcache_t));
153 if (!sc)
154 {
155 FS_FreeFile (data);
156 return NULL;
157 }
158
159 sc->length = info.samples;
160 sc->loopstart = info.loopstart;
161 sc->speed = info.rate;
162 sc->width = info.width;
163 sc->stereo = info.channels;
164
165 ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);
166
167 FS_FreeFile (data);
168
169 return sc;
170 }
171
172
173
174 /*
175 ===============================================================================
176
177 WAV loading
178
179 ===============================================================================
180 */
181
182
183 byte *data_p;
184 byte *iff_end;
185 byte *last_chunk;
186 byte *iff_data;
187 int iff_chunk_len;
188
189
GetLittleShort(void)190 short GetLittleShort(void)
191 {
192 short val = 0;
193 val = *data_p;
194 val = val + (*(data_p+1)<<8);
195 data_p += 2;
196 return val;
197 }
198
GetLittleLong(void)199 int GetLittleLong(void)
200 {
201 int val = 0;
202 val = *data_p;
203 val = val + (*(data_p+1)<<8);
204 val = val + (*(data_p+2)<<16);
205 val = val + (*(data_p+3)<<24);
206 data_p += 4;
207 return val;
208 }
209
FindNextChunk(char * name)210 void FindNextChunk(char *name)
211 {
212 while (1)
213 {
214 data_p=last_chunk;
215
216 if (data_p >= iff_end)
217 { // didn't find the chunk
218 data_p = NULL;
219 return;
220 }
221
222 data_p += 4;
223 iff_chunk_len = GetLittleLong();
224 if (iff_chunk_len < 0)
225 {
226 data_p = NULL;
227 return;
228 }
229 // if (iff_chunk_len > 1024*1024)
230 // Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
231 data_p -= 8;
232 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
233 if (!strncmp((char *)data_p, name, 4))
234 return;
235 }
236 }
237
FindChunk(char * name)238 void FindChunk(char *name)
239 {
240 last_chunk = iff_data;
241 FindNextChunk (name);
242 }
243
244
DumpChunks(void)245 void DumpChunks(void)
246 {
247 char str[5];
248
249 str[4] = 0;
250 data_p=iff_data;
251 do
252 {
253 memcpy (str, data_p, 4);
254 data_p += 4;
255 iff_chunk_len = GetLittleLong();
256 Com_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
257 data_p += (iff_chunk_len + 1) & ~1;
258 } while (data_p < iff_end);
259 }
260
261 /*
262 ============
263 GetWavinfo
264 ============
265 */
GetWavinfo(char * name,byte * wav,int wavlength)266 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
267 {
268 wavinfo_t info;
269 int i;
270 int format;
271 int samples;
272
273 memset (&info, 0, sizeof(info));
274
275 if (!wav)
276 return info;
277
278 iff_data = wav;
279 iff_end = wav + wavlength;
280
281 // find "RIFF" chunk
282 FindChunk("RIFF");
283 if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
284 {
285 Com_Printf("Missing RIFF/WAVE chunks\n");
286 return info;
287 }
288
289 // get "fmt " chunk
290 iff_data = data_p + 12;
291 // DumpChunks ();
292
293 FindChunk("fmt ");
294 if (!data_p)
295 {
296 Com_Printf("Missing fmt chunk\n");
297 return info;
298 }
299 data_p += 8;
300 format = GetLittleShort();
301 if (format != 1)
302 {
303 Com_Printf("Microsoft PCM format only\n");
304 return info;
305 }
306
307 info.channels = GetLittleShort();
308 info.rate = GetLittleLong();
309 data_p += 4+2;
310 info.width = GetLittleShort() / 8;
311
312 // get cue chunk
313 FindChunk("cue ");
314 if (data_p)
315 {
316 data_p += 32;
317 info.loopstart = GetLittleLong();
318 // Com_Printf("loopstart=%d\n", sfx->loopstart);
319
320 // if the next chunk is a LIST chunk, look for a cue length marker
321 FindNextChunk ("LIST");
322 if (data_p)
323 {
324 if (!strncmp ((char *)data_p + 28, "mark", 4))
325 { // this is not a proper parse, but it works with cooledit...
326 data_p += 24;
327 i = GetLittleLong (); // samples in loop
328 info.samples = info.loopstart + i;
329 // Com_Printf("looped length: %i\n", i);
330 }
331 }
332 }
333 else
334 info.loopstart = -1;
335
336 // find data chunk
337 FindChunk("data");
338 if (!data_p)
339 {
340 Com_Printf("Missing data chunk\n");
341 return info;
342 }
343
344 data_p += 4;
345 samples = GetLittleLong () / info.width;
346
347 if (info.samples)
348 {
349 if (samples < info.samples)
350 Com_Error (ERR_DROP, "Sound %s has a bad loop length", name);
351 }
352 else
353 info.samples = samples;
354
355 info.dataofs = data_p - wav;
356
357 return info;
358 }
359
360