Quake II RTX doxygen  1.0 dev
mem.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (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. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 // snd_mem.c: sound caching
19 
20 #include "sound.h"
21 
23 
24 #if USE_SNDDMA
25 /*
26 ================
27 ResampleSfx
28 ================
29 */
30 static sfxcache_t *ResampleSfx(sfx_t *sfx)
31 {
32  int outcount;
33  int srcsample;
34  float stepscale;
35  int i;
36  int samplefrac, fracstep;
37  sfxcache_t *sc;
38 
39  stepscale = (float)s_info.rate / dma.speed; // this is usually 0.5, 1, or 2
40 
41  outcount = s_info.samples / stepscale;
42  if (!outcount) {
43  Com_DPrintf("%s resampled to zero length\n", s_info.name);
44  sfx->error = Q_ERR_TOO_FEW;
45  return NULL;
46  }
47 
48  sc = sfx->cache = S_Malloc(outcount * s_info.width + sizeof(sfxcache_t) - 1);
49 
50  sc->length = outcount;
51  sc->loopstart = s_info.loopstart == -1 ? -1 : s_info.loopstart / stepscale;
52  sc->width = s_info.width;
53 
54 // resample / decimate to the current source rate
55 //Com_Printf("%s: %f, %d\n",sfx->name,stepscale,sc->width);
56  if (stepscale == 1) {
57 // fast special case
58  if (sc->width == 1) {
59  memcpy(sc->data, s_info.data, outcount);
60  } else {
61 #if __BYTE_ORDER == __LITTLE_ENDIAN
62  memcpy(sc->data, s_info.data, outcount << 1);
63 #else
64  for (i = 0; i < outcount; i++) {
65  ((uint16_t *)sc->data)[i] = LittleShort(((uint16_t *)s_info.data)[i]);
66  }
67 #endif
68  }
69  } else {
70 // general case
71  samplefrac = 0;
72  fracstep = stepscale * 256;
73  if (sc->width == 1) {
74  for (i = 0; i < outcount; i++) {
75  srcsample = samplefrac >> 8;
76  samplefrac += fracstep;
77  sc->data[i] = s_info.data[srcsample];
78  }
79  } else {
80  for (i = 0; i < outcount; i++) {
81  srcsample = samplefrac >> 8;
82  samplefrac += fracstep;
83  ((uint16_t *)sc->data)[i] = LittleShort(((uint16_t *)s_info.data)[srcsample]);
84  }
85  }
86  }
87 
88  return sc;
89 }
90 #endif
91 
92 /*
93 ===============================================================================
94 
95 WAV loading
96 
97 ===============================================================================
98 */
99 
100 static byte *data_p;
101 static byte *iff_end;
102 static byte *iff_data;
103 static uint32_t iff_chunk_len;
104 
105 static int GetLittleShort(void)
106 {
107  int val;
108 
109  if (data_p + 2 > iff_end) {
110  return -1;
111  }
112 
113  val = LittleShortMem(data_p);
114  data_p += 2;
115  return val;
116 }
117 
118 static int GetLittleLong(void)
119 {
120  int val;
121 
122  if (data_p + 4 > iff_end) {
123  return -1;
124  }
125 
126  val = LittleLongMem(data_p);
127  data_p += 4;
128  return val;
129 }
130 
131 static void FindNextChunk(uint32_t search)
132 {
133  uint32_t chunk, len;
134  size_t remaining;
135 
136  while (data_p + 8 < iff_end) {
137  chunk = RawLongMem(data_p); data_p += 4;
138  len = LittleLongMem(data_p); data_p += 4;
139  remaining = (size_t)(iff_end - data_p);
140  if (len > remaining) {
141  len = remaining;
142  }
143  if (chunk == search) {
144  iff_chunk_len = len;
145  return;
146  }
147  data_p += (len + 1) & ~1;
148  }
149 
150  // didn't find the chunk
151  data_p = NULL;
152 }
153 
154 static void FindChunk(uint32_t search)
155 {
156  data_p = iff_data;
157  FindNextChunk(search);
158 }
159 
160 #define TAG_RIFF MakeRawLong('R', 'I', 'F', 'F')
161 #define TAG_WAVE MakeRawLong('W', 'A', 'V', 'E')
162 #define TAG_fmt MakeRawLong('f', 'm', 't', ' ')
163 #define TAG_cue MakeRawLong('c', 'u', 'e', ' ')
164 #define TAG_LIST MakeRawLong('L', 'I', 'S', 'T')
165 #define TAG_MARK MakeRawLong('M', 'A', 'R', 'K')
166 #define TAG_data MakeRawLong('d', 'a', 't', 'a')
167 
168 static qboolean GetWavinfo(void)
169 {
170  int format;
171  int samples, width;
172  uint32_t chunk;
173 
174 // find "RIFF" chunk
176  if (!data_p) {
177  Com_DPrintf("%s has missing/invalid RIFF chunk\n", s_info.name);
178  return qfalse;
179  }
180  chunk = GetLittleLong();
181  if (chunk != TAG_WAVE) {
182  Com_DPrintf("%s has missing/invalid WAVE chunk\n", s_info.name);
183  return qfalse;
184  }
185 
186  iff_data = data_p;
187 
188 // get "fmt " chunk
190  if (!data_p) {
191  Com_DPrintf("%s has missing/invalid fmt chunk\n", s_info.name);
192  return qfalse;
193  }
194  format = GetLittleShort();
195  if (format != 1) {
196  Com_DPrintf("%s has non-Microsoft PCM format\n", s_info.name);
197  return qfalse;
198  }
199 
200  format = GetLittleShort();
201  if (format != 1) {
202  Com_DPrintf("%s has bad number of channels\n", s_info.name);
203  return qfalse;
204  }
205 
207  if (s_info.rate < 8000 || s_info.rate > 48000) {
208  Com_DPrintf("%s has bad rate\n", s_info.name);
209  return qfalse;
210  }
211 
212  data_p += 4 + 2;
213 
214  width = GetLittleShort();
215  switch (width) {
216  case 8:
217  s_info.width = 1;
218  break;
219  case 16:
220  s_info.width = 2;
221  break;
222  default:
223  Com_DPrintf("%s has bad width\n", s_info.name);
224  return qfalse;
225  }
226 
227 // get cue chunk
229  if (data_p) {
230  data_p += 24;
232  if (s_info.loopstart < 0 || s_info.loopstart > INT_MAX) {
233  Com_DPrintf("%s has bad loop start\n", s_info.name);
234  return qfalse;
235  }
236 
238  if (data_p) {
239  data_p += 20;
240  chunk = GetLittleLong();
241  if (chunk == TAG_MARK) {
242  // this is not a proper parse, but it works with cooledit...
243  data_p += 16;
244  samples = GetLittleLong(); // samples in loop
245  if (samples < 0 || samples > INT_MAX - s_info.loopstart) {
246  Com_DPrintf("%s has bad loop length\n", s_info.name);
247  return qfalse;
248  }
250  }
251  }
252  } else {
253  s_info.loopstart = -1;
254  }
255 
256 // find data chunk
258  if (!data_p) {
259  Com_DPrintf("%s has missing/invalid data chunk\n", s_info.name);
260  return qfalse;
261  }
262 
264  if (!samples) {
265  Com_DPrintf("%s has zero length\n", s_info.name);
266  return qfalse;
267  }
268 
269  if (s_info.samples) {
270  if (samples < s_info.samples) {
271  Com_DPrintf("%s has bad loop length\n", s_info.name);
272  return qfalse;
273  }
274  } else {
276  }
277 
278  s_info.data = data_p;
279 
280  return qtrue;
281 }
282 
283 /*
284 ==============
285 S_LoadSound
286 ==============
287 */
289 {
290  byte *data;
291  sfxcache_t *sc;
292  ssize_t len;
293  char *name;
294 
295  if (s->name[0] == '*')
296  return NULL;
297 
298 // see if still in memory
299  sc = s->cache;
300  if (sc)
301  return sc;
302 
303 // don't retry after error
304  if (s->error)
305  return NULL;
306 
307 // load it in
308  if (s->truename)
309  name = s->truename;
310  else
311  name = s->name;
312 
313  len = FS_LoadFile(name, (void **)&data);
314  if (!data) {
315  s->error = len;
316  return NULL;
317  }
318 
319  memset(&s_info, 0, sizeof(s_info));
320  s_info.name = name;
321 
322  iff_data = data;
323  iff_end = data + len;
324  if (!GetWavinfo()) {
325  s->error = Q_ERR_INVALID_FORMAT;
326  goto fail;
327  }
328 
329 #if USE_OPENAL
330  if (s_started == SS_OAL)
331  sc = AL_UploadSfx(s);
332 #endif
333 
334 #if USE_SNDDMA
335  if (s_started == SS_DMA)
336  sc = ResampleSfx(s);
337 #endif
338 
339 fail:
340  FS_FreeFile(data);
341  return sc;
342 }
343 
s_info
wavinfo_t s_info
Definition: mem.c:22
wavinfo_t::width
int width
Definition: sound.h:91
AL_UploadSfx
sfxcache_t * AL_UploadSfx(sfx_t *s)
Definition: al.c:201
dma
dma_t dma
Definition: dma.c:22
FindChunk
static void FindChunk(uint32_t search)
Definition: mem.c:154
sfx_s::truename
char * truename
Definition: sound.h:48
TAG_MARK
#define TAG_MARK
Definition: mem.c:165
data_p
static byte * data_p
Definition: mem.c:100
GetLittleLong
static int GetLittleLong(void)
Definition: mem.c:118
iff_end
static byte * iff_end
Definition: mem.c:101
wavinfo_t::samples
int samples
Definition: sound.h:93
sfx_s
Definition: sound.h:44
iff_data
static byte * iff_data
Definition: mem.c:102
sfxcache_s::length
int length
Definition: sound.h:34
TAG_WAVE
#define TAG_WAVE
Definition: mem.c:161
TAG_fmt
#define TAG_fmt
Definition: mem.c:162
sfx_s::error
qerror_t error
Definition: sound.h:49
width
static int width
Definition: physical_sky.c:38
sfx_s::cache
sfxcache_t * cache
Definition: sound.h:47
wavinfo_t::data
byte * data
Definition: sound.h:94
GetWavinfo
static qboolean GetWavinfo(void)
Definition: mem.c:168
sfxcache_s::data
byte data[1]
Definition: sound.h:41
S_LoadSound
sfxcache_t * S_LoadSound(sfx_t *s)
Definition: mem.c:288
TAG_cue
#define TAG_cue
Definition: mem.c:163
sfxcache_s::loopstart
int loopstart
Definition: sound.h:35
wavinfo_t::rate
int rate
Definition: sound.h:90
sfxcache_s
Definition: sound.h:33
FindNextChunk
static void FindNextChunk(uint32_t search)
Definition: mem.c:131
TAG_LIST
#define TAG_LIST
Definition: mem.c:164
wavinfo_t::name
char * name
Definition: sound.h:89
TAG_data
#define TAG_data
Definition: mem.c:166
iff_chunk_len
static uint32_t iff_chunk_len
Definition: mem.c:103
TAG_RIFF
#define TAG_RIFF
Definition: mem.c:160
wavinfo_t::loopstart
int loopstart
Definition: sound.h:92
samples
unsigned samples[LAG_WIDTH]
Definition: screen.c:528
s_started
sndstarted_t s_started
Definition: main.c:32
sfxcache_s::width
int width
Definition: sound.h:36
sound.h
wavinfo_t
Definition: sound.h:88
sfx_s::name
char name[MAX_QPATH]
Definition: sound.h:45
S_Malloc
#define S_Malloc(x)
Definition: sound.h:168
GetLittleShort
static int GetLittleShort(void)
Definition: mem.c:105