1 /*
2 * Copyright (C) 1997-2001 Id Software, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * 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 along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59
17 * 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 * ================ ResampleSfx ================
31 */
32 void
ResampleSfx(sfx_t * sfx,int inrate,int inwidth,byte * data)33 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 = sfx->cache;
43 if (!sc)
44 return;
45
46 stepscale = (float)inrate / dma.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 = dma.speed;
54 if (s_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 /* fast special case */
64 for (i = 0; i < outcount; i++)
65 ((signed char *)sc->data)[i]
66 = (int)((unsigned char)(data[i]) - 128);
67 } else {
68 /* general case */
69 samplefrac = 0;
70 fracstep = stepscale * 256;
71 for (i = 0; i < outcount; i++) {
72 srcsample = samplefrac >> 8;
73 samplefrac += fracstep;
74 if (inwidth == 2)
75 sample = LittleShort(((short *)data)[srcsample]);
76 else
77 sample = (int)((unsigned char)(data[srcsample]) - 128) << 8;
78 if (sc->width == 2)
79 ((short *)sc->data)[i] = sample;
80 else
81 ((signed char *)sc->data)[i] = sample >> 8;
82 }
83 }
84 }
85
86 /*
87 * ===========================================================================
88 * ==
89 */
90
91 /*
92 * ============== S_LoadSound ==============
93 */
94 sfxcache_t *
S_LoadSound(sfx_t * s)95 S_LoadSound(sfx_t * s)
96 {
97 char namebuffer[MAX_QPATH];
98 byte *data;
99 wavinfo_t info;
100 int len;
101 float stepscale;
102 sfxcache_t *sc;
103 int size;
104 char *name;
105
106 if (s->name[0] == '*')
107 return NULL;
108
109 /* see if still in memory */
110 sc = s->cache;
111 if (sc)
112 return sc;
113
114 /* Com_Printf ("S_LoadSound: %x\n", (int)stackbuf); */
115 /* load it in */
116 if (s->truename)
117 name = s->truename;
118 else
119 name = s->name;
120
121 if (name[0] == '#')
122 Q_strncpyz(namebuffer, &name[1], sizeof(namebuffer));
123 else
124 Com_sprintf(namebuffer, sizeof(namebuffer), "sound/%s", name);
125
126 /* Com_Printf ("loading %s\n",namebuffer); */
127
128 size = FS_LoadFile(namebuffer, (void **)&data);
129
130 if (!data) {
131 Com_DPrintf("Couldn't load %s\n", namebuffer);
132 return NULL;
133 }
134 info = GetWavinfo(s->name, data, size);
135 if (info.channels != 1) {
136 Com_Printf("%s is a stereo sample\n", s->name);
137 FS_FreeFile(data);
138 return NULL;
139 }
140 stepscale = (float)info.rate / dma.speed;
141 len = info.samples / stepscale;
142
143 len = len * info.width * info.channels;
144
145 sc = s->cache = Z_Malloc(len + sizeof(sfxcache_t));
146 if (!sc) {
147 FS_FreeFile(data);
148 return NULL;
149 }
150 sc->length = info.samples;
151 sc->loopstart = info.loopstart;
152 sc->speed = info.rate;
153 sc->width = info.width;
154 sc->stereo = info.channels;
155
156 ResampleSfx(s, sc->speed, sc->width, data + info.dataofs);
157
158 FS_FreeFile(data);
159
160 return sc;
161 }
162
163
164
165 /*
166 *
167 * ============================================================================
168 * ===
169 *
170 * WAV loading
171 *
172 * ===========
173 * ===================================================================
174 */
175
176
177 byte *data_p;
178 byte *iff_end;
179 byte *last_chunk;
180 byte *iff_data;
181 int iff_chunk_len;
182
183
184 short
GetLittleShort(void)185 GetLittleShort(void)
186 {
187 short val = 0;
188
189 val = *data_p;
190 val = val + (*(data_p + 1) << 8);
191 data_p += 2;
192 return val;
193 }
194
195 int
GetLittleLong(void)196 GetLittleLong(void)
197 {
198 int val = 0;
199
200 val = *data_p;
201 val = val + (*(data_p + 1) << 8);
202 val = val + (*(data_p + 2) << 16);
203 val = val + (*(data_p + 3) << 24);
204 data_p += 4;
205 return val;
206 }
207
208 void
FindNextChunk(char * name)209 FindNextChunk(char *name)
210 {
211 while (1) {
212 data_p = last_chunk;
213
214 if (data_p >= iff_end) { /* didn't find the chunk */
215 data_p = NULL;
216 return;
217 }
218 data_p += 4;
219 iff_chunk_len = GetLittleLong();
220 if (iff_chunk_len < 0) {
221 data_p = NULL;
222 return;
223 }
224 /* if (iff_chunk_len > 1024*1024) */
225
226 /*
227 * Sys_Error ("FindNextChunk: %i length is past the 1 meg
228 * sanity limit", iff_chunk_len);
229 */
230 data_p -= 8;
231 last_chunk = data_p + 8 + ((iff_chunk_len + 1) & ~1);
232 if (!strncmp((char *)data_p, name, 4))
233 return;
234 }
235 }
236
237 void
FindChunk(char * name)238 FindChunk(char *name)
239 {
240 last_chunk = iff_data;
241 FindNextChunk(name);
242 }
243
244
245 void
DumpChunks(void)246 DumpChunks(void)
247 {
248 char str[5];
249
250 str[4] = 0;
251 data_p = iff_data;
252 do {
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 * ============ GetWavinfo ============
263 */
264 wavinfo_t
GetWavinfo(char * name,byte * wav,int wavlength)265 GetWavinfo(char *name, byte * wav, int wavlength)
266 {
267 wavinfo_t info;
268 int i;
269 int format;
270 int samples;
271
272 memset(&info, 0, sizeof(info));
273
274 if (!wav)
275 return info;
276
277 iff_data = wav;
278 iff_end = wav + wavlength;
279
280 /* find "RIFF" chunk */
281 FindChunk("RIFF");
282 if (!(data_p && !strncmp((char *)data_p + 8, "WAVE", 4))) {
283 Com_Printf("Missing RIFF/WAVE chunks\n");
284 return info;
285 }
286 /* get "fmt " chunk */
287 iff_data = data_p + 12;
288 /* DumpChunks (); */
289
290 FindChunk("fmt ");
291 if (!data_p) {
292 Com_Printf("Missing fmt chunk\n");
293 return info;
294 }
295 data_p += 8;
296 format = GetLittleShort();
297 if (format != 1) {
298 Com_Printf("Microsoft PCM format only\n");
299 return info;
300 }
301 info.channels = GetLittleShort();
302 info.rate = GetLittleLong();
303 data_p += 4 + 2;
304 info.width = GetLittleShort() / 8;
305
306 /* get cue chunk */
307 FindChunk("cue ");
308 if (data_p) {
309 data_p += 32;
310 info.loopstart = GetLittleLong();
311 /* Com_Printf("loopstart=%d\n", sfx->loopstart); */
312
313 /*
314 * if the next chunk is a LIST chunk, look for a cue length
315 * marker
316 */
317 FindNextChunk("LIST");
318 if (data_p) {
319 if (!strncmp((char *)data_p + 28, "mark", 4)) { /* this is not a proper
320 * parse, but it works
321 * with cooledit... */
322 data_p += 24;
323 i = GetLittleLong(); /* samples in loop */
324 info.samples = info.loopstart + i;
325 /* Com_Printf("looped length: %i\n", i); */
326 }
327 }
328 } else
329 info.loopstart = -1;
330
331 /* find data chunk */
332 FindChunk("data");
333 if (!data_p) {
334 Com_Printf("Missing data chunk\n");
335 return info;
336 }
337 data_p += 4;
338 samples = GetLittleLong() / info.width;
339
340 if (info.samples) {
341 if (samples < info.samples)
342 Com_Error(ERR_DROP, "Sound %s has a bad loop length", name);
343 } else
344 info.samples = samples;
345
346 info.dataofs = data_p - wav;
347
348 return info;
349 }
350