Mercurial > almixer_isolated
annotate Isolated/LGPL/oggtremor.c @ 79:358b0bd5df43 tip
Added support for Apportable's alcSuspend()/alcResume() in BeginInterruption()/EndInterruption. You must define ALMIXER_USE_APPORTABLE_OPENAL_EXTENSIONS to compile in this support.
author | Eric Wing <ewing@coronalabs.com> |
---|---|
date | Tue, 30 Oct 2012 16:01:30 -0700 |
parents | 714ec6c5789d |
children |
rev | line source |
---|---|
38 | 1 /* |
2 * SDL_sound -- An abstract sound format decoding API. | |
3 * Copyright (C) 2001 Ryan C. Gordon. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2.1 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 /* | |
21 * Ogg Vorbis decoder for SDL_sound. | |
22 * | |
23 * This driver handles .OGG audio files, and depends on libvorbisfile to | |
24 * do the actual decoding work. libvorbisfile is part of libvorbis, which | |
25 * is part of the Ogg Vorbis project. | |
26 * | |
27 * Ogg Vorbis: http://www.xiph.org/ogg/vorbis/ | |
28 * vorbisfile documentation: http://www.xiph.org/ogg/vorbis/doc/vorbisfile/ | |
29 * | |
30 * Please see the file LICENSE.txt in the source's root directory. | |
31 * | |
32 * This file written by Ryan C. Gordon. (icculus@icculus.org) | |
33 */ | |
34 | |
35 #if HAVE_CONFIG_H | |
36 # include <config.h> | |
37 #endif | |
38 | |
39 #ifdef SOUND_SUPPORTS_OGG | |
40 | |
41 #include <stdio.h> | |
42 #include <stdlib.h> | |
43 #include <string.h> | |
44 #include <math.h> | |
45 | |
46 #include "SoundDecoder.h" | |
47 | |
48 #include "SoundDecoder_Internal.h" | |
49 #include "SDL_endian_minimal.h" | |
50 #include "ALmixer_RWops.h" | |
51 | |
52 //#include <vorbis/codec.h> | |
53 #include <ivorbisfile.h> | |
54 #define ERR_IO_ERROR "I/O error" | |
55 | |
56 | |
57 static int OGG_init(void); | |
58 static void OGG_quit(void); | |
59 static int OGG_open(Sound_Sample *sample, const char *ext); | |
60 static void OGG_close(Sound_Sample *sample); | |
61 static uint32_t OGG_read(Sound_Sample *sample); | |
62 static int OGG_rewind(Sound_Sample *sample); | |
63 static int OGG_seek(Sound_Sample *sample, uint32_t ms); | |
64 | |
65 static const char *extensions_ogg[] = { "OGG", NULL }; | |
66 const Sound_DecoderFunctions __Sound_DecoderFunctions_OGG = | |
67 { | |
68 { | |
69 extensions_ogg, | |
70 "Ogg Vorbis audio through VorbisFile", | |
71 "Ryan C. Gordon <icculus@icculus.org>", | |
72 "http://www.icculus.org/SDL_sound/" | |
73 }, | |
74 | |
75 OGG_init, /* init() method */ | |
76 OGG_quit, /* quit() method */ | |
77 OGG_open, /* open() method */ | |
78 OGG_close, /* close() method */ | |
79 OGG_read, /* read() method */ | |
80 OGG_rewind, /* rewind() method */ | |
81 OGG_seek /* seek() method */ | |
82 }; | |
83 | |
84 | |
85 static int OGG_init(void) | |
86 { | |
87 return(1); /* always succeeds. */ | |
88 } /* OGG_init */ | |
89 | |
90 | |
91 static void OGG_quit(void) | |
92 { | |
93 /* it's a no-op. */ | |
94 } /* OGG_quit */ | |
95 | |
96 | |
97 | |
98 /* | |
99 * These are callbacks from vorbisfile that let them read data from | |
100 * a RWops... | |
101 */ | |
102 | |
103 static size_t RWops_ogg_read(void *ptr, size_t size, size_t nmemb, void *datasource) | |
104 { | |
105 return((size_t) ALmixer_RWread((ALmixer_RWops *) datasource, ptr, size, nmemb)); | |
106 } /* RWops_ogg_read */ | |
107 | |
108 static int RWops_ogg_seek(void *datasource, ogg_int64_t offset, int whence) | |
109 { | |
110 return(ALmixer_RWseek((ALmixer_RWops *) datasource, offset, whence)); | |
111 } /* RWops_ogg_seek */ | |
112 | |
113 static int RWops_ogg_close(void *datasource) | |
114 { | |
115 /* do nothing; SDL_sound will delete the RWops at a higher level. */ | |
116 return(0); /* this is success in fclose(), so I guess that's okay. */ | |
117 } /* RWops_ogg_close */ | |
118 | |
119 static long RWops_ogg_tell(void *datasource) | |
120 { | |
121 return((long) ALmixer_RWtell((ALmixer_RWops *) datasource)); | |
122 } /* RWops_ogg_tell */ | |
123 | |
124 static const ov_callbacks RWops_ogg_callbacks = | |
125 { | |
126 RWops_ogg_read, | |
127 RWops_ogg_seek, | |
128 RWops_ogg_close, | |
129 RWops_ogg_tell | |
130 }; | |
131 | |
132 | |
133 /* Return a human readable version of an VorbisFile error code... */ | |
134 #if (defined DEBUG_CHATTER) | |
135 static const char *ogg_error(int errnum) | |
136 { | |
137 switch(errnum) | |
138 { | |
139 case OV_EREAD: | |
140 return("i/o error"); | |
141 case OV_ENOTVORBIS: | |
142 return("not a vorbis file"); | |
143 case OV_EVERSION: | |
144 return("Vorbis version mismatch"); | |
145 case OV_EBADHEADER: | |
146 return("invalid Vorbis bitstream header"); | |
147 case OV_EFAULT: | |
148 return("internal logic fault in Vorbis library"); | |
149 } /* switch */ | |
150 | |
151 return("unknown error"); | |
152 } /* ogg_error */ | |
153 #endif | |
154 | |
155 static __inline__ void output_ogg_comments(OggVorbis_File *vf) | |
156 { | |
157 #if (defined DEBUG_CHATTER) | |
158 int i; | |
159 vorbis_comment *vc = ov_comment(vf, -1); | |
160 | |
161 if (vc == NULL) | |
162 return; | |
163 | |
164 SNDDBG(("OGG: vendor == [%s].\n", vc->vendor)); | |
165 for (i = 0; i < vc->comments; i++) | |
166 { | |
167 SNDDBG(("OGG: user comment [%s].\n", vc->user_comments[i])); | |
168 } /* for */ | |
169 #endif | |
170 } /* output_ogg_comments */ | |
171 | |
172 | |
173 static int OGG_open(Sound_Sample *sample, const char *ext) | |
174 { | |
175 int rc; | |
176 double total_time; | |
177 OggVorbis_File *vf; | |
178 vorbis_info *info; | |
179 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
180 | |
181 vf = (OggVorbis_File *) malloc(sizeof (OggVorbis_File)); | |
182 BAIL_IF_MACRO(vf == NULL, ERR_OUT_OF_MEMORY, 0); | |
183 | |
184 rc = ov_open_callbacks(internal->rw, vf, NULL, 0, RWops_ogg_callbacks); | |
185 if (rc != 0) | |
186 { | |
187 #if (defined DEBUG_CHATTER) | |
188 SNDDBG(("OGG: can't grok data. reason: [%s].\n", ogg_error(rc))); | |
189 #endif | |
190 free(vf); | |
191 BAIL_MACRO("OGG: Not valid Ogg Vorbis data.", 0); | |
192 } /* if */ | |
193 | |
194 info = ov_info(vf, -1); | |
195 if (info == NULL) | |
196 { | |
197 ov_clear(vf); | |
198 free(vf); | |
199 BAIL_MACRO("OGG: failed to retrieve bitstream info", 0); | |
200 } /* if */ | |
201 | |
202 output_ogg_comments(vf); | |
203 | |
204 SNDDBG(("OGG: bitstream version == (%d).\n", info->version)); | |
205 SNDDBG(("OGG: bitstream channels == (%d).\n", info->channels)); | |
206 SNDDBG(("OGG: bitstream sampling rate == (%ld).\n", info->rate)); | |
207 SNDDBG(("OGG: seekable == {%s}.\n", ov_seekable(vf) ? "TRUE" : "FALSE")); | |
208 SNDDBG(("OGG: number of logical bitstreams == (%ld).\n", ov_streams(vf))); | |
209 SNDDBG(("OGG: serial number == (%ld).\n", ov_serialnumber(vf, -1))); | |
210 SNDDBG(("OGG: total seconds of sample == (%f).\n", ov_time_total(vf, -1))); | |
211 | |
212 internal->decoder_private = vf; | |
213 sample->flags = SOUND_SAMPLEFLAG_CANSEEK; | |
214 sample->actual.rate = (uint32_t) info->rate; | |
215 sample->actual.channels = (uint8_t) info->channels; | |
216 total_time = ov_time_total(vf, -1); | |
217 if (OV_EINVAL == total_time) | |
218 internal->total_time = -1; | |
219 else | |
78
714ec6c5789d
Fixed a unit conversion bug in the Ogg Tremor decoder for SoundDecoder. This fixes out of memory issues with LoadAll on long, but still reasonably sized .ogg files on some Android devices.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
53
diff
changeset
|
220 internal->total_time = (int32_t)(total_time); |
38 | 221 |
222 | |
223 /* | |
224 * Since we might have more than one logical bitstream in the OGG file, | |
225 * and these bitstreams may be in different formats, we might be | |
226 * converting two or three times: once in vorbisfile, once again in | |
227 * SDL_sound, and perhaps a third time to get it to the sound device's | |
228 * format. That's wickedly inefficient. | |
229 * | |
230 * To combat this a little, if the user specified a desired format, we | |
231 * claim that to be the "actual" format of the collection of logical | |
232 * bitstreams. This means that VorbisFile will do a conversion as | |
233 * necessary, and SDL_sound will not. If the user didn't specify a | |
234 * desired format, then we pretend the "actual" format is something that | |
235 * OGG files are apparently commonly encoded in. | |
236 */ | |
237 sample->actual.format = (sample->desired.format == 0) ? | |
238 AUDIO_S16SYS : sample->desired.format; | |
239 return(1); | |
240 } /* OGG_open */ | |
241 | |
242 | |
243 static void OGG_close(Sound_Sample *sample) | |
244 { | |
245 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
246 OggVorbis_File *vf = (OggVorbis_File *) internal->decoder_private; | |
247 ov_clear(vf); | |
248 free(vf); | |
249 } /* OGG_close */ | |
250 | |
251 /* Note: According to the Vorbis documentation: | |
252 * "ov_read() will decode at most one vorbis packet per invocation, | |
253 * so the value returned will generally be less than length." | |
254 * Due to this, for buffer sizes like 16384, SDL_Sound was always getting | |
255 * an underfilled buffer and always setting the EAGAIN flag. | |
256 * Since the SDL_Sound API implies that the entire buffer | |
257 * should be filled unless EOF, additional code has been added | |
258 * to this function to call ov_read() until the buffer is filled. | |
259 * However, there may still be some corner cases where the buffer | |
260 * cannot be entirely filled. So be aware. | |
261 */ | |
262 static uint32_t OGG_read(Sound_Sample *sample) | |
263 { | |
264 int rc; | |
265 int bitstream; | |
266 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
267 OggVorbis_File *vf = (OggVorbis_File *) internal->decoder_private; | |
268 | |
269 rc = ov_read(vf, internal->buffer, internal->buffer_size, | |
270 &bitstream); | |
271 | |
272 /* Make sure the read went smoothly... */ | |
273 if (rc == 0) | |
274 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
275 | |
276 else if (rc < 0) | |
277 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
278 | |
279 /* If the buffer isn't filled, keep trying to fill it | |
280 * until no more data can be grabbed */ | |
281 else if ((uint32_t) rc < internal->buffer_size) | |
282 { | |
283 /* Creating a pointer to the buffer that denotes where to start | |
284 * writing new data. */ | |
285 uint8_t* buffer_start_point = NULL; | |
286 int total_bytes_read = rc; | |
287 int bytes_remaining = internal->buffer_size - rc; | |
288 | |
289 /* Keep grabbing data until something prevents | |
290 * us from getting more. (Could be EOF, | |
291 * packets are too large to fit in remaining | |
292 * space, or an error.) | |
293 */ | |
294 while( (rc > 0) && (bytes_remaining > 0) ) | |
295 { | |
296 /* Set buffer pointer to end of last write */ | |
297 /* All the messiness is to get rid of the warning for | |
298 * dereferencing a void* | |
299 */ | |
300 buffer_start_point = &(((uint8_t*)internal->buffer)[total_bytes_read]); | |
301 rc = ov_read(vf, buffer_start_point, bytes_remaining, | |
302 &bitstream); | |
303 /* Make sure rc > 0 because we don't accidently want | |
304 * to change the counters if there was an error | |
305 */ | |
306 if(rc > 0) | |
307 { | |
308 total_bytes_read += rc; | |
309 bytes_remaining = bytes_remaining - rc; | |
310 } | |
311 } | |
312 /* I think the minimum read size is 2, though I'm | |
313 * not sure about this. (I've hit cases where I | |
314 * couldn't read less than 4.) What I don't want to do is | |
315 * accidently claim we hit EOF when the reason rc == 0 | |
316 * is because the requested amount of data was smaller | |
317 * than the minimum packet size. | |
318 * For now, I will be conservative | |
319 * and not set the EOF flag, and let the next call to | |
320 * this function figure it out. | |
321 * I think the ERROR flag is safe to set because | |
322 * it looks like OGG simply returns 0 if the | |
323 * read size is too small. | |
324 * And in most cases for sensible buffer sizes, | |
325 * this fix will fill the buffer, | |
326 * so we can set the EAGAIN flag without worrying | |
327 * that it will always be set. | |
328 */ | |
329 if(rc < 0) | |
330 { | |
331 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
332 } | |
333 else if(rc == 0) | |
334 { | |
335 /* Do nothing for now until there is a better solution */ | |
336 /* sample->flags |= SOUND_SAMPLEFLAG_EOF; */ | |
337 } | |
338 | |
339 /* Test for a buffer underrun. It should occur less frequently | |
340 * now, but it still may happen and not necessarily mean | |
341 * anything useful. */ | |
342 if ((uint32_t) total_bytes_read < internal->buffer_size) | |
343 { | |
344 sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; | |
345 } | |
346 /* change rc to the total bytes read so function | |
347 * can return the correct value. | |
348 */ | |
349 rc = total_bytes_read; | |
350 } | |
351 | |
352 return((uint32_t) rc); | |
353 } /* OGG_read */ | |
354 | |
355 | |
356 static int OGG_rewind(Sound_Sample *sample) | |
357 { | |
358 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
359 OggVorbis_File *vf = (OggVorbis_File *) internal->decoder_private; | |
360 | |
361 BAIL_IF_MACRO(ov_raw_seek(vf, 0) < 0, ERR_IO_ERROR, 0); | |
362 return(1); | |
363 } /* OGG_rewind */ | |
364 | |
365 | |
366 static int OGG_seek(Sound_Sample *sample, uint32_t ms) | |
367 { | |
368 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
369 OggVorbis_File *vf = (OggVorbis_File *) internal->decoder_private; | |
53
02a11507d65b
Fixed audio.seek bug in Ogg Tremor decoder interface. Unlike Ogg Vorbis, Tremor uses integer milliseconds instead of double seconds.
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
370 /* Unlike Vorbis, Tremor uses integer milliseconds instead of double seconds. */ |
02a11507d65b
Fixed audio.seek bug in Ogg Tremor decoder interface. Unlike Ogg Vorbis, Tremor uses integer milliseconds instead of double seconds.
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
371 BAIL_IF_MACRO(ov_time_seek(vf, (ogg_int64_t)ms) < 0, ERR_IO_ERROR, 0); |
38 | 372 return(1); |
373 } /* OGG_seek */ | |
374 | |
375 #endif /* SOUND_SUPPORTS_OGG */ | |
376 | |
377 | |
378 /* end of ogg.c ... */ | |
379 |