From c80bdc6a8d76569d0121b643a8e73d8b47c0a718 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Tue, 10 Dec 2002 06:43:59 +0000 Subject: [PATCH] revamp --skip processing, allow new mm:ss.sss form --- src/flac/decode.c | 72 ++++++++++++++++++++++++++++++++++------------- src/flac/decode.h | 3 +- src/flac/encode.c | 58 +++++++++++++++++++++++++++----------- src/flac/encode.h | 3 +- src/flac/main.c | 27 +++++++++++------- 5 files changed, 114 insertions(+), 49 deletions(-) diff --git a/src/flac/decode.c b/src/flac/decode.c index c92cd96f..e1a3906f 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -48,7 +48,7 @@ typedef struct { FLAC__bool test_only; FLAC__bool analysis_mode; analysis_options aopts; - FLAC__uint64 skip; + utils__SkipUntilSpecification *skip_specification; const char *inbasefilename; const char *outfilename; @@ -94,7 +94,7 @@ static FLAC__bool is_big_endian_host_; /* * local routines */ -static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, FLAC__uint64 skip, const char *infilename, const char *outfilename); +static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, const char *infilename, const char *outfilename); static void DecoderSession_destroy(DecoderSession *d, FLAC__bool error_occurred); static FLAC__bool DecoderSession_init_decoder(DecoderSession *d, decode_options_t decode_options, const char *infilename); static FLAC__bool DecoderSession_process(DecoderSession *d); @@ -138,7 +138,7 @@ int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool options.common.continue_through_decode_errors, analysis_mode, aopts, - options.common.skip, + &options.common.skip_specification, infilename, outfilename ) @@ -174,7 +174,7 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool options.common.continue_through_decode_errors, analysis_mode, aopts, - options.common.skip, + &options.common.skip_specification, infilename, outfilename ) @@ -190,7 +190,7 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool return DecoderSession_finish_ok(&decoder_session); } -FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, FLAC__uint64 skip, const char *infilename, const char *outfilename) +FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, const char *infilename, const char *outfilename) { #ifdef FLAC__HAS_OGG d->is_ogg = is_ogg; @@ -204,7 +204,7 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__ d->test_only = (0 == outfilename); d->analysis_mode = analysis_mode; d->aopts = aopts; - d->skip = skip; + d->skip_specification = skip_specification; d->inbasefilename = grabbag__file_get_basename(infilename); d->outfilename = outfilename; @@ -341,20 +341,46 @@ FLAC__bool DecoderSession_init_decoder(DecoderSession *decoder_session, decode_o FLAC__bool DecoderSession_process(DecoderSession *d) { - if(d->skip > 0) { +#ifdef FLAC__HAS_OGG + if(d->is_ogg) { + if(!OggFLAC__stream_decoder_process_until_end_of_metadata(d->decoder.ogg.stream)) { + if(d->verbose) fprintf(stderr, "\n"); + print_error_with_state(d, "ERROR while decoding metadata"); + return false; + } + if(OggFLAC__stream_decoder_get_FLAC_stream_decoder_state(d->decoder.ogg.stream) != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && OggFLAC__stream_decoder_get_FLAC_stream_decoder_state(d->decoder.ogg.stream) != FLAC__STREAM_DECODER_END_OF_STREAM) { + if(d->verbose) fprintf(stderr, "\n"); + print_error_with_state(d, "ERROR during metadata decoding"); + return false; + } + } + else +#endif + { + if(!FLAC__file_decoder_process_until_end_of_metadata(d->decoder.flac.file)) { + if(d->verbose) fprintf(stderr, "\n"); + print_error_with_state(d, "ERROR while decoding metadata"); + return false; + } + if(FLAC__file_decoder_get_state(d->decoder.flac.file) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_get_state(d->decoder.flac.file) != FLAC__FILE_DECODER_END_OF_FILE) { + if(d->verbose) fprintf(stderr, "\n"); + print_error_with_state(d, "ERROR during metadata decoding"); + return false; + } + } + if(d->abort_flag) + return false; + + if(d->skip_specification->value.samples > 0) { + const FLAC__uint64 skip = (FLAC__uint64)d->skip_specification->value.samples; + #ifdef FLAC__HAS_OGG if(d->is_ogg) { /*@@@ (move this check into main.c) */ fprintf(stderr, "%s: ERROR, can't skip when decoding Ogg-FLAC yet; convert to native-FLAC first\n", d->inbasefilename); return false; } #endif - if(!FLAC__file_decoder_process_until_end_of_metadata(d->decoder.flac.file)) { - print_error_with_state(d, "ERROR while decoding metadata"); - return false; - } - if(d->abort_flag) - return false; - if(!FLAC__file_decoder_seek_absolute(d->decoder.flac.file, d->skip)) { + if(!FLAC__file_decoder_seek_absolute(d->decoder.flac.file, skip)) { print_error_with_state(d, "ERROR seeking while skipping bytes"); return false; } @@ -679,22 +705,28 @@ void metadata_callback(const void *decoder, const FLAC__StreamMetadata *metadata DecoderSession *decoder_session = (DecoderSession*)client_data; (void)decoder; if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + FLAC__uint64 skip; + decoder_session->bps = metadata->data.stream_info.bits_per_sample; + decoder_session->channels = metadata->data.stream_info.channels; + decoder_session->sample_rate = metadata->data.stream_info.sample_rate; + + flac__utils_canonicalize_skip_until_specification(decoder_session->skip_specification, decoder_session->sample_rate); + FLAC__ASSERT(decoder_session->skip_specification->value.samples >= 0); + skip = (FLAC__uint64)decoder_session->skip_specification->value.samples; + /* remember, metadata->data.stream_info.total_samples can be 0, meaning 'unknown' */ - if(metadata->data.stream_info.total_samples > 0 && decoder_session->skip >= metadata->data.stream_info.total_samples) { + if(metadata->data.stream_info.total_samples > 0 && skip >= metadata->data.stream_info.total_samples) { fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", decoder_session->inbasefilename); decoder_session->abort_flag = true; return; } - else if(metadata->data.stream_info.total_samples == 0 && decoder_session->skip > 0) { + else if(metadata->data.stream_info.total_samples == 0 && skip > 0) { fprintf(stderr, "%s: ERROR, can't skip when FLAC metadata has total sample count of 0\n", decoder_session->inbasefilename); decoder_session->abort_flag = true; return; } else - decoder_session->total_samples = metadata->data.stream_info.total_samples - decoder_session->skip; - decoder_session->bps = metadata->data.stream_info.bits_per_sample; - decoder_session->channels = metadata->data.stream_info.channels; - decoder_session->sample_rate = metadata->data.stream_info.sample_rate; + decoder_session->total_samples = metadata->data.stream_info.total_samples - skip; if(decoder_session->bps != 8 && decoder_session->bps != 16 && decoder_session->bps != 24) { fprintf(stderr, "%s: ERROR: bits per sample is not 8/16/24\n", decoder_session->inbasefilename); diff --git a/src/flac/decode.h b/src/flac/decode.h index 4298c31f..5e0ee755 100644 --- a/src/flac/decode.h +++ b/src/flac/decode.h @@ -20,6 +20,7 @@ #define flac__decode_h #include "analyze.h" +#include "utils.h" #ifdef HAVE_CONFIG_H #include @@ -33,7 +34,7 @@ typedef struct { FLAC__bool use_first_serial_number; long serial_number; #endif - FLAC__uint64 skip; + utils__SkipUntilSpecification skip_specification; } decode_options_t; typedef struct { diff --git a/src/flac/encode.c b/src/flac/encode.c index 02da8eb8..b020aef5 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -58,6 +58,7 @@ typedef struct { const char *inbasefilename; const char *outfilename; + FLAC__uint64 skip; FLAC__bool replay_gain; unsigned channels; unsigned bits_per_sample; @@ -157,8 +158,6 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch FLAC__bool got_comm_chunk= false, got_ssnd_chunk= false; int info_align_carry= -1, info_align_zero= -1; - FLAC__ASSERT(!options.common.sector_align || options.common.skip == 0); - (void)infilesize; /* silence compiler warning about unused parameter */ (void)lookahead; /* silence compiler warning about unused parameter */ (void)lookahead_length; /* silence compiler warning about unused parameter */ @@ -263,6 +262,15 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch skip-= need; } + /* + * now that we know the sample rate, canonicalize the + * --skip string to a number of samples: + */ + flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate); + FLAC__ASSERT(options.common.skip_specification.value.samples >= 0); + encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples; + FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0); + got_comm_chunk= true; } else if(got_ssnd_chunk==false && !strncmp(chunk_id, "SSND", 4)) { /* sound data chunk */ @@ -303,8 +311,8 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch } block_size= xx; - if(options.common.skip>0U) { - FLAC__uint64 remaining= options.common.skip*bytes_per_frame; + if(encoder_session.skip>0U) { + FLAC__uint64 remaining= encoder_session.skip*bytes_per_frame; /* do 1<<30 bytes at a time, since 1<<30 is a nice round number, and */ /* is guaranteed to be less than LONG_MAX */ @@ -324,7 +332,7 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch } } - data_bytes-= (8U + (unsigned int)options.common.skip*bytes_per_frame); /*@@@ WATCHOUT: 4GB limit */ + data_bytes-= (8U + (unsigned int)encoder_session.skip*bytes_per_frame); /*@@@ WATCHOUT: 4GB limit */ encoder_session.total_samples_to_encode= data_bytes/bytes_per_frame + *options.common.align_reservoir_samples; if(options.common.sector_align) { align_remainder= (unsigned int)(encoder_session.total_samples_to_encode % 588U); @@ -487,8 +495,6 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons unsigned align_remainder = 0; int info_align_carry = -1, info_align_zero = -1; - FLAC__ASSERT(!options.common.sector_align || options.common.skip == 0); - (void)infilesize; (void)lookahead; (void)lookahead_length; @@ -595,6 +601,15 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons } } + /* + * now that we know the sample rate, canonicalize the + * --skip string to a number of samples: + */ + flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate); + FLAC__ASSERT(options.common.skip_specification.value.samples >= 0); + encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples; + FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0); + got_fmt_chunk = true; } else if(xx == 0x61746164 && !got_data_chunk && got_fmt_chunk) { /* "data" */ @@ -605,11 +620,11 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons bytes_per_wide_sample = channels * (bps >> 3); - if(options.common.skip > 0) { - if(fseek(infile, bytes_per_wide_sample * (unsigned)options.common.skip, SEEK_CUR) < 0) { + if(encoder_session.skip > 0) { + if(fseek(infile, bytes_per_wide_sample * (unsigned)encoder_session.skip, SEEK_CUR) < 0) { /* can't seek input, read ahead manually... */ unsigned left, need; - for(left = (unsigned)options.common.skip; left > 0; ) { /*@@@ WATCHOUT: 4GB limit */ + for(left = (unsigned)encoder_session.skip; left > 0; ) { /*@@@ WATCHOUT: 4GB limit */ need = min(left, CHUNK_OF_SAMPLES); if(fread(ucbuffer_, bytes_per_wide_sample, need, infile) < need) { fprintf(stderr, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename); @@ -620,7 +635,7 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons } } - data_bytes -= (unsigned)options.common.skip * bytes_per_wide_sample; /*@@@ WATCHOUT: 4GB limit */ + data_bytes -= (unsigned)encoder_session.skip * bytes_per_wide_sample; /*@@@ WATCHOUT: 4GB limit */ encoder_session.total_samples_to_encode = data_bytes / bytes_per_wide_sample + *options.common.align_reservoir_samples; if(options.common.sector_align) { align_remainder = (unsigned)(encoder_session.total_samples_to_encode % 588); @@ -783,12 +798,10 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons unsigned align_remainder = 0; int info_align_carry = -1, info_align_zero = -1; - FLAC__ASSERT(!options.common.sector_align || options.common.skip == 0); FLAC__ASSERT(!options.common.sector_align || options.channels == 2); FLAC__ASSERT(!options.common.sector_align || options.bps == 16); FLAC__ASSERT(!options.common.sector_align || options.sample_rate == 44100); FLAC__ASSERT(!options.common.sector_align || infilesize >= 0); - FLAC__ASSERT(!options.common.replay_gain || options.common.skip == 0); FLAC__ASSERT(!options.common.replay_gain || options.channels <= 2); FLAC__ASSERT(!options.common.replay_gain || grabbag__replaygain_is_valid_sample_frequency(options.sample_rate)); @@ -809,13 +822,23 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons ) return 1; + /* + * now that we know the sample rate, canonicalize the + * --skip string to a number of samples: + */ + flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, options.sample_rate); + FLAC__ASSERT(options.common.skip_specification.value.samples >= 0); + encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples; + FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0); + FLAC__ASSERT(!options.common.replay_gain || encoder_session.skip == 0); + /* get the file length */ if(infilesize < 0) { encoder_session.total_samples_to_encode = encoder_session.unencoded_size = 0; } else { if(options.common.sector_align) { - FLAC__ASSERT(options.common.skip == 0); + FLAC__ASSERT(encoder_session.skip == 0); encoder_session.total_samples_to_encode = (unsigned)infilesize / bytes_per_wide_sample + *options.common.align_reservoir_samples; align_remainder = (unsigned)(encoder_session.total_samples_to_encode % 588); if(options.common.is_last_file) @@ -824,7 +847,7 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons encoder_session.total_samples_to_encode -= align_remainder; /* will stop short and carry over to next file */ } else { - encoder_session.total_samples_to_encode = (unsigned)infilesize / bytes_per_wide_sample - options.common.skip; + encoder_session.total_samples_to_encode = (unsigned)infilesize / bytes_per_wide_sample - encoder_session.skip; } encoder_session.unencoded_size = encoder_session.total_samples_to_encode * bytes_per_wide_sample; @@ -833,8 +856,8 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons if(encoder_session.verbose && encoder_session.total_samples_to_encode <= 0) fprintf(stderr, "(No runtime statistics possible; please wait for encoding to finish...)\n"); - if(options.common.skip > 0) { - unsigned skip_bytes = bytes_per_wide_sample * (unsigned)options.common.skip; + if(encoder_session.skip > 0) { + unsigned skip_bytes = bytes_per_wide_sample * (unsigned)encoder_session.skip; if(skip_bytes > lookahead_length) { skip_bytes -= lookahead_length; lookahead_length = 0; @@ -997,6 +1020,7 @@ FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg, FLAC_ e->inbasefilename = grabbag__file_get_basename(infilename); e->outfilename = outfilename; + e->skip = 0; /* filled in later after the sample_rate is known */ e->unencoded_size = 0; e->total_samples_to_encode = 0; e->bytes_written = 0; diff --git a/src/flac/encode.h b/src/flac/encode.h index add8595a..b04fdb4a 100644 --- a/src/flac/encode.h +++ b/src/flac/encode.h @@ -20,6 +20,7 @@ #define flac__encode_h #include "FLAC/metadata.h" +#include "utils.h" #ifdef HAVE_CONFIG_H #include @@ -27,7 +28,7 @@ typedef struct { FLAC__bool verbose; - FLAC__uint64 skip; + utils__SkipUntilSpecification skip_specification; FLAC__bool verify; #ifdef FLAC__HAS_OGG FLAC__bool use_ogg; diff --git a/src/flac/main.c b/src/flac/main.c index 3106fd6e..44aae3f1 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -38,6 +38,7 @@ #include "analyze.h" #include "decode.h" #include "encode.h" +#include "utils.h" #include "vorbiscomment.h" #if 0 @@ -228,7 +229,7 @@ static struct { int padding; unsigned max_lpc_order; unsigned qlp_coeff_precision; - FLAC__uint64 skip; + const char *skip_specification; int format_is_big_endian; int format_is_unsigned_samples; int format_channels; @@ -333,7 +334,7 @@ int do_it() } else { if(option_values.test_only) { - if(option_values.skip > 0) + if(0 != option_values.skip_specification) return usage_error("ERROR: --skip is not allowed in test mode\n"); } } @@ -376,7 +377,7 @@ int do_it() if(option_values.sector_align) { if(option_values.mode_decode) return usage_error("ERROR: --sector-align only allowed for encoding\n"); - if(option_values.skip > 0) + if(0 != option_values.skip_specification) return usage_error("ERROR: --sector-align not allowed with --skip\n"); if(option_values.format_channels >= 0 && option_values.format_channels != 2) return usage_error("ERROR: --sector-align can only be done with stereo input\n"); @@ -534,7 +535,7 @@ FLAC__bool init_options() option_values.padding = 4096; option_values.max_lpc_order = 8; option_values.qlp_coeff_precision = 0; - option_values.skip = 0; + option_values.skip_specification = 0; option_values.format_is_big_endian = -1; option_values.format_is_unsigned_samples = -1; option_values.format_channels = -1; @@ -619,7 +620,7 @@ int parse_option(int short_option, const char *long_option, const char *option_a } else if(0 == strcmp(long_option, "skip")) { FLAC__ASSERT(0 != option_argument); - option_values.skip = (FLAC__uint64)atoi(option_argument); /* @@@ takes a pretty damn big file to overflow atoi() here, but it could happen */ + option_values.skip_specification = option_argument; } else if(0 == strcmp(long_option, "cuesheet")) { FLAC__ASSERT(0 != option_argument); @@ -1082,7 +1083,7 @@ void show_help() printf(" -o, --output-name=FILENAME Force the output file name\n"); printf(" --output-prefix=STRING Prepend STRING to output names\n"); printf(" --delete-input-file Deletes after a successful encode/decode\n"); - printf(" --skip=# Skip the first # samples of each input file\n"); + printf(" --skip={#|mm:ss.ss} Skip the given initial samples for each input\n"); printf("analysis options:\n"); printf(" --residual-text Include residual signal in text output\n"); printf(" --residual-gnuplot Generate gnuplot files of residual distribution\n"); @@ -1203,8 +1204,10 @@ void show_explain() printf(" successful encode or decode. If there was an\n"); printf(" error (including a verify error) the input file\n"); printf(" is left intact.\n"); - printf(" --skip=# Skip the first # samples of each input file; can\n"); - printf(" be used both for encoding and decoding\n"); + printf(" --skip={#|mm:ss.ss} Skip the first # samples of each input file; can\n"); + printf(" be used both for encoding and decoding. The\n"); + printf(" alternative form mm:ss.ss can be used to specify\n"); + printf(" minutes, seconds, and fractions of a second.\n"); printf("analysis options:\n"); printf(" --residual-text Include residual signal in text output. This\n"); printf(" will make the file very big, much larger than\n"); @@ -1436,8 +1439,10 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_ return usage_error("ERROR: --replay-gain cannot be used when encoding to stdout\n"); } + if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &common_options.skip_specification) || common_options.skip_specification.is_relative) + return usage_error("ERROR: invalid value for --skip\n"); + common_options.verbose = option_values.verbose; - common_options.skip = option_values.skip; common_options.verify = option_values.verify; #ifdef FLAC__HAS_OGG common_options.use_ogg = option_values.use_ogg; @@ -1540,6 +1545,9 @@ int decode_file(const char *infilename) } #endif + if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &common_options.skip_specification) || common_options.skip_specification.is_relative) + return usage_error("ERROR: invalid value for --skip\n"); + common_options.verbose = option_values.verbose; common_options.continue_through_decode_errors = option_values.continue_through_decode_errors; #ifdef FLAC__HAS_OGG @@ -1547,7 +1555,6 @@ int decode_file(const char *infilename) common_options.use_first_serial_number = !option_values.has_serial_number; common_options.serial_number = option_values.serial_number; #endif - common_options.skip = option_values.skip; if(!option_values.force_raw_format) { wav_decode_options_t options;