38
|
1 #ifndef ALMIXER_COMPILED_WITH_SDL
|
|
2
|
|
3 #include "SoundDecoder.h"
|
|
4 #include "SoundDecoder_Internal.h"
|
|
5 #include "tErrorLib.h"
|
|
6 #include "LinkedList.h"
|
|
7 #include <stdlib.h>
|
|
8 #include <string.h>
|
|
9 #include <assert.h>
|
|
10
|
|
11 #ifdef ANDROID_NDK
|
|
12 #include <android/log.h>
|
|
13 #endif
|
|
14
|
|
15 /* A partial shim reimplementation of SDL_sound to work around the LGPL issues.
|
|
16 * This implementation is more limited than SDL_sound.
|
|
17 * For example, there is no generic software conversion routines.
|
|
18 * Functions are also not necessarily thread safe.
|
|
19 * This implementation relies on the back-end decoder much more heavily
|
|
20 * than SDL_sound. (For example, I bypass the internal->buffer.)
|
|
21 */
|
|
22
|
|
23 static LinkedList* s_listOfLoadedSamples = NULL;
|
|
24
|
|
25 static signed char s_isInitialized = 0;
|
|
26 static TErrorPool* s_errorPool = NULL;
|
|
27
|
|
28 static const SoundDecoder_DecoderInfo** s_availableDecoders = NULL;
|
|
29
|
|
30 #ifdef __APPLE__
|
|
31 //extern const SoundDecoder_DecoderFunctions __SoundDecoder_DecoderFunctions_CoreAudio;
|
|
32 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_CoreAudio;
|
|
33 #endif
|
|
34 #ifdef ANDROID_NDK
|
|
35 #ifdef SOUND_SUPPORTS_WAV
|
|
36 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV;
|
|
37 #endif
|
|
38 #ifdef SOUND_SUPPORTS_MPG123
|
|
39 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MPG123;
|
|
40 #endif
|
|
41 #ifdef SOUND_SUPPORTS_OGG
|
|
42 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_OGG;
|
|
43 #endif
|
|
44 #endif
|
|
45
|
|
46 typedef struct
|
|
47 {
|
|
48 int available;
|
|
49 const SoundDecoder_DecoderFunctions* funcs;
|
|
50 } SoundElement;
|
|
51
|
|
52 static SoundElement s_linkedDecoders[] =
|
|
53 {
|
|
54 #if defined(__APPLE__)
|
|
55 { 0, &__Sound_DecoderFunctions_CoreAudio },
|
|
56 #endif
|
|
57 #if defined(ANDROID_NDK)
|
|
58 #ifdef SOUND_SUPPORTS_WAV
|
|
59 { 0, &__Sound_DecoderFunctions_WAV },
|
|
60 #endif
|
|
61 #ifdef SOUND_SUPPORTS_MPG123
|
|
62 { 0, &__Sound_DecoderFunctions_MPG123 },
|
|
63 #endif
|
|
64 #ifdef SOUND_SUPPORTS_OGG
|
|
65 { 0, &__Sound_DecoderFunctions_OGG },
|
|
66 #endif
|
|
67 #endif
|
|
68 { 0, NULL }
|
|
69 };
|
|
70
|
|
71
|
|
72 #include <ctype.h>
|
|
73 int SoundDecoder_strcasecmp(const char* str1, const char* str2)
|
|
74 {
|
|
75 int the_char1;
|
|
76 int the_char2;
|
|
77 int i = 0;
|
|
78 if(str1 == str2)
|
|
79 {
|
|
80 return 0;
|
|
81 }
|
|
82 if(NULL == str1)
|
|
83 {
|
|
84 return -1;
|
|
85 }
|
|
86 if(NULL == str2)
|
|
87 {
|
|
88 return 1;
|
|
89 }
|
|
90
|
|
91 do
|
|
92 {
|
|
93 the_char1 = tolower(str1[i]);
|
|
94 the_char2 = tolower(str2[i]);
|
|
95 if(the_char1 < the_char2)
|
|
96 {
|
|
97 return -1;
|
|
98 }
|
|
99 else if(the_char1 > the_char2)
|
|
100 {
|
|
101 return 1;
|
|
102 }
|
|
103 i++;
|
|
104 } while( (0 != the_char1) && (0 != the_char2) );
|
|
105
|
|
106 return 0;
|
|
107 }
|
|
108
|
|
109
|
|
110 #ifdef ANDROID_NDK
|
|
111 #include <stdarg.h>
|
|
112 #include <android/log.h>
|
|
113 int SoundDecoder_DebugPrint(const char* format, ...)
|
|
114 {
|
|
115 va_list arg_list;
|
|
116 int ret_val;
|
|
117
|
|
118 va_start(arg_list, format);
|
|
119 ret_val = __android_log_vprint(ANDROID_LOG_INFO, "SoundDecoder", format, arg_list);
|
|
120 va_end(arg_list);
|
|
121 return ret_val;
|
|
122 }
|
|
123 #endif
|
|
124
|
|
125 const char* SoundDecoder_GetError()
|
|
126 {
|
|
127 const char* error_string = NULL;
|
|
128 if(NULL == s_errorPool)
|
|
129 {
|
|
130 return "Error: You should not call SoundDecoder_GetError while Sound is not initialized";
|
|
131 }
|
|
132 error_string = TError_GetLastErrorStr(s_errorPool);
|
|
133 /* SDL returns empty strings instead of NULL */
|
|
134 if(NULL == error_string)
|
|
135 {
|
|
136 return "";
|
|
137 }
|
|
138 else
|
|
139 {
|
|
140 return error_string;
|
|
141 }
|
|
142 }
|
|
143
|
|
144 void SoundDecoder_ClearError()
|
|
145 {
|
|
146 if(NULL == s_errorPool)
|
|
147 {
|
|
148 return;
|
|
149 }
|
|
150 TError_SetError(s_errorPool, 0, NULL);
|
|
151 }
|
|
152
|
|
153 void SoundDecoder_SetError(const char* err_str, ...)
|
|
154 {
|
|
155 if(NULL == s_errorPool)
|
|
156 {
|
|
157 fprintf(stderr, "Error: You should not call SoundDecoder_SetError while Sound is not initialized\n");
|
|
158 return;
|
|
159 }
|
|
160 va_list argp;
|
|
161 va_start(argp, err_str);
|
|
162 // SDL_SetError which I'm emulating has no number parameter.
|
|
163 TError_SetErrorv(s_errorPool, 1, err_str, argp);
|
|
164 va_end(argp);
|
|
165 #ifdef ANDROID_NDK
|
|
166 __android_log_print(ANDROID_LOG_INFO, "SoundDecoder_SetError", TError_GetLastErrorStr(s_errorPool));
|
|
167 #endif
|
|
168 }
|
|
169
|
|
170
|
|
171 void SoundDecoder_GetLinkedVersion(SoundDecoder_Version* the_version)
|
|
172 {
|
|
173 if(NULL != the_version)
|
|
174 {
|
|
175 the_version->major = SOUNDDECODER_VER_MAJOR;
|
|
176 the_version->minor = SOUNDDECODER_VER_MINOR;
|
|
177 the_version->patch = SOUNDDECODER_VER_PATCH;
|
|
178 }
|
|
179 }
|
|
180
|
|
181
|
|
182 const SoundDecoder_DecoderInfo** SoundDecoder_AvailableDecoders()
|
|
183 {
|
|
184 return(s_availableDecoders);
|
|
185 }
|
|
186
|
|
187 int SoundDecoder_Init()
|
|
188 {
|
|
189 size_t total_number_of_decoders;
|
|
190 size_t i;
|
|
191 size_t current_pos = 0;
|
|
192 if(1 == s_isInitialized)
|
|
193 {
|
|
194 return 1;
|
|
195 }
|
|
196 if(NULL == s_errorPool)
|
|
197 {
|
|
198 s_errorPool = TError_CreateErrorPool();
|
|
199 }
|
|
200 if(NULL == s_errorPool)
|
|
201 {
|
|
202 return 0;
|
|
203 }
|
|
204
|
|
205 total_number_of_decoders = sizeof(s_linkedDecoders) / sizeof(s_linkedDecoders[0]);
|
|
206 s_availableDecoders = (const SoundDecoder_DecoderInfo **)malloc((total_number_of_decoders) * sizeof(SoundDecoder_DecoderInfo*));
|
|
207 if(NULL == s_availableDecoders)
|
|
208 {
|
|
209 SoundDecoder_SetError(ERR_OUT_OF_MEMORY);
|
|
210 return 0;
|
|
211 }
|
|
212
|
|
213 /* Allocate memory for linked list of sound samples. */
|
|
214 s_listOfLoadedSamples = LinkedList_Create();
|
|
215 if(NULL == s_listOfLoadedSamples)
|
|
216 {
|
|
217 free(s_availableDecoders);
|
|
218 s_availableDecoders = NULL;
|
|
219 SoundDecoder_SetError(ERR_OUT_OF_MEMORY);
|
|
220 return 0;
|
|
221 }
|
|
222
|
|
223 for(i = 0; s_linkedDecoders[i].funcs != NULL; i++)
|
|
224 {
|
|
225 s_linkedDecoders[i].available = s_linkedDecoders[i].funcs->init();
|
|
226 if(s_linkedDecoders[i].available)
|
|
227 {
|
|
228 s_availableDecoders[current_pos] = &(s_linkedDecoders[i].funcs->info);
|
|
229 current_pos++;
|
|
230 }
|
|
231 }
|
|
232
|
|
233 s_availableDecoders[current_pos] = NULL;
|
|
234 s_isInitialized = 1;
|
|
235 return 1;
|
|
236 }
|
|
237
|
|
238 void SoundDecoder_Quit()
|
|
239 {
|
|
240 size_t i;
|
|
241 if(0 == s_isInitialized)
|
|
242 {
|
|
243 return;
|
|
244 }
|
|
245
|
|
246 /*
|
|
247 * SDL_sound actually embeds the linked list in the internal data structure.
|
|
248 * So any sample can potentially reach any other sample.
|
|
249 * But I'm keeping my own separate list.
|
|
250 */
|
|
251 while(LinkedList_Size(s_listOfLoadedSamples) > 0)
|
|
252 {
|
|
253 SoundDecoder_Sample* sound_sample = (SoundDecoder_Sample*)LinkedList_PopBack(s_listOfLoadedSamples);
|
|
254 SoundDecoder_FreeSample(sound_sample);
|
|
255 }
|
|
256 LinkedList_Free(s_listOfLoadedSamples);
|
|
257 s_listOfLoadedSamples = NULL;
|
|
258
|
|
259
|
|
260 for(i = 0; s_linkedDecoders[i].funcs != NULL; i++)
|
|
261 {
|
|
262 if (s_linkedDecoders[i].available)
|
|
263 {
|
|
264 s_linkedDecoders[i].funcs->quit();
|
|
265 s_linkedDecoders[i].available = 0;
|
|
266 }
|
|
267 }
|
|
268
|
|
269 if(NULL != s_availableDecoders)
|
|
270 {
|
|
271 free(s_availableDecoders);
|
|
272 }
|
|
273 s_availableDecoders = NULL;
|
|
274
|
|
275
|
|
276 /* Remember: ALmixer_SetError/GetError calls will not work while this is gone. */
|
|
277 TError_FreeErrorPool(s_errorPool);
|
|
278 s_errorPool = NULL;
|
|
279
|
|
280 s_isInitialized = 0;
|
|
281 }
|
|
282
|
|
283
|
|
284 void SoundDecoder_FreeSample(SoundDecoder_Sample* sound_sample)
|
|
285 {
|
|
286 SoundDecoder_SampleInternal* sample_internal;
|
|
287
|
|
288 /* Quit unloads all samples, so it is not possible to free a sample
|
|
289 * when not initialized.
|
|
290 */
|
|
291 if(0 == s_isInitialized)
|
|
292 {
|
|
293 return;
|
|
294 }
|
|
295
|
|
296 if(sound_sample == NULL)
|
|
297 {
|
|
298 return;
|
|
299 }
|
|
300
|
|
301 /* SDL_sound keeps a linked list of all the loaded samples.
|
|
302 * We want to remove the current sample from that list.
|
|
303 */
|
|
304 LinkedListNode* the_node = LinkedList_Find(s_listOfLoadedSamples, sound_sample, NULL);
|
|
305 if(NULL == the_node)
|
|
306 {
|
|
307 SoundDecoder_SetError("SoundDecoder_FreeSample: Internal Error, sample does not exist in linked list.");
|
|
308 return;
|
|
309 }
|
|
310 LinkedList_Remove(s_listOfLoadedSamples, the_node);
|
|
311
|
|
312 sample_internal = (SoundDecoder_SampleInternal*)sound_sample->opaque;
|
|
313
|
|
314 /* Ugh...SDL_sound has a lot of pointers.
|
|
315 * I hope I didn't miss any dynamic memory.
|
|
316 */
|
|
317
|
|
318 /* Call close on the decoder */
|
|
319 sample_internal->funcs->close(sound_sample);
|
|
320
|
|
321 if(NULL != sample_internal->rw)
|
|
322 {
|
|
323 sample_internal->rw->close(sample_internal->rw);
|
|
324 }
|
|
325
|
|
326 /* Ooops. The public buffer might be shared with the internal buffer.
|
|
327 * Make sure to not accidentally double delete.
|
|
328 */
|
|
329 if((NULL != sample_internal->buffer)
|
|
330 && (sample_internal->buffer != sound_sample->buffer)
|
|
331 )
|
|
332 {
|
|
333 free(sample_internal->buffer);
|
|
334 }
|
|
335 free(sample_internal);
|
|
336
|
|
337 if(NULL != sound_sample->buffer)
|
|
338 {
|
|
339 free(sound_sample->buffer);
|
|
340 }
|
|
341 free(sound_sample);
|
|
342 }
|
|
343
|
|
344
|
|
345
|
|
346 static int Internal_LoadSample(const SoundDecoder_DecoderFunctions *funcs,
|
|
347 SoundDecoder_Sample* sound_sample, const char *ext
|
|
348 )
|
|
349 {
|
|
350 SoundDecoder_SampleInternal* internal_sample = (SoundDecoder_SampleInternal*)sound_sample->opaque;
|
|
351 long current_file_position = internal_sample->rw->seek(internal_sample->rw, 0, SEEK_CUR);
|
|
352
|
|
353 /* fill in the funcs for this decoder... */
|
|
354 sound_sample->decoder = &funcs->info;
|
|
355 internal_sample->funcs = funcs;
|
|
356 if (!funcs->open(sound_sample, ext))
|
|
357 {
|
|
358 internal_sample->rw->seek(internal_sample->rw, current_file_position, SEEK_SET);
|
|
359 return 0;
|
|
360 }
|
|
361
|
|
362 /* we found a compatible decoder */
|
|
363
|
|
364 /* SDL_sound normally goes on to setup a bunch of things to
|
|
365 * support format conversion via SDL APIs.
|
|
366 * I am not porting any of that stuff.
|
|
367 * My goal is to simply setup the struct properties to values
|
|
368 * that will not cause any confusion with other parts of the implementation.
|
|
369 */
|
|
370
|
|
371
|
|
372 if(0 == sound_sample->desired.format)
|
|
373 {
|
|
374 sound_sample->desired.format = sound_sample->actual.format;
|
|
375 }
|
|
376 if(0 == sound_sample->desired.channels)
|
|
377 {
|
|
378 sound_sample->desired.channels = sound_sample->actual.channels;
|
|
379 }
|
|
380 if(0 == sound_sample->desired.rate)
|
|
381 {
|
|
382 sound_sample->desired.rate = sound_sample->actual.rate;
|
|
383 }
|
|
384
|
|
385 /* I'm a little confused at the difference between the internal
|
|
386 * public buffer. I am going to make them the same.
|
|
387 * I assume I already allocated the public buffer.
|
|
388 */
|
|
389 internal_sample->buffer = sound_sample->buffer;
|
|
390 internal_sample->buffer_size = sound_sample->buffer_size;
|
|
391
|
|
392 /* Insert the new sample into the linked list of samples. */
|
|
393 LinkedList_PushBack(s_listOfLoadedSamples, sound_sample);
|
|
394
|
|
395 return 1;
|
|
396 }
|
|
397
|
|
398
|
|
399
|
|
400 SoundDecoder_Sample* SoundDecoder_NewSampleFromFile(const char* file_name,
|
|
401 SoundDecoder_AudioInfo* desired_format,
|
|
402 size_t buffer_size)
|
|
403 {
|
|
404
|
|
405 const char* file_extension;
|
|
406 ALmixer_RWops* rw_ops;
|
|
407
|
|
408
|
|
409 if(0 == s_isInitialized)
|
|
410 {
|
|
411 SoundDecoder_SetError(ERR_NOT_INITIALIZED);
|
|
412 return NULL;
|
|
413 }
|
|
414 if(NULL == file_name)
|
|
415 {
|
|
416 SoundDecoder_SetError("No file specified");
|
|
417 return NULL;
|
|
418 }
|
|
419
|
|
420 file_extension = strrchr(file_name, '.');
|
|
421 if(NULL != file_extension)
|
|
422 {
|
|
423 file_extension++;
|
|
424 }
|
|
425
|
|
426 rw_ops = ALmixer_RWFromFile(file_name, "rb");
|
|
427
|
|
428 return SoundDecoder_NewSample(rw_ops, file_extension, desired_format, buffer_size);
|
|
429 }
|
|
430
|
|
431
|
|
432 SoundDecoder_Sample* SoundDecoder_NewSample(ALmixer_RWops* rw_ops, const char* file_extension, SoundDecoder_AudioInfo* desired_format, size_t buffer_size)
|
|
433 {
|
|
434 SoundDecoder_Sample* new_sample;
|
|
435 SoundDecoder_SampleInternal* internal_sample;
|
|
436 SoundElement* current_decoder;
|
|
437
|
|
438 if(0 == s_isInitialized)
|
|
439 {
|
|
440 SoundDecoder_SetError(ERR_NOT_INITIALIZED);
|
|
441 return NULL;
|
|
442 }
|
|
443 if(NULL == rw_ops)
|
|
444 {
|
|
445 SoundDecoder_SetError("No file specified");
|
|
446 return NULL;
|
|
447 }
|
|
448
|
|
449 new_sample = (SoundDecoder_Sample*)calloc(1, sizeof(SoundDecoder_Sample));
|
|
450 if(NULL == new_sample)
|
|
451 {
|
|
452 SoundDecoder_SetError(ERR_OUT_OF_MEMORY);
|
|
453 return NULL;
|
|
454 }
|
|
455 internal_sample = (SoundDecoder_SampleInternal*)calloc(1, sizeof(SoundDecoder_SampleInternal));
|
|
456 if(NULL == internal_sample)
|
|
457 {
|
|
458 free(new_sample);
|
|
459 SoundDecoder_SetError(ERR_OUT_OF_MEMORY);
|
|
460 return NULL;
|
|
461 }
|
|
462 new_sample->buffer = calloc(1, buffer_size);
|
|
463 if(NULL == new_sample->buffer)
|
|
464 {
|
|
465 free(internal_sample);
|
|
466 free(new_sample);
|
|
467 SoundDecoder_SetError(ERR_OUT_OF_MEMORY);
|
|
468 return NULL;
|
|
469 }
|
|
470
|
|
471 new_sample->buffer_size = buffer_size;
|
|
472
|
|
473 if(NULL != desired_format)
|
|
474 {
|
|
475 memcpy(&new_sample->desired, desired_format, sizeof(SoundDecoder_AudioInfo));
|
|
476 }
|
|
477
|
|
478 internal_sample->rw = rw_ops;
|
|
479 new_sample->opaque = internal_sample;
|
|
480
|
|
481
|
|
482 if(NULL != file_extension)
|
|
483 {
|
|
484 for(current_decoder = &s_linkedDecoders[0]; current_decoder->funcs != NULL; current_decoder++)
|
|
485 {
|
|
486 if(current_decoder->available)
|
|
487 {
|
|
488 const char** decoder_file_extension = current_decoder->funcs->info.extensions;
|
|
489 while(*decoder_file_extension)
|
|
490 {
|
|
491 if(0 == (SoundDecoder_strcasecmp(*decoder_file_extension, file_extension)))
|
|
492 {
|
|
493 if(Internal_LoadSample(current_decoder->funcs, new_sample, file_extension))
|
|
494 {
|
|
495 return(new_sample);
|
|
496 }
|
|
497 break; /* go to the next decoder */
|
|
498 }
|
|
499 decoder_file_extension++;
|
|
500 }
|
|
501 }
|
|
502 }
|
|
503 }
|
|
504
|
|
505 /* no direct file_extensionension match? Try everything we've got... */
|
|
506 for(current_decoder = &s_linkedDecoders[0]; current_decoder->funcs != NULL; current_decoder++)
|
|
507 {
|
|
508 if(current_decoder->available)
|
|
509 {
|
|
510 int already_tried_decoder = 0;
|
|
511 const char** decoder_file_extension = current_decoder->funcs->info.extensions;
|
|
512
|
|
513 /* skip decoders we already tried from the above loop */
|
|
514 while(*decoder_file_extension)
|
|
515 {
|
|
516 if(SoundDecoder_strcasecmp(*decoder_file_extension, file_extension) == 0)
|
|
517 {
|
|
518 already_tried_decoder = 1;
|
|
519 break;
|
|
520 }
|
|
521 decoder_file_extension++;
|
|
522 }
|
|
523
|
|
524 if(0 == already_tried_decoder)
|
|
525 {
|
|
526 if (Internal_LoadSample(current_decoder->funcs, new_sample, file_extension))
|
|
527 {
|
|
528 return new_sample;
|
|
529 }
|
|
530 }
|
|
531 }
|
|
532 }
|
|
533
|
|
534 /* could not find a decoder */
|
|
535 SoundDecoder_SetError(ERR_UNSUPPORTED_FORMAT);
|
|
536 /* clean up the memory */
|
|
537 free(new_sample->opaque);
|
|
538 if(NULL != new_sample->buffer)
|
|
539 {
|
|
540 free(new_sample->buffer);
|
|
541 }
|
|
542 free(new_sample);
|
|
543
|
|
544 rw_ops->close(rw_ops);
|
|
545 return NULL;
|
|
546 }
|
|
547
|
|
548
|
|
549 int SoundDecoder_SetBufferSize(SoundDecoder_Sample* sound_sample, size_t new_buffer_size)
|
|
550 {
|
|
551 SoundDecoder_SampleInternal* internal_sample = NULL;
|
|
552 void* new_buffer_ptr = NULL;
|
|
553
|
|
554 BAIL_IF_MACRO(!s_isInitialized, ERR_NOT_INITIALIZED, 0);
|
|
555 BAIL_IF_MACRO(NULL == sound_sample, ERR_NULL_SAMPLE, 0);
|
|
556
|
|
557 internal_sample = ((SoundDecoder_SampleInternal*)sound_sample->opaque);
|
|
558
|
|
559
|
|
560 new_buffer_ptr = realloc(sound_sample->buffer, new_buffer_size);
|
|
561 BAIL_IF_MACRO(NULL == new_buffer_ptr, ERR_OUT_OF_MEMORY, 0);
|
|
562
|
|
563 sound_sample->buffer = new_buffer_ptr;
|
|
564 sound_sample->buffer_size = new_buffer_size;
|
|
565 internal_sample->buffer = sound_sample->buffer;
|
|
566 internal_sample->buffer_size = sound_sample->buffer_size;
|
|
567
|
|
568 return 1;
|
|
569 }
|
|
570
|
|
571
|
|
572 size_t SoundDecoder_Decode(SoundDecoder_Sample* sound_sample)
|
|
573 {
|
|
574 SoundDecoder_SampleInternal* internal_sample = NULL;
|
|
575 size_t bytes_read = 0;
|
|
576
|
|
577 BAIL_IF_MACRO(!s_isInitialized, ERR_NOT_INITIALIZED, 0);
|
|
578 BAIL_IF_MACRO(NULL == sound_sample, ERR_NULL_SAMPLE, 0);
|
|
579 BAIL_IF_MACRO(sound_sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREVIOUS_SAMPLE_ERROR, 0);
|
|
580 BAIL_IF_MACRO(sound_sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_ALREADY_AT_EOF_ERROR, 0);
|
|
581
|
|
582 internal_sample = (SoundDecoder_SampleInternal*)sound_sample->opaque;
|
|
583
|
|
584 assert(sound_sample->buffer != NULL);
|
|
585 assert(sound_sample->buffer_size > 0);
|
|
586 assert(internal_sample->buffer != NULL);
|
|
587 assert(internal_sample->buffer_size > 0);
|
|
588
|
|
589 /* reset EAGAIN. Decoder can flip it back on if it needs to. */
|
|
590 sound_sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
|
|
591 bytes_read = internal_sample->funcs->read(sound_sample);
|
|
592 return bytes_read;
|
|
593 }
|
|
594
|
|
595
|
|
596 size_t SoundDecoder_DecodeAll(SoundDecoder_Sample* sound_sample)
|
|
597 {
|
|
598 SoundDecoder_SampleInternal* internal_sample = NULL;
|
|
599 void* data_buffer = NULL;
|
|
600 size_t updated_buffer_size = 0;
|
|
601
|
|
602 BAIL_IF_MACRO(!s_isInitialized, ERR_NOT_INITIALIZED, 0);
|
|
603 BAIL_IF_MACRO(NULL == sound_sample, ERR_NULL_SAMPLE, 0);
|
|
604
|
|
605 /* My original thought was to call SetBufferSize and resize to
|
|
606 * the size needed to hold the entire decoded file utilizing total_time,
|
|
607 * but it appears SDL_sound simply loops on SoundDecoder_Decode.
|
|
608 * I suppose it is possible to partially decode or seek a file, and then
|
|
609 * call DecodeAll so you won't have the whole thing in which case
|
|
610 * my idea would waste memory.
|
|
611 */
|
|
612 while( (0 == (sound_sample->flags & SOUND_SAMPLEFLAG_EOF) )
|
|
613 && (0 == (sound_sample->flags & SOUND_SAMPLEFLAG_ERROR))
|
|
614 )
|
|
615 {
|
|
616 size_t bytes_decoded = SoundDecoder_Decode(sound_sample);
|
|
617 void* realloced_ptr = realloc(data_buffer, updated_buffer_size + bytes_decoded);
|
|
618 if(NULL == realloced_ptr)
|
|
619 {
|
|
620 sound_sample->flags |= SOUND_SAMPLEFLAG_ERROR;
|
|
621 SoundDecoder_SetError(ERR_OUT_OF_MEMORY);
|
|
622 if(NULL != data_buffer)
|
|
623 {
|
|
624 free(data_buffer);
|
|
625 }
|
|
626 return bytes_decoded;
|
|
627 }
|
|
628 data_buffer = realloced_ptr;
|
|
629 /* copy the chunk of decoded PCM to the end of our new data buffer */
|
|
630 memcpy( ((char*)data_buffer) + updated_buffer_size, sound_sample->buffer, bytes_decoded );
|
|
631 updated_buffer_size += bytes_decoded;
|
|
632 }
|
|
633
|
|
634 internal_sample = (SoundDecoder_SampleInternal*)sound_sample->opaque;
|
|
635 if(internal_sample->buffer != sound_sample->buffer)
|
|
636 {
|
|
637 free(internal_sample->buffer);
|
|
638 }
|
|
639 free(sound_sample->buffer);
|
|
640
|
|
641 sound_sample->buffer = data_buffer;
|
|
642 sound_sample->buffer_size = updated_buffer_size;
|
|
643 internal_sample->buffer = sound_sample->buffer;
|
|
644 internal_sample->buffer_size = sound_sample->buffer_size;
|
|
645
|
|
646 return sound_sample->buffer_size;
|
|
647 }
|
|
648
|
|
649
|
|
650 int SoundDecoder_Rewind(SoundDecoder_Sample* sound_sample)
|
|
651 {
|
|
652 SoundDecoder_SampleInternal* internal_sample;
|
|
653 int ret_val;
|
|
654
|
|
655 BAIL_IF_MACRO(!s_isInitialized, ERR_NOT_INITIALIZED, 0);
|
|
656 BAIL_IF_MACRO(NULL == sound_sample, ERR_NULL_SAMPLE, 0);
|
|
657
|
|
658 internal_sample = (SoundDecoder_SampleInternal*)sound_sample->opaque;
|
|
659 ret_val = internal_sample->funcs->rewind(sound_sample);
|
|
660 if(0 == ret_val)
|
|
661 {
|
|
662 sound_sample->flags |= SOUND_SAMPLEFLAG_ERROR;
|
|
663 SoundDecoder_SetError("Rewind failed");
|
|
664 return 0;
|
|
665 }
|
|
666 /* reset flags */
|
|
667 sound_sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
|
|
668 sound_sample->flags &= ~SOUND_SAMPLEFLAG_ERROR;
|
|
669 sound_sample->flags &= ~SOUND_SAMPLEFLAG_EOF;
|
|
670
|
|
671 return 1;
|
|
672 }
|
|
673
|
|
674
|
|
675 int SoundDecoder_Seek(SoundDecoder_Sample* sound_sample, size_t ms)
|
|
676 {
|
|
677 SoundDecoder_SampleInternal* internal_sample;
|
|
678 int ret_val;
|
|
679
|
|
680 BAIL_IF_MACRO(!s_isInitialized, ERR_NOT_INITIALIZED, 0);
|
|
681 BAIL_IF_MACRO(NULL == sound_sample, ERR_NULL_SAMPLE, 0);
|
|
682
|
|
683 BAIL_IF_MACRO(!(sound_sample->flags & SOUND_SAMPLEFLAG_CANSEEK), "Sound sample is not seekable", 0);
|
|
684
|
|
685 internal_sample = (SoundDecoder_SampleInternal*)sound_sample->opaque;
|
|
686 ret_val = internal_sample->funcs->seek(sound_sample, ms);
|
|
687 if(0 == ret_val)
|
|
688 {
|
|
689 sound_sample->flags |= SOUND_SAMPLEFLAG_ERROR;
|
|
690 SoundDecoder_SetError("Seek failed");
|
|
691 return 0;
|
|
692 }
|
|
693 /* reset flags */
|
|
694 sound_sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN;
|
|
695 sound_sample->flags &= ~SOUND_SAMPLEFLAG_ERROR;
|
|
696 sound_sample->flags &= ~SOUND_SAMPLEFLAG_EOF;
|
|
697
|
|
698 return 1;
|
|
699 }
|
|
700
|
|
701
|
|
702 ptrdiff_t SoundDecoder_GetDuration(SoundDecoder_Sample* sound_sample)
|
|
703 {
|
|
704 SoundDecoder_SampleInternal* internal_sample;
|
|
705 BAIL_IF_MACRO(!s_isInitialized, ERR_NOT_INITIALIZED, -1);
|
|
706 BAIL_IF_MACRO(NULL == sound_sample, ERR_NULL_SAMPLE, 0);
|
|
707 internal_sample = (SoundDecoder_SampleInternal*)sound_sample->opaque;
|
|
708 return internal_sample->total_time;
|
|
709 }
|
|
710
|
|
711 #endif
|