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.