Mercurial > almixer_isolated
comparison Isolated/SoundDecoder.c @ 38:71b465ff0622
Added support files.
author | Eric Wing <ewing@anscamobile.com> |
---|---|
date | Thu, 28 Apr 2011 16:22:30 -0700 |
parents | |
children | 5c9eaf0cc385 |
comparison
equal
deleted
inserted
replaced
37:b346b6608eab | 38:71b465ff0622 |
---|---|
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 |