implement --cuesheet and --no-cued-seekpoints
authorJosh Coalson <jcoalson@users.sourceforce.net>
Wed, 27 Nov 2002 23:34:56 +0000 (23:34 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Wed, 27 Nov 2002 23:34:56 +0000 (23:34 +0000)
src/flac/encode.c
src/flac/encode.h
src/flac/main.c

index 8fa48dd..02da8eb 100644 (file)
@@ -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);
index a74dbb5..add8595 100644 (file)
@@ -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;
index f89df08..3f9b512 100644 (file)
@@ -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;