From: Josh Coalson Date: Wed, 7 Jan 2009 07:03:17 +0000 (+0000) Subject: Allow MM:SS:FF and MM:SS.SS time formats in non-CD-DA cuesheets (SF#1947353, SF#21824... X-Git-Tag: 1.3.0pre1~152 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c9ed238c3db9b797d302485b362ef031655ad6d4;p=platform%2Fupstream%2Fflac.git Allow MM:SS:FF and MM:SS.SS time formats in non-CD-DA cuesheets (SF#1947353, SF#2182432: https://sourceforge.net/tracker2/?func=detail&aid=1947353&group_id=13478&atid=363478 https://sourceforge.net/tracker2/index.php?func=detail&aid=2182432&group_id=13478&atid=113478) --- diff --git a/doc/html/changelog.html b/doc/html/changelog.html index 216f8a1..c6de19d 100644 --- a/doc/html/changelog.html +++ b/doc/html/changelog.html @@ -63,6 +63,7 @@
  • @@ -83,6 +84,7 @@
  • Added support for encoding from and decoding to the RF64 format, and a new corresponding option --force-rf64-format. (SF #1762502). --keep-foreign-metadata is also supported.
  • Added support for encoding from and decoding to the Sony Wave64 format, and a new corresponding option --force-wave64-format. (SF #1769582). --keep-foreign-metadata is also supported.
  • Added new options --preserve-modtime and --no-preserve-modtime to specify whether or not output files should copy the timestamp and permissions from their input files. The default is --preserve-modtime as in previous versions. (SF #1805428).
  • +
  • Allow MM:SS:FF and MM:SS.SS time formats in non-CD-DA cuesheets. (SF #1947353, SF #2182432)
  • The --sector-align option of flac has been deprecated and may not exist in future versions. shntool provides similar functionality. (SF #1805946)
  • Improved error message when user attempts to decode a non-FLAC file (SF #2222789).
  • Fix bug where flac was disallowing use of --replay-gain when encoding from stdin (SF #1840124).
  • @@ -92,7 +94,7 @@
  • metaflac:
  • diff --git a/include/share/grabbag/cuesheet.h b/include/share/grabbag/cuesheet.h index 0b5b9c5..90347d4 100644 --- a/include/share/grabbag/cuesheet.h +++ b/include/share/grabbag/cuesheet.h @@ -31,7 +31,7 @@ extern "C" { unsigned grabbag__cuesheet_msf_to_frame(unsigned minutes, unsigned seconds, unsigned frames); void grabbag__cuesheet_frame_to_msf(unsigned frame, unsigned *minutes, unsigned *seconds, unsigned *frames); -FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset); +FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset); void grabbag__cuesheet_emit(FILE *file, const FLAC__StreamMetadata *cuesheet, const char *file_reference); diff --git a/src/flac/encode.c b/src/flac/encode.c index d529d2f..52aa7d2 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -162,7 +162,7 @@ static FLAC__bool flac_decoder_eof_callback(const FLAC__StreamDecoder *decoder, static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); static void flac_decoder_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); static void flac_decoder_error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); -static FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors); +static FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors); static void print_stats(const EncoderSession *encoder_session); static void print_error_with_init_status(const EncoderSession *e, const char *message, FLAC__StreamEncoderInitStatus init_status); static void print_error_with_state(const EncoderSession *e, const char *message); @@ -1770,7 +1770,7 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio } } - if(!parse_cuesheet(&static_metadata.cuesheet, options.cuesheet_filename, e->inbasefilename, is_cdda, e->total_samples_to_encode, e->treat_warnings_as_errors)) + if(!parse_cuesheet(&static_metadata.cuesheet, options.cuesheet_filename, e->inbasefilename, sample_rate, is_cdda, e->total_samples_to_encode, e->treat_warnings_as_errors)) return false; if(!convert_to_seek_table_template(options.requested_seek_points, options.num_requested_seek_points, options.cued_seekpoints? static_metadata.cuesheet : 0, e)) { @@ -2560,7 +2560,7 @@ void flac_decoder_error_callback(const FLAC__StreamDecoder *decoder, FLAC__Strea data->fatal_error = true; } -FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors) +FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors) { FILE *f; unsigned last_line_read; @@ -2579,7 +2579,7 @@ FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_ return false; } - *cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, is_cdda, lead_out_offset); + *cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset); fclose(f); diff --git a/src/libFLAC/lpc.c b/src/libFLAC/lpc.c index d594f1d..7fbae9f 100644 --- a/src/libFLAC/lpc.c +++ b/src/libFLAC/lpc.c @@ -42,6 +42,9 @@ #include #endif +/* OPT: #undef'ing this may improve the speed on some architectures */ +#define FLAC__LPC_UNROLLED_FILTER_LOOPS + #ifndef FLAC__INTEGER_ONLY_LIBRARY #ifndef M_LN2 @@ -49,9 +52,6 @@ #define M_LN2 0.69314718055994530942 #endif -/* OPT: #undef'ing this may improve the speed on some architectures */ -#define FLAC__LPC_UNROLLED_FILTER_LOOPS - void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) { diff --git a/src/metaflac/operations_shorthand_cuesheet.c b/src/metaflac/operations_shorthand_cuesheet.c index 7c92363..f7ff444 100644 --- a/src/metaflac/operations_shorthand_cuesheet.c +++ b/src/metaflac/operations_shorthand_cuesheet.c @@ -29,7 +29,7 @@ #include "share/grabbag.h" #include "operations_shorthand.h" -static FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link); +static FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, unsigned sample_rate, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link); static FLAC__bool export_cs_to(const char *filename, const FLAC__StreamMetadata *cuesheet, const char *cs_filename); FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write) @@ -39,6 +39,7 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new(); FLAC__uint64 lead_out_offset = 0; FLAC__bool is_cdda = false; + unsigned sample_rate = 0; if(0 == iterator) die("out of memory allocating iterator"); @@ -54,7 +55,8 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata FLAC__metadata_iterator_delete(iterator); return false; } - is_cdda = (block->data.stream_info.channels == 1 || block->data.stream_info.channels == 2) && (block->data.stream_info.bits_per_sample == 16) && (block->data.stream_info.sample_rate == 44100); + sample_rate = block->data.stream_info.sample_rate; + is_cdda = (block->data.stream_info.channels == 1 || block->data.stream_info.channels == 2) && (block->data.stream_info.bits_per_sample == 16) && (sample_rate == 44100); } else if(block->type == FLAC__METADATA_TYPE_CUESHEET) cuesheet = block; @@ -73,7 +75,7 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata ok = false; } else { - ok = import_cs_from(filename, &cuesheet, operation->argument.import_cuesheet_from.filename, needs_write, lead_out_offset, is_cdda, operation->argument.import_cuesheet_from.add_seekpoint_link); + ok = import_cs_from(filename, &cuesheet, operation->argument.import_cuesheet_from.filename, needs_write, lead_out_offset, sample_rate, is_cdda, operation->argument.import_cuesheet_from.add_seekpoint_link); if(ok) { /* append CUESHEET block */ while(FLAC__metadata_iterator_next(iterator)) @@ -108,7 +110,7 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata * local routines */ -FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link) +FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, unsigned sample_rate, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link) { FILE *f; const char *error_message; @@ -129,7 +131,7 @@ FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, return false; } - *cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, is_cdda, lead_out_offset); + *cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset); if(f != stdin) fclose(f); diff --git a/src/share/grabbag/cuesheet.c b/src/share/grabbag/cuesheet.c index 3554b36..bd6a8ab 100644 --- a/src/share/grabbag/cuesheet.c +++ b/src/share/grabbag/cuesheet.c @@ -76,10 +76,11 @@ static FLAC__int64 local__parse_int64_(const char *s) return ret; } -/* accept '[0-9]+:[0-9][0-9]?:[0-9][0-9]?', but max second of 59 and max frame of 74, e.g. 0:0:0, 123:45:67 +/* accept minute:second:frame syntax of '[0-9]+:[0-9][0-9]?:[0-9][0-9]?', but max second of 59 and max frame of 74, e.g. 0:0:0, 123:45:67 * return sample number or <0 for error + * WATCHOUT: if sample rate is not evenly divisible by 75, the resulting sample number will be approximate */ -static FLAC__int64 local__parse_msf_(const char *s) +static FLAC__int64 local__parse_msf_(const char *s, unsigned sample_rate) { FLAC__int64 ret, field; char c; @@ -96,7 +97,7 @@ static FLAC__int64 local__parse_msf_(const char *s) return -1; } - ret = field * 60 * 44100; + ret = field * 60 * sample_rate; c = *s++; if(c >= '0' && c <= '9') @@ -117,7 +118,7 @@ static FLAC__int64 local__parse_msf_(const char *s) if(field >= 60) return -1; - ret += field * 44100; + ret += field * sample_rate; c = *s++; if(c >= '0' && c <= '9') @@ -139,7 +140,45 @@ static FLAC__int64 local__parse_msf_(const char *s) if(field >= 75) return -1; - ret += field * (44100 / 75); + ret += field * (sample_rate / 75); + + return ret; +} + +/* accept minute:second syntax of '[0-9]+:[0-9][0-9]?{,.[0-9]+}', but second < 60, e.g. 0:0.0, 3:5, 15:31.731 + * return sample number or <0 for error + * WATCHOUT: depending on the sample rate, the resulting sample number may be approximate with fractional seconds + */ +static FLAC__int64 local__parse_ms_(const char *s, unsigned sample_rate) +{ + FLAC__int64 ret, field; + double x; + char c, *end; + + c = *s++; + if(c >= '0' && c <= '9') + field = (c - '0'); + else + return -1; + while(':' != (c = *s++)) { + if(c >= '0' && c <= '9') + field = field * 10 + (c - '0'); + else + return -1; + } + + ret = field * 60 * sample_rate; + + s++; /* skip the ':' */ + if(strspn(s, "0123456789.") != strlen(s)) + return -1; + x = strtod(s, &end); + if(*end || end == s) + return -1; + if(x < 0.0 || x >= 60.0) + return -1; + + ret += (FLAC__int64)(x * sample_rate); return ret; } @@ -198,7 +237,7 @@ static char *local__get_field_(char **s, FLAC__bool allow_quotes) return p; } -static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__StreamMetadata *cuesheet, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) +static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__StreamMetadata *cuesheet, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) { #if defined _MSC_VER || defined __MINGW32__ || defined __EMX__ #define FLAC__STRCASECMP stricmp @@ -212,6 +251,13 @@ static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, FLAC__bool disc_has_catalog = false, track_has_flags = false, track_has_isrc = false, has_forced_leadout = false; FLAC__StreamMetadata_CueSheet *cs = &cuesheet->data.cue_sheet; + FLAC__ASSERT(!is_cdda || sample_rate == 44100); + /* double protection */ + if(is_cdda && sample_rate != 44100) { + *error_message = "CD-DA cuesheet only allowed with 44.1kHz sample rate"; + return false; + } + cs->lead_in = is_cdda? 2 * 44100 /* The default lead-in size for CD-DA */ : 0; cs->is_cd = is_cdda; @@ -302,18 +348,28 @@ static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, *error_message = "INDEX is missing an offset after the index number"; return false; } - xx = local__parse_msf_(field); + /* first parse as minute:second:frame format */ + xx = local__parse_msf_(field, sample_rate); if(xx < 0) { + /* CD-DA must use only MM:SS:FF format */ if(is_cdda) { *error_message = "illegal INDEX offset (not of the form MM:SS:FF)"; return false; } - xx = local__parse_int64_(field); + /* as an extension for non-CD-DA we allow MM:SS.SS or raw sample number */ + xx = local__parse_ms_(field, sample_rate); if(xx < 0) { - *error_message = "illegal INDEX offset"; - return false; + xx = local__parse_int64_(field); + if(xx < 0) { + *error_message = "illegal INDEX offset"; + return false; + } } } + else if(sample_rate % 75) { + *error_message = "illegal INDEX offset (MM:SS:FF form not allowed if sample rate is not a multiple of 75)"; + return false; + } if(is_cdda && cs->num_tracks == 1 && cs->tracks[0].num_indices == 0 && xx != 0) { *error_message = "first INDEX of first TRACK must have an offset of 00:00:00"; return false; @@ -533,7 +589,7 @@ static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, #undef FLAC__STRCASECMP } -FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) +FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) { FLAC__StreamMetadata *cuesheet; @@ -549,7 +605,7 @@ FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_mes return 0; } - if(!local__cuesheet_parse_(file, error_message, last_line_read, cuesheet, is_cdda, lead_out_offset)) { + if(!local__cuesheet_parse_(file, error_message, last_line_read, cuesheet, sample_rate, is_cdda, lead_out_offset)) { FLAC__metadata_object_delete(cuesheet); return 0; } diff --git a/src/test_grabbag/cuesheet/main.c b/src/test_grabbag/cuesheet/main.c index 2993558..355c9f3 100644 --- a/src/test_grabbag/cuesheet/main.c +++ b/src/test_grabbag/cuesheet/main.c @@ -28,7 +28,7 @@ #include "FLAC/metadata.h" #include "share/grabbag.h" -static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) +static int do_cuesheet(const char *infilename, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) { FILE *fin, *fout; const char *error_message; @@ -48,7 +48,7 @@ static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64 fprintf(stderr, "can't open file %s for reading: %s\n", infilename, strerror(errno)); return 255; } - if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, is_cdda, lead_out_offset))) { + if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset))) { if(fin != stdin) fclose(fin); } @@ -80,7 +80,7 @@ static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64 fprintf(stderr, "can't open file %s for reading: %s\n", tmpfilename, strerror(errno)); return 255; } - if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, is_cdda, lead_out_offset))) { + if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset))) { if(fin != stdin) fclose(fin); } @@ -111,28 +111,32 @@ static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64 int main(int argc, char *argv[]) { FLAC__uint64 lead_out_offset; + unsigned sample_rate; FLAC__bool is_cdda = false; - const char *usage = "usage: test_cuesheet cuesheet_file lead_out_offset [ cdda ]\n"; + const char *usage = "usage: test_cuesheet cuesheet_file lead_out_offset [ [ sample_rate ] cdda ]\n"; if(argc > 1 && 0 == strcmp(argv[1], "-h")) { printf(usage); return 0; } - if(argc < 3 || argc > 4) { + if(argc < 3 || argc > 5) { fprintf(stderr, usage); return 255; } lead_out_offset = (FLAC__uint64)strtoul(argv[2], 0, 10); - if(argc == 4) { - if(0 == strcmp(argv[3], "cdda")) - is_cdda = true; - else { - fprintf(stderr, usage); - return 255; + if(argc >= 4) { + sample_rate = (unsigned)atoi(argv[3]); + if(argc >= 5) { + if(0 == strcmp(argv[4], "cdda")) + is_cdda = true; + else { + fprintf(stderr, usage); + return 255; + } } } - return do_cuesheet(argv[1], is_cdda, lead_out_offset); + return do_cuesheet(argv[1], sample_rate, is_cdda, lead_out_offset); } diff --git a/test/test_grabbag.sh b/test/test_grabbag.sh index 550ac85..4191412 100755 --- a/test/test_grabbag.sh +++ b/test/test_grabbag.sh @@ -111,7 +111,7 @@ rm -f $log # for cuesheet in $bad_cuesheets ; do echo "NEGATIVE $cuesheet" >> $log 2>&1 - run_test_cuesheet $cuesheet $good_leadout cdda >> $log 2>&1 + run_test_cuesheet $cuesheet $good_leadout 44100 cdda >> $log 2>&1 exit_code=$? if [ "$exit_code" = 255 ] ; then die "Error: test script is broken" @@ -126,7 +126,7 @@ done # for cuesheet in $good_cuesheets ; do echo "POSITIVE $cuesheet" >> $log 2>&1 - run_test_cuesheet $cuesheet $good_leadout cdda >> $log 2>&1 + run_test_cuesheet $cuesheet $good_leadout 44100 cdda >> $log 2>&1 exit_code=$? if [ "$exit_code" = 255 ] ; then die "Error: test script is broken"