]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_wav.c
Unbreak Nexuiz weapon animation.
[xonotic/darkplaces.git] / snd_wav.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:
17
18                 Free Software Foundation, Inc.
19                 59 Temple Place - Suite 330
20                 Boston, MA  02111-1307, USA
21
22 */
23
24
25 #include "quakedef.h"
26 #include "snd_main.h"
27 #include "snd_wav.h"
28
29
30 typedef struct wavinfo_s
31 {
32         int             rate;
33         int             width;
34         int             channels;
35         int             loopstart;
36         int             samples;
37         int             dataofs;                // chunk starts this many bytes from file start
38 } wavinfo_t;
39
40
41 static unsigned char *data_p;
42 static unsigned char *iff_end;
43 static unsigned char *last_chunk;
44 static unsigned char *iff_data;
45 static int iff_chunk_len;
46
47
48 static short GetLittleShort(void)
49 {
50         short val;
51
52         val = BuffLittleShort (data_p);
53         data_p += 2;
54
55         return val;
56 }
57
58 static int GetLittleLong(void)
59 {
60         int val = 0;
61
62         val = BuffLittleLong (data_p);
63         data_p += 4;
64
65         return val;
66 }
67
68 static void FindNextChunk(const char *name)
69 {
70         while (1)
71         {
72                 data_p=last_chunk;
73
74                 if (data_p >= iff_end)
75                 {       // didn't find the chunk
76                         data_p = NULL;
77                         return;
78                 }
79
80                 data_p += 4;
81                 iff_chunk_len = GetLittleLong();
82                 if (iff_chunk_len < 0)
83                 {
84                         data_p = NULL;
85                         return;
86                 }
87                 if (data_p + iff_chunk_len > iff_end)
88                 {
89                         // truncated chunk!
90                         data_p = NULL;
91                         return;
92                 }
93                 data_p -= 8;
94                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
95                 if (!strncmp((const char *)data_p, name, 4))
96                         return;
97         }
98 }
99
100 static void FindChunk(const char *name)
101 {
102         last_chunk = iff_data;
103         FindNextChunk (name);
104 }
105
106
107 /*
108 static void DumpChunks(void)
109 {
110         char str[5];
111
112         str[4] = 0;
113         data_p=iff_data;
114         do
115         {
116                 memcpy (str, data_p, 4);
117                 data_p += 4;
118                 iff_chunk_len = GetLittleLong();
119                 Con_Printf("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
120                 data_p += (iff_chunk_len + 1) & ~1;
121         } while (data_p < iff_end);
122 }
123 */
124
125
126 /*
127 ============
128 GetWavinfo
129 ============
130 */
131 static wavinfo_t GetWavinfo (char *name, unsigned char *wav, int wavlength)
132 {
133         wavinfo_t info;
134         int i;
135         int format;
136         int samples;
137
138         memset (&info, 0, sizeof(info));
139
140         if (!wav)
141                 return info;
142
143         iff_data = wav;
144         iff_end = wav + wavlength;
145
146         // find "RIFF" chunk
147         FindChunk("RIFF");
148         if (!(data_p && !strncmp((const char *)data_p+8, "WAVE", 4)))
149         {
150                 Con_Print("Missing RIFF/WAVE chunks\n");
151                 return info;
152         }
153
154         // get "fmt " chunk
155         iff_data = data_p + 12;
156         //DumpChunks ();
157
158         FindChunk("fmt ");
159         if (!data_p)
160         {
161                 Con_Print("Missing fmt chunk\n");
162                 return info;
163         }
164         data_p += 8;
165         format = GetLittleShort();
166         if (format != 1)
167         {
168                 Con_Print("Microsoft PCM format only\n");
169                 return info;
170         }
171
172         info.channels = GetLittleShort();
173         info.rate = GetLittleLong();
174         data_p += 4+2;
175         info.width = GetLittleShort() / 8;
176
177         // get cue chunk
178         FindChunk("cue ");
179         if (data_p)
180         {
181                 data_p += 32;
182                 info.loopstart = GetLittleLong();
183
184                 // if the next chunk is a LIST chunk, look for a cue length marker
185                 FindNextChunk ("LIST");
186                 if (data_p)
187                 {
188                         if (!strncmp ((const char *)data_p + 28, "mark", 4))
189                         {       // this is not a proper parse, but it works with cooledit...
190                                 data_p += 24;
191                                 i = GetLittleLong ();   // samples in loop
192                                 info.samples = info.loopstart + i;
193                         }
194                 }
195         }
196         else
197                 info.loopstart = -1;
198
199         // find data chunk
200         FindChunk("data");
201         if (!data_p)
202         {
203                 Con_Print("Missing data chunk\n");
204                 return info;
205         }
206
207         data_p += 4;
208         samples = GetLittleLong () / info.width / info.channels;
209
210         if (info.samples)
211         {
212                 if (samples < info.samples)
213                 {
214                         Con_Printf ("Sound %s has a bad loop length\n", name);
215                         info.samples = samples;
216                 }
217         }
218         else
219                 info.samples = samples;
220
221         info.dataofs = data_p - wav;
222
223         return info;
224 }
225
226
227 /*
228 ====================
229 WAV_GetSamplesFloat
230 ====================
231 */
232 static void WAV_GetSamplesFloat(channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat)
233 {
234         int i, len = numsampleframes * sfx->format.channels;
235         if (sfx->format.width == 2)
236         {
237                 const short *bufs = (const short *)sfx->fetcher_data + firstsampleframe * sfx->format.channels;
238                 for (i = 0;i < len;i++)
239                         outsamplesfloat[i] = bufs[i] * (1.0f / 32768.0f);
240         }
241         else
242         {
243                 const signed char *bufb = (const signed char *)sfx->fetcher_data + firstsampleframe * sfx->format.channels;
244                 for (i = 0;i < len;i++)
245                         outsamplesfloat[i] = bufb[i] * (1.0f / 128.0f);
246         }
247 }
248
249 /*
250 ====================
251 WAV_FreeSfx
252 ====================
253 */
254 static void WAV_FreeSfx(sfx_t *sfx)
255 {
256         // free the loaded sound data
257         Mem_Free(sfx->fetcher_data);
258 }
259
260 const snd_fetcher_t wav_fetcher = { WAV_GetSamplesFloat, NULL, WAV_FreeSfx };
261
262
263 /*
264 ==============
265 S_LoadWavFile
266 ==============
267 */
268 qboolean S_LoadWavFile (const char *filename, sfx_t *sfx)
269 {
270         fs_offset_t filesize;
271         unsigned char *data;
272         wavinfo_t info;
273         int i, len;
274         const unsigned char *inb;
275         unsigned char *outb;
276
277         // Already loaded?
278         if (sfx->fetcher != NULL)
279                 return true;
280
281         // Load the file
282         data = FS_LoadFile(filename, snd_mempool, false, &filesize);
283         if (!data)
284                 return false;
285
286         // Don't try to load it if it's not a WAV file
287         if (memcmp (data, "RIFF", 4) || memcmp (data + 8, "WAVE", 4))
288         {
289                 Mem_Free(data);
290                 return false;
291         }
292
293         if (developer_loading.integer >= 2)
294                 Con_Printf ("Loading WAV file \"%s\"\n", filename);
295
296         info = GetWavinfo (sfx->name, data, (int)filesize);
297         if (info.channels < 1 || info.channels > 2)  // Stereo sounds are allowed (intended for music)
298         {
299                 Con_Printf("%s has an unsupported number of channels (%i)\n",sfx->name, info.channels);
300                 Mem_Free(data);
301                 return false;
302         }
303         //if (info.channels == 2)
304         //      Log_Printf("stereosounds.log", "%s\n", sfx->name);
305
306         sfx->format.speed = info.rate;
307         sfx->format.width = info.width;
308         sfx->format.channels = info.channels;
309         sfx->fetcher = &wav_fetcher;
310         sfx->fetcher_data = Mem_Alloc(snd_mempool, info.samples * sfx->format.width * sfx->format.channels);
311         sfx->total_length = info.samples;
312         sfx->memsize += filesize;
313         len = info.samples * sfx->format.channels * sfx->format.width;
314         inb = data + info.dataofs;
315         outb = (unsigned char *)sfx->fetcher_data;
316         if (info.width == 2)
317         {
318                 if (mem_bigendian)
319                 {
320                         // we have to byteswap the data at load (better than doing it while mixing)
321                         for (i = 0;i < len;i += 2)
322                         {
323                                 outb[i] = inb[i+1];
324                                 outb[i+1] = inb[i];
325                         }
326                 }
327                 else
328                 {
329                         // we can just copy it straight
330                         memcpy(outb, inb, len);
331                 }
332         }
333         else
334         {
335                 // convert unsigned byte sound data to signed bytes for quicker mixing
336                 for (i = 0;i < len;i++)
337                         outb[i] = inb[i] - 0x80;
338         }
339
340         if (info.loopstart < 0)
341                 sfx->loopstart = sfx->total_length;
342         else
343                 sfx->loopstart = info.loopstart;
344         sfx->loopstart = min(sfx->loopstart, sfx->total_length);
345         sfx->flags &= ~SFXFLAG_STREAMED;
346
347         return true;
348 }