Mercurial > SDL_sound_CoreAudio
comparison playsound/playsound.c @ 310:9e21cb0d3ae7
Seek support added.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Wed, 24 Apr 2002 20:01:20 +0000 |
parents | 6a80b2f9c47c |
children | 8195b86207bb |
comparison
equal
deleted
inserted
replaced
309:20e443fc4b7b | 310:9e21cb0d3ae7 |
---|---|
116 " --volume n Playback volume multiplier (default 1.0).\n" | 116 " --volume n Playback volume multiplier (default 1.0).\n" |
117 " --stdin [ext] Read from stdin (treat data as format [ext])\n" | 117 " --stdin [ext] Read from stdin (treat data as format [ext])\n" |
118 " --version Display version information and exit.\n" | 118 " --version Display version information and exit.\n" |
119 " --decoders List supported data formats and exit.\n" | 119 " --decoders List supported data formats and exit.\n" |
120 " --predecode Decode entire sample before playback.\n" | 120 " --predecode Decode entire sample before playback.\n" |
121 " --loop n Loop playback n times. 0=forever.\n" | 121 " --loop n Loop playback n times.\n" |
122 | 122 " --seek list List of seek points and playback durations.\n" |
123 /*" --seek list List of seek points and playback durations.\n"*/ | |
124 | |
125 " --credits Shameless promotion.\n" | 123 " --credits Shameless promotion.\n" |
126 " --help Display this information and exit.\n" | 124 " --help Display this information and exit.\n" |
127 "\n" | 125 "\n" |
128 " Valid arguments to the --format option are:\n" | 126 " Valid arguments to the --format option are:\n" |
129 " U8 Unsigned 8-bit.\n" | 127 " U8 Unsigned 8-bit.\n" |
130 " S8 Signed 8-bit.\n" | 128 " S8 Signed 8-bit.\n" |
131 " U16LSB Unsigned 16-bit (least significant byte first).\n" | 129 " U16LSB Unsigned 16-bit (least significant byte first).\n" |
132 " U16MSB Unsigned 16-bit (most significant byte first).\n" | 130 " U16MSB Unsigned 16-bit (most significant byte first).\n" |
133 " S16LSB Signed 16-bit (least significant byte first).\n" | 131 " S16LSB Signed 16-bit (least significant byte first).\n" |
134 " S16MSB Signed 16-bit (most significant byte first).\n" | 132 " S16MSB Signed 16-bit (most significant byte first).\n" |
135 | |
136 /* | |
137 "\n" | 133 "\n" |
138 " Valid arguments to the --seek options look like:\n" | 134 " Valid arguments to the --seek options look like:\n" |
139 " --seek=\"mm:ss;mm:ss;mm:ss\"\n" | 135 " --seek=\"mm:ss;mm:ss;mm:ss\"\n" |
140 " Where the first \"mm:ss\" is the position, in minutes and\n" | 136 " Where the first \"mm:ss\" is the position, in minutes and\n" |
141 " seconds to seek to at start of playback. The second mm:ss\n" | 137 " seconds to seek to at start of playback. The second mm:ss\n" |
142 " is how long to play audio from that point. The third mm:ss\n" | 138 " is how long to play audio from that point. The third mm:ss\n" |
143 " is another seek after the duration of playback has completed.\n" | 139 " is another seek after the duration of playback has completed.\n" |
144 " If the final playback duration is omitted, playback continues\n" | 140 " If the final playback duration is omitted, playback continues\n" |
145 " until the end of the file. --loop and --seek can coexist.\n" | 141 " until the end of the file. --loop and --seek can coexist.\n" |
146 */ | |
147 | |
148 "\n", | 142 "\n", |
149 argv0, DEFAULT_DECODEBUF, DEFAULT_AUDIOBUF); | 143 argv0, DEFAULT_DECODEBUF, DEFAULT_AUDIOBUF); |
150 } /* output_usage */ | 144 } /* output_usage */ |
151 | 145 |
152 | 146 |
293 /* !!! FIXME: Put this in a struct and pass a pointer to it as the | 287 /* !!! FIXME: Put this in a struct and pass a pointer to it as the |
294 * !!! FIXME: audio callback's argument. This will clean up the | 288 * !!! FIXME: audio callback's argument. This will clean up the |
295 * !!! FIXME: namespace and let me reinitialize this for each file in | 289 * !!! FIXME: namespace and let me reinitialize this for each file in |
296 * !!! FIXME: a cleaner way. | 290 * !!! FIXME: a cleaner way. |
297 */ | 291 */ |
298 static Uint8 *decoded_ptr = NULL; | 292 static volatile Uint8 *decoded_ptr = NULL; |
299 static Uint32 decoded_bytes = 0; | 293 static volatile Uint32 decoded_bytes = 0; |
300 static int predecode = 0; | 294 static volatile int predecode = 0; |
301 static int looping = 0; | 295 static volatile int looping = 0; |
302 static int wants_volume_change = 0; | 296 static volatile int wants_volume_change = 0; |
303 static float volume = 1.0; | 297 static volatile float volume = 1.0; |
298 static volatile Uint32 total_seeks = 0; | |
299 static volatile Uint32 *seek_list = NULL; | |
300 static volatile Uint32 seek_index = 0; | |
301 static volatile Sint32 bytes_before_next_seek = -1; | |
302 | |
303 static Uint32 cvtMsToBytePos(Sound_AudioInfo *info, Uint32 ms) | |
304 { | |
305 /* "frames" == "sample frames" */ | |
306 float frames_per_ms = ((float) info->rate) / 1000.0; | |
307 Uint32 frame_offset = (Uint32) (frames_per_ms * ((float) ms)); | |
308 Uint32 frame_size = (Uint32) ((info->format & 0xFF) / 8) * info->channels; | |
309 return(frame_offset * frame_size); | |
310 } /* cvtMsToBytePos */ | |
311 | |
312 | |
313 static void do_seek(Sound_Sample *sample) | |
314 { | |
315 printf("Seeking to %.2d:%.2d...\n", | |
316 ((seek_list[seek_index] / 1000) / 60), | |
317 ((seek_list[seek_index] / 1000) % 60)); | |
318 | |
319 if (predecode) | |
320 { | |
321 Uint32 pos = cvtMsToBytePos(&sample->desired, seek_list[seek_index]); | |
322 if (pos > sample->buffer_size) | |
323 { | |
324 fprintf(stderr, "Seek past end of predecoded buffer.\n"); | |
325 done_flag = 1; | |
326 } /* if */ | |
327 else | |
328 { | |
329 decoded_ptr = (((Uint8 *) sample->buffer) + pos); | |
330 decoded_bytes = sample->buffer_size - pos; | |
331 } /* else */ | |
332 } /* if */ | |
333 else | |
334 { | |
335 if (!Sound_Seek(sample, seek_list[seek_index])) | |
336 { | |
337 fprintf(stderr, "Sound_Seek() failed: %s\n", Sound_GetError()); | |
338 done_flag = 1; | |
339 } /* if */ | |
340 } /* else */ | |
341 | |
342 seek_index++; | |
343 if (seek_index >= total_seeks) | |
344 bytes_before_next_seek = -1; /* no more seeks. */ | |
345 else | |
346 { | |
347 bytes_before_next_seek = cvtMsToBytePos(&sample->desired, | |
348 seek_list[seek_index]); | |
349 seek_index++; | |
350 } /* else */ | |
351 } /* do_seek */ | |
304 | 352 |
305 | 353 |
306 /* | 354 /* |
307 * This updates (decoded_bytes) and (decoder_ptr) with more audio data, | 355 * This updates (decoded_bytes) and (decoded_ptr) with more audio data, |
308 * taking into account looping and/or predecoding. | 356 * taking into account potential looping, seeking and predecoding. |
309 */ | 357 */ |
310 static int read_more_data(Sound_Sample *sample) | 358 static int read_more_data(Sound_Sample *sample) |
311 { | 359 { |
312 if (done_flag) /* probably a sigint; stop trying to read. */ | 360 if (done_flag) /* probably a sigint; stop trying to read. */ |
361 { | |
313 decoded_bytes = 0; | 362 decoded_bytes = 0; |
363 return(0); | |
364 } /* if */ | |
365 | |
366 if ((bytes_before_next_seek >= 0) && | |
367 (decoded_bytes > bytes_before_next_seek)) | |
368 { | |
369 decoded_bytes = bytes_before_next_seek; | |
370 } /* if */ | |
314 | 371 |
315 if (decoded_bytes > 0) /* don't need more data; just return. */ | 372 if (decoded_bytes > 0) /* don't need more data; just return. */ |
316 return(decoded_bytes); | 373 return(decoded_bytes); |
317 | 374 |
318 /* need more. See if there's more to be read... */ | 375 /* Need more audio data. See if we're supposed to seek... */ |
319 if (!(sample->flags & (SOUND_SAMPLEFLAG_ERROR | SOUND_SAMPLEFLAG_EOF))) | 376 if ((bytes_before_next_seek == 0) && (seek_index < total_seeks)) |
377 { | |
378 do_seek(sample); /* do it, baby! */ | |
379 return(read_more_data(sample)); /* handle loops conditions. */ | |
380 } /* if */ | |
381 | |
382 /* See if there's more to be read... */ | |
383 if ( (bytes_before_next_seek != 0) && | |
384 (!(sample->flags & (SOUND_SAMPLEFLAG_ERROR | SOUND_SAMPLEFLAG_EOF))) ) | |
320 { | 385 { |
321 decoded_bytes = Sound_Decode(sample); | 386 decoded_bytes = Sound_Decode(sample); |
322 if (sample->flags & SOUND_SAMPLEFLAG_ERROR) | 387 if (sample->flags & SOUND_SAMPLEFLAG_ERROR) |
323 { | 388 { |
324 fprintf(stderr, "Error in decoding sound file!\n" | 389 fprintf(stderr, "Error in decoding sound file!\n" |
334 if (!looping) | 399 if (!looping) |
335 return(0); | 400 return(0); |
336 | 401 |
337 looping--; | 402 looping--; |
338 | 403 |
404 seek_index = 0; | |
405 bytes_before_next_seek = (total_seeks > 0) ? 0 : -1; | |
406 | |
339 /* we just need to point predecoded samples to the start of the buffer. */ | 407 /* we just need to point predecoded samples to the start of the buffer. */ |
340 if (predecode) | 408 if (predecode) |
341 { | 409 { |
342 decoded_bytes = sample->buffer_size; | 410 decoded_bytes = sample->buffer_size; |
343 decoded_ptr = sample->buffer; | 411 decoded_ptr = sample->buffer; |
344 return(decoded_bytes); | |
345 } /* if */ | 412 } /* if */ |
346 else | 413 else |
347 { | 414 { |
348 Sound_Rewind(sample); /* error is checked in recursion. */ | 415 Sound_Rewind(sample); /* error is checked in recursion. */ |
349 return(read_more_data(sample)); | |
350 } /* else */ | 416 } /* else */ |
351 | 417 |
352 assert(0); /* shouldn't ever hit this point. */ | 418 return(read_more_data(sample)); |
353 return(0); | |
354 } /* read_more_data */ | 419 } /* read_more_data */ |
355 | 420 |
356 | 421 |
357 static void memcpy_with_volume(Sound_Sample *sample, | 422 static void memcpy_with_volume(Sound_Sample *sample, |
358 Uint8 *dst, Uint8 *src, int len) | 423 Uint8 *dst, Uint8 *src, int len) |
448 if (cpysize > decoded_bytes) | 513 if (cpysize > decoded_bytes) |
449 cpysize = decoded_bytes; | 514 cpysize = decoded_bytes; |
450 | 515 |
451 if (cpysize > 0) | 516 if (cpysize > 0) |
452 { | 517 { |
453 memcpy_with_volume(sample, stream + bw, decoded_ptr, cpysize); | 518 memcpy_with_volume(sample, stream + bw, |
519 (Uint8 *) decoded_ptr, cpysize); | |
454 bw += cpysize; | 520 bw += cpysize; |
455 decoded_ptr += cpysize; | 521 decoded_ptr += cpysize; |
456 decoded_bytes -= cpysize; | 522 decoded_bytes -= cpysize; |
523 if (bytes_before_next_seek >= 0) | |
524 bytes_before_next_seek -= cpysize; | |
457 } /* if */ | 525 } /* if */ |
458 } /* while */ | 526 } /* while */ |
459 } /* audio_callback */ | 527 } /* audio_callback */ |
528 | |
529 | |
530 static int count_seek_list(const char *list) | |
531 { | |
532 const char *ptr; | |
533 int retval = 0; | |
534 | |
535 for (ptr = list; ptr != NULL; ptr = strchr(ptr + 1, ';')) | |
536 retval++; | |
537 | |
538 return(retval); | |
539 } /* count_seek_list */ | |
540 | |
541 | |
542 static Uint32 parse_time_str(char *str) | |
543 { | |
544 Uint32 minutes = 0; | |
545 Uint32 seconds = 0; | |
546 char *ptr = strchr(str, ':'); | |
547 if (ptr != NULL) | |
548 { | |
549 *ptr = '\0'; | |
550 minutes = atoi(str); | |
551 str = ptr + 1; | |
552 } /* if */ | |
553 | |
554 seconds = atoi(str); | |
555 | |
556 return( ((minutes * 60) + seconds) * 1000 ); | |
557 } /* parse_time_str */ | |
558 | |
559 | |
560 static void parse_seek_list(const char *_list) | |
561 { | |
562 Uint32 i; | |
563 char *list = alloca(strlen(_list) + 1); | |
564 if (list == NULL) | |
565 { | |
566 fprintf(stderr, "alloca() failed. Skipping seek list.\n"); | |
567 return; | |
568 } /* if */ | |
569 | |
570 strcpy(list, _list); | |
571 | |
572 if (seek_list != NULL) | |
573 free((void *) seek_list); | |
574 | |
575 total_seeks = count_seek_list(list); | |
576 seek_list = (Uint32 *) malloc(total_seeks * sizeof (Uint32)); | |
577 if (seek_list == NULL) | |
578 { | |
579 fprintf(stderr, "malloc() failed. Skipping seek list.\n"); | |
580 total_seeks = 0; | |
581 return; | |
582 } /* if */ | |
583 | |
584 for (i = 0; i < total_seeks; i++) | |
585 { | |
586 char *ptr = strchr(list, ';'); | |
587 if (ptr != NULL) | |
588 *ptr = '\0'; | |
589 seek_list[i] = parse_time_str(list); | |
590 list = ptr + 1; | |
591 } /* for */ | |
592 } /* parse_seek_list */ | |
460 | 593 |
461 | 594 |
462 static int str_to_fmt(char *str) | 595 static int str_to_fmt(char *str) |
463 { | 596 { |
464 if (strcmp(str, "U8") == 0) | 597 if (strcmp(str, "U8") == 0) |
570 | 703 |
571 for (i = 1; i < argc; i++) | 704 for (i = 1; i < argc; i++) |
572 { | 705 { |
573 char *filename = NULL; | 706 char *filename = NULL; |
574 | 707 |
708 /* !!! FIXME: Go read gripe about all the global variables. */ | |
575 /* set variables back to defaults for next file... */ | 709 /* set variables back to defaults for next file... */ |
576 if (new_sample) | 710 if (new_sample) |
577 { | 711 { |
578 new_sample = 0; | 712 new_sample = 0; |
579 memset(&sound_desired, '\0', sizeof (sound_desired)); | 713 memset(&sound_desired, '\0', sizeof (sound_desired)); |
586 decode_buffersize = DEFAULT_DECODEBUF; | 720 decode_buffersize = DEFAULT_DECODEBUF; |
587 sample = NULL; | 721 sample = NULL; |
588 use_specific_audiofmt = 0; | 722 use_specific_audiofmt = 0; |
589 wants_volume_change = 0; | 723 wants_volume_change = 0; |
590 volume = 1.0; | 724 volume = 1.0; |
725 if (seek_list != NULL) | |
726 { | |
727 free((void *) seek_list); | |
728 seek_list = NULL; | |
729 } /* if */ | |
730 total_seeks = 0; | |
731 seek_index = 0; | |
732 bytes_before_next_seek = -1; | |
591 } /* if */ | 733 } /* if */ |
592 | 734 |
593 if (strcmp(argv[i], "--rate") == 0 && argc > i + 1) | 735 if (strcmp(argv[i], "--rate") == 0 && argc > i + 1) |
594 { | 736 { |
595 use_specific_audiofmt = 1; | 737 use_specific_audiofmt = 1; |
596 sound_desired.rate = atoi(argv[++i]); | 738 sound_desired.rate = atoi(argv[++i]); |
597 if (sound_desired.rate <= 0) | 739 if (sound_desired.rate <= 0) |
598 { | 740 { |
599 fprintf(stderr, "Bad argument to --rate!\n"); | 741 fprintf(stderr, "Bad argument to --rate!\n"); |
600 return(42); | 742 return(42); |
601 } | 743 } /* if */ |
602 } /* else if */ | 744 } /* else if */ |
603 | 745 |
604 else if (strcmp(argv[i], "--format") == 0 && argc > i + 1) | 746 else if (strcmp(argv[i], "--format") == 0 && argc > i + 1) |
605 { | 747 { |
606 use_specific_audiofmt = 1; | 748 use_specific_audiofmt = 1; |
608 if (sound_desired.format == 0) | 750 if (sound_desired.format == 0) |
609 { | 751 { |
610 fprintf(stderr, "Bad argument to --format! Try one of:\n" | 752 fprintf(stderr, "Bad argument to --format! Try one of:\n" |
611 "U8, S8, U16LSB, S16LSB, U16MSB, S16MSB\n"); | 753 "U8, S8, U16LSB, S16LSB, U16MSB, S16MSB\n"); |
612 return(42); | 754 return(42); |
613 } | 755 } /* if */ |
614 } /* else if */ | 756 } /* else if */ |
615 | 757 |
616 else if (strcmp(argv[i], "--channels") == 0 && argc > i + 1) | 758 else if (strcmp(argv[i], "--channels") == 0 && argc > i + 1) |
617 { | 759 { |
618 use_specific_audiofmt = 1; | 760 use_specific_audiofmt = 1; |
621 { | 763 { |
622 fprintf(stderr, | 764 fprintf(stderr, |
623 "Bad argument to --channels! Try 1 (mono) or 2 " | 765 "Bad argument to --channels! Try 1 (mono) or 2 " |
624 "(stereo).\n"); | 766 "(stereo).\n"); |
625 return(42); | 767 return(42); |
626 } | 768 } /* if */ |
627 } /* else if */ | 769 } /* else if */ |
628 | 770 |
629 else if (strcmp(argv[i], "--audiobuf") == 0 && argc > i + 1) | 771 else if (strcmp(argv[i], "--audiobuf") == 0 && argc > i + 1) |
630 { | 772 { |
631 audio_buffersize = atoi(argv[++i]); | 773 audio_buffersize = atoi(argv[++i]); |
649 } /* else if */ | 791 } /* else if */ |
650 | 792 |
651 else if (strcmp(argv[i], "--loop") == 0) | 793 else if (strcmp(argv[i], "--loop") == 0) |
652 { | 794 { |
653 looping = atoi(argv[++i]); | 795 looping = atoi(argv[++i]); |
796 } /* else if */ | |
797 | |
798 else if (strcmp(argv[i], "--seek") == 0) | |
799 { | |
800 parse_seek_list(argv[++i]); | |
654 } /* else if */ | 801 } /* else if */ |
655 | 802 |
656 else if (strcmp(argv[i], "--stdin") == 0) | 803 else if (strcmp(argv[i], "--stdin") == 0) |
657 { | 804 { |
658 SDL_RWops *rw = SDL_RWFromFP(stdin, 1); | 805 SDL_RWops *rw = SDL_RWFromFP(stdin, 1); |
666 use_specific_audiofmt ? &sound_desired : NULL, | 813 use_specific_audiofmt ? &sound_desired : NULL, |
667 decode_buffersize); | 814 decode_buffersize); |
668 } /* if */ | 815 } /* if */ |
669 | 816 |
670 else if (strncmp(argv[i], "--", 2) == 0) | 817 else if (strncmp(argv[i], "--", 2) == 0) |
671 /* ignore it. */ ; | 818 { |
819 /* ignore it. */ | |
820 } /* else if */ | |
672 | 821 |
673 else | 822 else |
674 { | 823 { |
675 filename = argv[i]; | 824 filename = argv[i]; |
676 sample = sample_from_archive(filename, | 825 sample = sample_from_archive(filename, |
694 { | 843 { |
695 fprintf(stderr, "Couldn't load \"%s\"!\n" | 844 fprintf(stderr, "Couldn't load \"%s\"!\n" |
696 " reason: [%s].\n", | 845 " reason: [%s].\n", |
697 filename, Sound_GetError()); | 846 filename, Sound_GetError()); |
698 continue; | 847 continue; |
848 } /* if */ | |
849 | |
850 if (total_seeks > 0) | |
851 { | |
852 if ((!predecode) && (!(sample->flags & SOUND_SAMPLEFLAG_CANSEEK))) | |
853 { | |
854 fprintf(stderr, "Want seeks, but sample cannot handle it!\n"); | |
855 Sound_FreeSample(sample); | |
856 close_archive(filename); | |
857 continue; | |
858 } /* if */ | |
859 | |
860 bytes_before_next_seek = 0; | |
699 } /* if */ | 861 } /* if */ |
700 | 862 |
701 /* | 863 /* |
702 * Unless explicitly specified, pick the format from the sound | 864 * Unless explicitly specified, pick the format from the sound |
703 * to be played. | 865 * to be played. |