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