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