implement --cuesheet and --no-cued-seekpoints

This commit is contained in:
Josh Coalson 2002-11-27 23:34:56 +00:00
parent cef5703fc3
commit d7fa6ab5b1
3 changed files with 121 additions and 20 deletions

View 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);

View 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;

View 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;