From d7fa6ab5b118b5be31647c81419dcb6f40d06931 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Wed, 27 Nov 2002 23:34:56 +0000 Subject: [PATCH] implement --cuesheet and --no-cued-seekpoints --- src/flac/encode.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++------- src/flac/encode.h | 2 ++ src/flac/main.c | 51 +++++++++++++++++++++++++------- 3 files changed, 121 insertions(+), 20 deletions(-) diff --git a/src/flac/encode.c b/src/flac/encode.c index 8fa48dd..02da8eb 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -125,7 +125,7 @@ static int EncoderSession_finish_ok(EncoderSession *e, int info_align_carry, int static int EncoderSession_finish_error(EncoderSession *e); static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate); static FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const buffer[], unsigned samples); -static FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, EncoderSession *e); +static FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e); static void format_input(FLAC__int32 *dest[], unsigned wide_samples, FLAC__bool is_big_endian, FLAC__bool is_unsigned_samples, unsigned channels, unsigned bps); #ifdef FLAC__HAS_OGG static FLAC__StreamEncoderWriteStatus ogg_stream_encoder_write_callback(const OggFLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data); @@ -133,6 +133,7 @@ static FLAC__StreamEncoderWriteStatus ogg_stream_encoder_write_callback(const Og static FLAC__StreamEncoderWriteStatus flac_stream_encoder_write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data); static void flac_stream_encoder_metadata_callback(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data); static void flac_file_encoder_progress_callback(const FLAC__FileEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); +static FLAC__bool parse_cuesheet_(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__uint64 lead_out_offset); static void print_stats(const EncoderSession *encoder_session); static void print_error_with_state(const EncoderSession *e, const char *message); static void print_verify_error(EncoderSession *e); @@ -1178,8 +1179,8 @@ int EncoderSession_finish_error(EncoderSession *e) FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate) { unsigned num_metadata; - FLAC__StreamMetadata padding; - FLAC__StreamMetadata *metadata[3]; + FLAC__StreamMetadata padding, *cuesheet = 0; + FLAC__StreamMetadata *metadata[4]; e->replay_gain = options.replay_gain; e->channels = channels; @@ -1206,17 +1207,24 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio if(channels != 2) options.do_mid_side = options.loose_mid_side = false; - if(!convert_to_seek_table_template(options.requested_seek_points, options.num_requested_seek_points, e)) { + if(!parse_cuesheet_(&cuesheet, options.cuesheet_filename, e->inbasefilename, e->total_samples_to_encode)) + return false; + + if(!convert_to_seek_table_template(options.requested_seek_points, options.num_requested_seek_points, options.cued_seekpoints? cuesheet : 0, e)) { fprintf(stderr, "%s: ERROR allocating memory for seek table\n", e->inbasefilename); + if(0 != cuesheet) + free(cuesheet); return false; } num_metadata = 0; - metadata[num_metadata++] = options.vorbis_comment; if(e->seek_table_template->data.seek_table.num_points > 0) { e->seek_table_template->is_last = false; /* the encoder will set this for us */ metadata[num_metadata++] = e->seek_table_template; } + if(0 != cuesheet) + metadata[num_metadata++] = cuesheet; + metadata[num_metadata++] = options.vorbis_comment; if(options.padding > 0) { padding.is_last = false; /* the encoder will set this for us */ padding.type = FLAC__METADATA_TYPE_PADDING; @@ -1258,6 +1266,8 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio if(OggFLAC__stream_encoder_init(e->encoder.ogg.stream) != FLAC__STREAM_ENCODER_OK) { print_error_with_state(e, "ERROR initializing encoder"); + if(0 != cuesheet) + free(cuesheet); return false; } } @@ -1292,6 +1302,8 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio if(FLAC__stream_encoder_init(e->encoder.flac.stream) != FLAC__STREAM_ENCODER_OK) { print_error_with_state(e, "ERROR initializing encoder"); + if(0 != cuesheet) + free(cuesheet); return false; } } @@ -1324,10 +1336,15 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio if(FLAC__file_encoder_init(e->encoder.flac.file) != FLAC__FILE_ENCODER_OK) { print_error_with_state(e, "ERROR initializing encoder"); + if(0 != cuesheet) + free(cuesheet); return false; } } + if(0 != cuesheet) + free(cuesheet); + return true; } @@ -1352,16 +1369,18 @@ FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const b } } -FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, EncoderSession *e) +FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e) { FLAC__bool only_placeholders; FLAC__bool has_real_points; - if(num_requested_seek_points == 0) + if(num_requested_seek_points == 0 && 0 == cuesheet) return true; - if(num_requested_seek_points < 0) + if(num_requested_seek_points < 0) { requested_seek_points = "100x;"; + num_requested_seek_points = 1; + } if(e->is_stdout) only_placeholders = true; @@ -1372,8 +1391,26 @@ FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int else only_placeholders = false; - if(!grabbag__seektable_convert_specification_to_template(requested_seek_points, only_placeholders, e->total_samples_to_encode, e->sample_rate, e->seek_table_template, &has_real_points)) - return false; + if(num_requested_seek_points > 0) { + if(!grabbag__seektable_convert_specification_to_template(requested_seek_points, only_placeholders, e->total_samples_to_encode, e->sample_rate, e->seek_table_template, &has_real_points)) + return false; + } + + if(0 != cuesheet) { + unsigned i, j; + const FLAC__StreamMetadata_CueSheet *cs = &cuesheet->data.cue_sheet; + for(i = 0; i < cs->num_tracks; i++) { + const FLAC__StreamMetadata_CueSheet_Track *tr = cs->tracks+i; + for(j = 0; j < tr->num_indices; j++) { + if(!FLAC__metadata_object_seektable_template_append_point(e->seek_table_template, tr->offset + tr->indices[j].offset)) + return false; + has_real_points = true; + } + } + if(has_real_points) + if(!FLAC__metadata_object_seektable_template_sort(e->seek_table_template, /*compact=*/true)) + return false; + } if(has_real_points) { if(e->is_stdout) @@ -1524,6 +1561,37 @@ void flac_file_encoder_progress_callback(const FLAC__FileEncoder *encoder, FLAC_ print_stats(encoder_session); } +FLAC__bool parse_cuesheet_(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__uint64 lead_out_offset) +{ + FILE *f; + unsigned last_line_read; + const char *error_message; + + if(0 == cuesheet_filename) + return true; + + if(lead_out_offset == 0) { + fprintf(stderr, "%s: ERROR cannot import cuesheet when the number of input samples to encode is unknown\n", inbasefilename); + return false; + } + + if(0 == (f = fopen(cuesheet_filename, "r"))) { + fprintf(stderr, "%s: ERROR opening cuesheet \"%s\" for reading\n", inbasefilename, cuesheet_filename); + return false; + } + + *cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, /*@@@@is_cdda=*/true, lead_out_offset); + + fclose(f); + + if(0 == *cuesheet) { + fprintf(stderr, "%s: ERROR parsing cuesheet \"%s\" on line %u: %s\n", inbasefilename, cuesheet_filename, last_line_read, error_message); + return false; + } + + return true; +} + void print_stats(const EncoderSession *encoder_session) { const FLAC__uint64 samples_written = min(encoder_session->total_samples_to_encode, encoder_session->samples_written); diff --git a/src/flac/encode.h b/src/flac/encode.h index a74dbb5..add8595 100644 --- a/src/flac/encode.h +++ b/src/flac/encode.h @@ -49,6 +49,8 @@ typedef struct { int padding; char *requested_seek_points; int num_requested_seek_points; + const char *cuesheet_filename; + FLAC__bool cued_seekpoints; /* options related to --replay-gain and --sector-align */ FLAC__bool is_first_file; diff --git a/src/flac/main.c b/src/flac/main.c index f89df08..3f9b512 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -103,6 +103,8 @@ static struct share__option long_options_[] = { /* * encoding options */ + { "cuesheet", 1, 0, 0 }, + { "no-cued-seekpoints", 0, 0, 0 }, { "tag", 1, 0, 'T' }, { "compression-level-0", 0, 0, '0' }, { "compression-level-1", 0, 0, '1' }, @@ -116,7 +118,7 @@ static struct share__option long_options_[] = { { "compression-level-9", 0, 0, '9' }, { "best", 0, 0, '8' }, { "fast", 0, 0, '0' }, - { "super-secret-impractical-compression-level", 0, 0, 0 }, + { "super-secret-totally-impractical-compression-level", 0, 0, 0 }, { "verify", 0, 0, 'V' }, { "force-raw-format", 0, 0, 0 }, { "lax", 0, 0, 0 }, @@ -236,8 +238,10 @@ static struct { int min_residual_partition_order; int max_residual_partition_order; int rice_parameter_search_dist; - char requested_seek_points[50000]; /* @@@ bad MAGIC NUMBER */ + char requested_seek_points[50000]; /* @@@ bad MAGIC NUMBER but buffer overflow is checked */ int num_requested_seek_points; /* -1 => no -S options were given, 0 => -S- was given */ + const char *cuesheet_filename; + FLAC__bool cued_seekpoints; unsigned num_files; char **filenames; @@ -409,6 +413,9 @@ int do_it() if(option_values.cmdline_forced_outfilename && option_values.output_prefix) { return usage_error("ERROR: --output-prefix conflicts with -o/--output-name\n"); } + if(!option_values.mode_decode && 0 != option_values.cuesheet_filename && option_values.num_files > 1) { + return usage_error("ERROR: --cuesheet cannot be used when encoding multiple files\n"); + } } if(option_values.verbose) { fprintf(stderr, "\n"); @@ -539,6 +546,8 @@ FLAC__bool init_options() option_values.rice_parameter_search_dist = -1; option_values.requested_seek_points[0] = '\0'; option_values.num_requested_seek_points = -1; + option_values.cuesheet_filename = 0; + option_values.cued_seekpoints = true; option_values.num_files = 0; option_values.filenames = 0; @@ -612,7 +621,15 @@ int parse_option(int short_option, const char *long_option, const char *option_a 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 */ } - else if(0 == strcmp(long_option, "super-secret-impractical-compression-level")) { + else if(0 == strcmp(long_option, "cuesheet")) { + FLAC__ASSERT(0 != option_argument); + option_values.cuesheet_filename = option_argument; + } + else if(0 == strcmp(long_option, "no-cued-seekpoints")) { + option_values.cued_seekpoints = false; + } + else if(0 == strcmp(long_option, "super-secret-totally-impractical-compression-level")) { + option_values.lax = true; option_values.do_exhaustive_model_search = true; option_values.do_escape_coding = true; option_values.do_mid_side = true; @@ -895,8 +912,13 @@ int parse_option(int short_option, const char *long_option, const char *option_a if(option_values.num_requested_seek_points < 0) option_values.num_requested_seek_points = 0; option_values.num_requested_seek_points++; - strcat(option_values.requested_seek_points, option_argument); - strcat(option_values.requested_seek_points, ";"); + if(strlen(option_values.requested_seek_points)+strlen(option_argument)+2 >= sizeof(option_values.requested_seek_points)) { + return usage_error("ERROR: too many seekpoints requested\n"); + } + else { + strcat(option_values.requested_seek_points, option_argument); + strcat(option_values.requested_seek_points, ";"); + } break; case 'P': FLAC__ASSERT(0 != option_argument); @@ -1069,9 +1091,10 @@ void show_help() printf(" --lax Allow encoder to generate non-Subset files\n"); printf(" --sector-align Align multiple files on sector boundaries\n"); printf(" --replay-gain Calculate ReplayGain & store in Vorbis comments\n"); + printf(" --cuesheet=FILENAME Import cuesheet and store in CUESHEET block\n"); + printf(" -T, --tag=FIELD=VALUE Add a Vorbis comment; may appear multiple times\n"); printf(" -S, --seekpoint={#|X|#x|#s} Add seek point(s)\n"); printf(" -P, --padding=# Write a PADDING block of length #\n"); - printf(" -T, --tag=FIELD=VALUE Add a Vorbis comment; may appear multiple times\n"); printf(" -0, --compression-level-0, --fast Synonymous with -l 0 -b 1152 -r 2,2\n"); printf(" -1, --compression-level-1 Synonymous with -l 0 -b 1152 -M -r 2,2\n"); printf(" -2, --compression-level-2 Synonymous with -l 0 -b 1152 -m -r 3\n"); @@ -1222,6 +1245,16 @@ void show_explain() printf(" one of 8, 11.025, 12, 16, 22.05, 24, 32, 44.1,\n"); printf(" or 48 kHz. NOTE: this option may also leave a\n"); printf(" few extra bytes in the PADDING block.\n"); + printf(" --cuesheet=FILENAME Import the given cuesheet file and store it in\n"); + printf(" a CUESHEET metadata block. This option may only\n"); + printf(" be used when encoding a single file. A\n"); + printf(" seekpoint will be added for each index point in\n"); + printf(" the cuesheet to the SEEKTABLE unless\n"); + printf(" --no-cued-seekpoints is specified.\n"); + printf(" -T, --tag=FIELD=VALUE Add a Vorbis comment. Make sure to quote the\n"); + printf(" comment if necessary. This option may appear\n"); + printf(" more than once to add several comments. NOTE:\n"); + printf(" all tags will be added to all encoded files.\n"); printf(" -S, --seekpoint={#|X|#x|#s} Include a point or points in a SEEKTABLE\n"); printf(" # : a specific sample number for a seek point\n"); printf(" X : a placeholder point (always goes at the end of the SEEKTABLE)\n"); @@ -1253,10 +1286,6 @@ void show_explain() printf(" 576, 1152, 2304, 4608, 256, 512, 1024, 2048,\n"); printf(" 4096, 8192, 16384, or 32768 (unless --lax is\n"); printf(" used)\n"); - printf(" -T, --tag=FIELD=VALUE Add a Vorbis comment. Make sure to quote the\n"); - printf(" comment if necessary. This option may appear\n"); - printf(" more than once to add several comments. NOTE:\n"); - printf(" all tags will be added to all encoded files.\n"); printf(" -0, --compression-level-0, --fast Synonymous with -l 0 -b 1152 -r 2,2\n"); printf(" -1, --compression-level-1 Synonymous with -l 0 -b 1152 -M -r 2,2\n"); printf(" -2, --compression-level-2 Synonymous with -l 0 -b 1152 -m -r 3\n"); @@ -1424,6 +1453,8 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_ common_options.padding = option_values.padding; common_options.requested_seek_points = option_values.requested_seek_points; common_options.num_requested_seek_points = option_values.num_requested_seek_points; + common_options.cuesheet_filename = option_values.cuesheet_filename; + common_options.cued_seekpoints = option_values.cued_seekpoints; common_options.is_first_file = is_first_file; common_options.is_last_file = is_last_file; common_options.align_reservoir = align_reservoir; -- 2.7.4