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