1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
5 Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
6
7 This file is part of Quake III Arena source code.
8
9 Quake III Arena source code is free software; you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
13
14 Quake III Arena source code is distributed in the hope that it will be
15 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Quake III Arena source code; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ===========================================================================
23 */
24
25 // OGG support is enabled by this define
26 #ifdef USE_CODEC_VORBIS
27
28 // includes for the Q3 sound system
29 #include "client.h"
30 #include "snd_codec.h"
31
32 // includes for the OGG codec
33 #include <errno.h>
34 #define OV_EXCLUDE_STATIC_CALLBACKS
35 #include <vorbis/vorbisfile.h>
36
37 // The OGG codec can return the samples in a number of different formats,
38 // we use the standard signed short format.
39 #define OGG_SAMPLEWIDTH 2
40
41 // Q3 OGG codec
42 snd_codec_t ogg_codec =
43 {
44 ".ogg",
45 S_OGG_CodecLoad,
46 S_OGG_CodecOpenStream,
47 S_OGG_CodecReadStream,
48 S_OGG_CodecCloseStream,
49 NULL
50 };
51
52 // callbacks for vobisfile
53
54 // fread() replacement
S_OGG_Callback_read(void * ptr,size_t size,size_t nmemb,void * datasource)55 size_t S_OGG_Callback_read(void *ptr, size_t size, size_t nmemb, void *datasource)
56 {
57 snd_stream_t *stream;
58 int byteSize = 0;
59 int bytesRead = 0;
60 size_t nMembRead = 0;
61
62 // check if input is valid
63 if(!ptr)
64 {
65 errno = EFAULT;
66 return 0;
67 }
68
69 if(!(size && nmemb))
70 {
71 // It's not an error, caller just wants zero bytes!
72 errno = 0;
73 return 0;
74 }
75
76 if(!datasource)
77 {
78 errno = EBADF;
79 return 0;
80 }
81
82 // we use a snd_stream_t in the generic pointer to pass around
83 stream = (snd_stream_t *) datasource;
84
85 // FS_Read does not support multi-byte elements
86 byteSize = nmemb * size;
87
88 // read it with the Q3 function FS_Read()
89 bytesRead = FS_Read(ptr, byteSize, stream->file);
90
91 // update the file position
92 stream->pos += bytesRead;
93
94 // this function returns the number of elements read not the number of bytes
95 nMembRead = bytesRead / size;
96
97 // even if the last member is only read partially
98 // it is counted as a whole in the return value
99 if(bytesRead % size)
100 {
101 nMembRead++;
102 }
103
104 return nMembRead;
105 }
106
107 // fseek() replacement
S_OGG_Callback_seek(void * datasource,ogg_int64_t offset,int whence)108 int S_OGG_Callback_seek(void *datasource, ogg_int64_t offset, int whence)
109 {
110 snd_stream_t *stream;
111 int retVal = 0;
112
113 // check if input is valid
114 if(!datasource)
115 {
116 errno = EBADF;
117 return -1;
118 }
119
120 // snd_stream_t in the generic pointer
121 stream = (snd_stream_t *) datasource;
122
123 // we must map the whence to its Q3 counterpart
124 switch(whence)
125 {
126 case SEEK_SET :
127 {
128 // set the file position in the actual file with the Q3 function
129 retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET);
130
131 // something has gone wrong, so we return here
132 if(retVal < 0)
133 {
134 return retVal;
135 }
136
137 // keep track of file position
138 stream->pos = (int) offset;
139 break;
140 }
141
142 case SEEK_CUR :
143 {
144 // set the file position in the actual file with the Q3 function
145 retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR);
146
147 // something has gone wrong, so we return here
148 if(retVal < 0)
149 {
150 return retVal;
151 }
152
153 // keep track of file position
154 stream->pos += (int) offset;
155 break;
156 }
157
158 case SEEK_END :
159 {
160 // Quake 3 seems to have trouble with FS_SEEK_END
161 // so we use the file length and FS_SEEK_SET
162
163 // set the file position in the actual file with the Q3 function
164 retVal = FS_Seek(stream->file, (long) stream->length + (long) offset, FS_SEEK_SET);
165
166 // something has gone wrong, so we return here
167 if(retVal < 0)
168 {
169 return retVal;
170 }
171
172 // keep track of file position
173 stream->pos = stream->length + (int) offset;
174 break;
175 }
176
177 default :
178 {
179 // unknown whence, so we return an error
180 errno = EINVAL;
181 return -1;
182 }
183 }
184
185 // stream->pos shouldn't be smaller than zero or bigger than the filesize
186 stream->pos = (stream->pos < 0) ? 0 : stream->pos;
187 stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos;
188
189 return 0;
190 }
191
192 // fclose() replacement
S_OGG_Callback_close(void * datasource)193 int S_OGG_Callback_close(void *datasource)
194 {
195 // we do nothing here and close all things manually in S_OGG_CodecCloseStream()
196 return 0;
197 }
198
199 // ftell() replacement
S_OGG_Callback_tell(void * datasource)200 long S_OGG_Callback_tell(void *datasource)
201 {
202 snd_stream_t *stream;
203
204 // check if input is valid
205 if(!datasource)
206 {
207 errno = EBADF;
208 return -1;
209 }
210
211 // snd_stream_t in the generic pointer
212 stream = (snd_stream_t *) datasource;
213
214 return (long) FS_FTell(stream->file);
215 }
216
217 // the callback structure
218 const ov_callbacks S_OGG_Callbacks =
219 {
220 &S_OGG_Callback_read,
221 &S_OGG_Callback_seek,
222 &S_OGG_Callback_close,
223 &S_OGG_Callback_tell
224 };
225
226 /*
227 =================
228 S_OGG_CodecOpenStream
229 =================
230 */
S_OGG_CodecOpenStream(const char * filename)231 snd_stream_t *S_OGG_CodecOpenStream(const char *filename)
232 {
233 snd_stream_t *stream;
234
235 // OGG codec control structure
236 OggVorbis_File *vf;
237
238 // some variables used to get informations about the OGG
239 vorbis_info *OGGInfo;
240 ogg_int64_t numSamples;
241
242 // check if input is valid
243 if(!filename)
244 {
245 return NULL;
246 }
247
248 // Open the stream
249 stream = S_CodecUtilOpen(filename, &ogg_codec);
250 if(!stream)
251 {
252 return NULL;
253 }
254
255 // alloctate the OggVorbis_File
256 vf = Z_Malloc(sizeof(OggVorbis_File));
257 if(!vf)
258 {
259 S_CodecUtilClose(&stream);
260
261 return NULL;
262 }
263
264 // open the codec with our callbacks and stream as the generic pointer
265 if(ov_open_callbacks(stream, vf, NULL, 0, S_OGG_Callbacks) != 0)
266 {
267 Z_Free(vf);
268
269 S_CodecUtilClose(&stream);
270
271 return NULL;
272 }
273
274 // the stream must be seekable
275 if(!ov_seekable(vf))
276 {
277 ov_clear(vf);
278
279 Z_Free(vf);
280
281 S_CodecUtilClose(&stream);
282
283 return NULL;
284 }
285
286 // we only support OGGs with one substream
287 if(ov_streams(vf) != 1)
288 {
289 ov_clear(vf);
290
291 Z_Free(vf);
292
293 S_CodecUtilClose(&stream);
294
295 return NULL;
296 }
297
298 // get the info about channels and rate
299 OGGInfo = ov_info(vf, 0);
300 if(!OGGInfo)
301 {
302 ov_clear(vf);
303
304 Z_Free(vf);
305
306 S_CodecUtilClose(&stream);
307
308 return NULL;
309 }
310
311 // get the number of sample-frames in the OGG
312 numSamples = ov_pcm_total(vf, 0);
313
314 // fill in the info-structure in the stream
315 stream->info.rate = OGGInfo->rate;
316 stream->info.width = OGG_SAMPLEWIDTH;
317 stream->info.channels = OGGInfo->channels;
318 stream->info.samples = numSamples;
319 stream->info.size = stream->info.samples * stream->info.channels * stream->info.width;
320 stream->info.dataofs = 0;
321
322 // We use stream->pos for the file pointer in the compressed ogg file
323 stream->pos = 0;
324
325 // We use the generic pointer in stream for the OGG codec control structure
326 stream->ptr = vf;
327
328 return stream;
329 }
330
331 /*
332 =================
333 S_OGG_CodecCloseStream
334 =================
335 */
S_OGG_CodecCloseStream(snd_stream_t * stream)336 void S_OGG_CodecCloseStream(snd_stream_t *stream)
337 {
338 // check if input is valid
339 if(!stream)
340 {
341 return;
342 }
343
344 // let the OGG codec cleanup its stuff
345 ov_clear((OggVorbis_File *) stream->ptr);
346
347 // free the OGG codec control struct
348 Z_Free(stream->ptr);
349
350 // close the stream
351 S_CodecUtilClose(&stream);
352 }
353
354 /*
355 =================
356 S_OGG_CodecReadStream
357 =================
358 */
S_OGG_CodecReadStream(snd_stream_t * stream,int bytes,void * buffer)359 int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
360 {
361 // buffer handling
362 int bytesRead, bytesLeft, c;
363 char *bufPtr;
364
365 // Bitstream for the decoder
366 int BS = 0;
367
368 // big endian machines want their samples in big endian order
369 int IsBigEndian = 0;
370
371 # ifdef Q3_BIG_ENDIAN
372 IsBigEndian = 1;
373 # endif // Q3_BIG_ENDIAN
374
375 // check if input is valid
376 if(!(stream && buffer))
377 {
378 return 0;
379 }
380
381 if(bytes <= 0)
382 {
383 return 0;
384 }
385
386 bytesRead = 0;
387 bytesLeft = bytes;
388 bufPtr = buffer;
389
390 // cycle until we have the requested or all available bytes read
391 while(-1)
392 {
393 // read some bytes from the OGG codec
394 c = ov_read((OggVorbis_File *) stream->ptr, bufPtr, bytesLeft, IsBigEndian, OGG_SAMPLEWIDTH, 1, &BS);
395
396 // no more bytes are left
397 if(c <= 0)
398 {
399 break;
400 }
401
402 bytesRead += c;
403 bytesLeft -= c;
404 bufPtr += c;
405
406 // we have enough bytes
407 if(bytesLeft <= 0)
408 {
409 break;
410 }
411 }
412
413 return bytesRead;
414 }
415
416 /*
417 =====================================================================
418 S_OGG_CodecLoad
419
420 We handle S_OGG_CodecLoad as a special case of the streaming functions
421 where we read the whole stream at once.
422 ======================================================================
423 */
S_OGG_CodecLoad(const char * filename,snd_info_t * info)424 void *S_OGG_CodecLoad(const char *filename, snd_info_t *info)
425 {
426 snd_stream_t *stream;
427 byte *buffer;
428 int bytesRead;
429
430 // check if input is valid
431 if(!(filename && info))
432 {
433 return NULL;
434 }
435
436 // open the file as a stream
437 stream = S_OGG_CodecOpenStream(filename);
438 if(!stream)
439 {
440 return NULL;
441 }
442
443 // copy over the info
444 info->rate = stream->info.rate;
445 info->width = stream->info.width;
446 info->channels = stream->info.channels;
447 info->samples = stream->info.samples;
448 info->size = stream->info.size;
449 info->dataofs = stream->info.dataofs;
450
451 // allocate a buffer
452 // this buffer must be free-ed by the caller of this function
453 buffer = Z_Malloc(info->size);
454 if(!buffer)
455 {
456 S_OGG_CodecCloseStream(stream);
457
458 return NULL;
459 }
460
461 // fill the buffer
462 bytesRead = S_OGG_CodecReadStream(stream, info->size, buffer);
463
464 // we don't even have read a single byte
465 if(bytesRead <= 0)
466 {
467 Z_Free(buffer);
468 S_OGG_CodecCloseStream(stream);
469
470 return NULL;
471 }
472
473 S_OGG_CodecCloseStream(stream);
474
475 return buffer;
476 }
477
478 #endif // USE_CODEC_VORBIS
479