diff --git a/src/flac/main.c b/src/flac/main.c index e6e18b56..8991244c 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -33,443 +33,355 @@ #include "encode.h" #include "file.h" +#if 0 +/*[JEC] was:#if HAVE_GETOPT_LONG*/ +/*[JEC] see flac/include/share/getopt.h as to why the change */ +# include +#else +# include "share/getopt.h" +#endif + typedef enum { RAW, WAV, AIF } FileFormat; -static int short_usage(const char *message, ...); -static int long_usage(const char *message, ...); +static int do_it(); + +static void init_options(); +static int parse_options(int argc, char *argv[]); +static int parse_option(int short_option, const char *long_option, const char *option_argument); +static void free_options(); + +static int usage_error(const char *message, ...); +static void short_usage(); +static void long_usage(); static void format_mistake(const char *infilename, const char *wrong, const char *right); static int encode_file(const char *infilename, const char *forced_outfilename, FLAC__bool is_last_file); static int decode_file(const char *infilename, const char *forced_outfilename); -FLAC__bool verify = false, verbose = true, continue_through_decode_errors = false, lax = false, test_only = false, analyze = false, use_ogg = false; -FLAC__bool do_mid_side = true, loose_mid_side = false, do_exhaustive_model_search = false, do_escape_coding = false, do_qlp_coeff_prec_search = false; -FLAC__bool force_to_stdout = false, force_raw_format = false, delete_input = false, sector_align = false; -const char *cmdline_forced_outfilename = 0, *output_prefix = 0; -analysis_options aopts = { false, false }; -int padding = -1; -unsigned max_lpc_order = 8; -unsigned qlp_coeff_precision = 0; -FLAC__uint64 skip = 0; -int format_is_big_endian = -1, format_is_unsigned_samples = false; -int format_channels = -1, format_bps = -1, format_sample_rate = -1; -int blocksize = -1, min_residual_partition_order = -1, max_residual_partition_order = -1, rice_parameter_search_dist = -1; -char requested_seek_points[50000]; /* @@@ bad MAGIC NUMBER */ -int num_requested_seek_points = -1; /* -1 => no -S options were given, 0 => -S- was given */ -FLAC__int32 align_reservoir_0[588], align_reservoir_1[588]; /* for carrying over samples from --sector-align */ -FLAC__int32 *align_reservoir[2] = { align_reservoir_0, align_reservoir_1 }; -unsigned align_reservoir_samples = 0; /* 0 .. 587 */ +static void die(const char *message); +static char *local_strdup(const char *source); + + +/* + * FLAC__share__getopt format struct; note that for long options with no + * short option equivalent we just set the 'val' field to 0. + */ +static struct FLAC__share__option long_options_[] = { + /* + * general options + */ + { "--help", 0, 0, 'H' }, + { "--decode", 0, 0, 'd' }, + { "--analyze", 0, 0, 'a' }, + { "--test", 0, 0, 't' }, + { "--stdout", 0, 0, 'c' }, + { "--silent", 0, 0, 's' }, + { "--delete-input-file", 0, 0, 0 }, + { "--output-prefix", 1, 0, 0 }, + { "--output-name", 1, 0, 'o' }, + { "--skip", 1, 0, 0 }, + + /* + * decoding options + */ + { "--decode-through-errors", 0, 0, 'F' }, + + /* + * encoding options + */ + { "--compression-level-0", 0, 0, '0' }, + { "--compression-level-1", 0, 0, '1' }, + { "--compression-level-2", 0, 0, '2' }, + { "--compression-level-3", 0, 0, '3' }, + { "--compression-level-4", 0, 0, '4' }, + { "--compression-level-5", 0, 0, '5' }, + { "--compression-level-6", 0, 0, '6' }, + { "--compression-level-7", 0, 0, '7' }, + { "--compression-level-8", 0, 0, '8' }, + { "--compression-level-9", 0, 0, '9' }, + { "--best", 0, 0, '8' }, + { "--fast", 0, 0, '0' }, + { "--super-secret-impractical-compression-level", 0, 0, 0 }, + { "--verify", 0, 0, 'V' }, + { "--force-raw-input", 0, 0, 0 }, + { "--lax", 0, 0, 0 }, + { "--sector-align", 0, 0, 0 }, + { "--seekpoint", 1, 0, 'S' }, + { "--padding", 1, 0, 'P' }, +#ifdef FLAC__HAS_OGG + { "--ogg", 0, 0, 0 }, +#endif + { "--blocksize", 1, 0, 'b' }, + { "--exhaustive-model-search", 0, 0, 'e' }, +#if 0 + /* @@@ deprecated: */ + { "--escape-coding", 0, 0, 'E' }, +#endif + { "--max-lpc-order", 1, 0, 'l' }, + { "--mid-side", 0, 0, 'm' }, + { "--adaptive-mid-side", 0, 0, 'M' }, + { "--qlp-coeff-precision-search", 0, 0, 'p' }, + { "--qlp-coeff-precision", 1, 0, 'q' }, + { "--rice-partition-order", 1, 0, 'r' }, +#if 0 + /* @@@ deprecated: */ + { "--rice-parameter-search-distance", 1, 0, 'R' }, +#endif + { "--endian", 1, 0, 0 }, + { "--channels", 1, 0, 0 }, + { "--bps", 1, 0, 0 }, + { "--sample-rate", 1, 0, 0 }, + { "--sign", 1, 0, 0 }, + + /* + * analysis options + */ + { "--residual-gnu-plot", 0, 0, 0 }, + { "--residual-text", 0, 0, 0 }, + + /* + * negatives + */ + { "--no-decode-through-errors", 0, 0, 0 }, + { "--no-silent", 0, 0, 0 }, + { "--no-seektable", 0, 0, 0 }, + { "--no-delete-input-file", 0, 0, 0 }, + { "--no-sector-align", 0, 0, 0 }, + { "--no-lax", 0, 0, 0 }, +#ifdef FLAC__HAS_OGG + { "--no-ogg", 0, 0, 0 }, +#endif + { "--no-exhaustive-model-search", 0, 0, 0 }, +#if 0 + /* @@@ deprecated: */ + { "--no-escape-coding", 0, 0, 0 }, +#endif + { "--no-mid-side", 0, 0, 0 }, + { "--no-adaptive-mid-side", 0, 0, 0 }, + { "--no-qlp-coeff-prec-search", 0, 0, 0 }, + { "--no-padding", 0, 0, 0 }, + { "--no-verify", 0, 0, 0 }, + { "--no-residual-gnuplot", 0, 0, 0 }, + { "--no-residual-text", 0, 0, 0 }, + + {0, 0, 0, 0} +}; + + +/* + * global to hold command-line option values + */ + +static struct { + FLAC__bool show_long_help; + FLAC__bool mode_decode; + FLAC__bool verify; + FLAC__bool verbose; + FLAC__bool continue_through_decode_errors; + FLAC__bool lax; + FLAC__bool test_only; + FLAC__bool analyze; + FLAC__bool use_ogg; + FLAC__bool do_mid_side; + FLAC__bool loose_mid_side; + FLAC__bool do_exhaustive_model_search; + FLAC__bool do_escape_coding; + FLAC__bool do_qlp_coeff_prec_search; + FLAC__bool force_to_stdout; + FLAC__bool force_raw_format; + FLAC__bool delete_input; + FLAC__bool sector_align; + const char *cmdline_forced_outfilename; + const char *output_prefix; + analysis_options aopts; + int padding; + unsigned max_lpc_order; + unsigned qlp_coeff_precision; + FLAC__uint64 skip; + int format_is_big_endian; + int format_is_unsigned_samples; + int format_channels; + int format_bps; + int format_sample_rate; + int blocksize; + int min_residual_partition_order; + int max_residual_partition_order; + int rice_parameter_search_dist; + char requested_seek_points[50000]; /* @@@ bad MAGIC NUMBER */ + int num_requested_seek_points; /* -1 => no -S options were given, 0 => -S- was given */ + + unsigned num_files; + char **filenames; +} option_values; + + +/* + * miscellaneous globals + */ + +static FLAC__int32 align_reservoir_0[588], align_reservoir_1[588]; /* for carrying over samples from --sector-align */ +static FLAC__int32 *align_reservoir[2] = { align_reservoir_0, align_reservoir_1 }; +static unsigned align_reservoir_samples = 0; /* 0 .. 587 */ static const char *flac_suffix = ".flac", *ogg_suffix = ".ogg"; + int main(int argc, char *argv[]) { - int i, retval = 0; - FLAC__bool mode_decode = false; + int retval = 0; - if(argc <= 1) - return short_usage(0); + init_options(); - /* get the options */ - for(i = 1; i < argc; i++) { - if(argv[i][0] != '-' || argv[i][1] == 0) - break; - if(0 == strcmp(argv[i], "-H") || 0 == strcmp(argv[i], "--help")) - return long_usage(0); - else if(0 == strcmp(argv[i], "-d")) - mode_decode = true; - else if(0 == strcmp(argv[i], "-a")) { - mode_decode = true; - analyze = true; - } - else if(0 == strcmp(argv[i], "-t")) { - mode_decode = true; - test_only = true; - } - else if(0 == strcmp(argv[i], "-c")) - force_to_stdout = true; - else if(0 == strcmp(argv[i], "-F")) - continue_through_decode_errors = true; - else if(0 == strcmp(argv[i], "-F-")) - continue_through_decode_errors = false; - else if(0 == strcmp(argv[i], "-s")) - verbose = false; - else if(0 == strcmp(argv[i], "-s-")) - verbose = true; - else if(0 == strcmp(argv[i], "-S")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -S\n"); - if(num_requested_seek_points < 0) - num_requested_seek_points = 0; - num_requested_seek_points++; - strcat(requested_seek_points, argv[i]); - strcat(requested_seek_points, "<"); - } - else if(0 == strcmp(argv[i], "-S-")) { - num_requested_seek_points = 0; - requested_seek_points[0] = '\0'; - } - else if(0 == strcmp(argv[i], "--delete-input-file")) - delete_input = true; - else if(0 == strcmp(argv[i], "--delete-input-file-")) - delete_input = false; - else if(0 == strcmp(argv[i], "--output-prefix")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with --output-prefix\n"); - output_prefix = argv[i]; - } - else if(0 == strcmp(argv[i], "--sector-align")) - sector_align = true; - else if(0 == strcmp(argv[i], "--sector-align-")) - sector_align = false; - else if(0 == strcmp(argv[i], "--skip")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with --skip\n"); - skip = (FLAC__uint64)atoi(argv[i]); /* @@@ takes a pretty damn big file to overflow atoi() here, but it could happen */ - } - else if(0 == strcmp(argv[i], "--lax")) - lax = true; - else if(0 == strcmp(argv[i], "--lax-")) - lax = false; -#ifdef FLAC__HAS_OGG - else if(0 == strcmp(argv[i], "--ogg")) - use_ogg = true; - else if(0 == strcmp(argv[i], "--ogg-")) - use_ogg = false; -#endif - else if(0 == strcmp(argv[i], "-b")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -b\n"); - blocksize = atoi(argv[i]); - } - else if(0 == strcmp(argv[i], "-e")) - do_exhaustive_model_search = true; - else if(0 == strcmp(argv[i], "-e-")) - do_exhaustive_model_search = false; - else if(0 == strcmp(argv[i], "-E")) - do_escape_coding = true; - else if(0 == strcmp(argv[i], "-E-")) - do_escape_coding = false; - else if(0 == strcmp(argv[i], "-l")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -l\n"); - max_lpc_order = atoi(argv[i]); - } - else if(0 == strcmp(argv[i], "-m")) { - do_mid_side = true; - loose_mid_side = false; - } - else if(0 == strcmp(argv[i], "-m-")) - do_mid_side = loose_mid_side = false; - else if(0 == strcmp(argv[i], "-M")) - loose_mid_side = do_mid_side = true; - else if(0 == strcmp(argv[i], "-M-")) - loose_mid_side = do_mid_side = false; - else if(0 == strcmp(argv[i], "-o")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -o\n"); - cmdline_forced_outfilename = argv[i]; - } - else if(0 == strcmp(argv[i], "-p")) - do_qlp_coeff_prec_search = true; - else if(0 == strcmp(argv[i], "-p-")) - do_qlp_coeff_prec_search = false; - else if(0 == strcmp(argv[i], "-P")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -P\n"); - padding = atoi(argv[i]); - if(padding < 0) - return long_usage("ERROR: argument to -P must be >= 0\n"); - } - else if(0 == strcmp(argv[i], "-P-")) - padding = -1; - else if(0 == strcmp(argv[i], "-q")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -q\n"); - qlp_coeff_precision = atoi(argv[i]); - } - else if(0 == strcmp(argv[i], "-r")) { - char *p; - if(++i >= argc) - return long_usage("ERROR: must specify a value with -r\n"); - p = strchr(argv[i], ','); - if(0 == p) { - min_residual_partition_order = 0; - max_residual_partition_order = atoi(argv[i]); - } - else { - min_residual_partition_order = atoi(argv[i]); - max_residual_partition_order = atoi(++p); - } - } - else if(0 == strcmp(argv[i], "--old-unworking-do-not-use-R")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -R\n"); - rice_parameter_search_dist = atoi(argv[i]); - } - else if(0 == strcmp(argv[i], "-V")) - verify = true; - else if(0 == strcmp(argv[i], "-V-")) - verify = false; - else if(0 == strcmp(argv[i], "-fb")) - format_is_big_endian = true; - else if(0 == strcmp(argv[i], "-fl")) - format_is_big_endian = false; - else if(0 == strcmp(argv[i], "-fc")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -fc\n"); - format_channels = atoi(argv[i]); - } - else if(0 == strcmp(argv[i], "-fp")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -fp\n"); - format_bps = atoi(argv[i]); - } - else if(0 == strcmp(argv[i], "-fs")) { - if(++i >= argc) - return long_usage("ERROR: must specify a value with -fs\n"); - format_sample_rate = atoi(argv[i]); - } - else if(0 == strcmp(argv[i], "-fu")) - format_is_unsigned_samples = true; - else if(0 == strcmp(argv[i], "-fr")) - force_raw_format = true; - else if(0 == strcmp(argv[i], "--a-rgp")) - aopts.do_residual_gnuplot = true; - else if(0 == strcmp(argv[i], "--a-rgp-")) - aopts.do_residual_gnuplot = false; - else if(0 == strcmp(argv[i], "--a-rtext")) - aopts.do_residual_text = true; - else if(0 == strcmp(argv[i], "--a-rtext-")) - aopts.do_residual_text = false; - else if(0 == strcmp(argv[i], "-0") || 0 == strcmp(argv[i], "--fast")) { - do_exhaustive_model_search = false; - do_escape_coding = false; - do_mid_side = false; - loose_mid_side = false; - qlp_coeff_precision = 0; - min_residual_partition_order = max_residual_partition_order = 2; - rice_parameter_search_dist = 0; - max_lpc_order = 0; - } - else if(0 == strcmp(argv[i], "-1")) { - do_exhaustive_model_search = false; - do_escape_coding = false; - do_mid_side = true; - loose_mid_side = true; - qlp_coeff_precision = 0; - min_residual_partition_order = max_residual_partition_order = 2; - rice_parameter_search_dist = 0; - max_lpc_order = 0; - } - else if(0 == strcmp(argv[i], "-2")) { - do_exhaustive_model_search = false; - do_escape_coding = false; - do_mid_side = true; - loose_mid_side = false; - qlp_coeff_precision = 0; - min_residual_partition_order = 0; - max_residual_partition_order = 3; - rice_parameter_search_dist = 0; - max_lpc_order = 0; - } - else if(0 == strcmp(argv[i], "-3")) { - do_exhaustive_model_search = false; - do_escape_coding = false; - do_mid_side = false; - loose_mid_side = false; - qlp_coeff_precision = 0; - min_residual_partition_order = max_residual_partition_order = 3; - rice_parameter_search_dist = 0; - max_lpc_order = 6; - } - else if(0 == strcmp(argv[i], "-4")) { - do_exhaustive_model_search = false; - do_escape_coding = false; - do_mid_side = true; - loose_mid_side = true; - qlp_coeff_precision = 0; - min_residual_partition_order = max_residual_partition_order = 3; - rice_parameter_search_dist = 0; - max_lpc_order = 8; - } - else if(0 == strcmp(argv[i], "-5")) { - do_exhaustive_model_search = false; - do_escape_coding = false; - do_mid_side = true; - loose_mid_side = false; - qlp_coeff_precision = 0; - min_residual_partition_order = max_residual_partition_order = 3; - rice_parameter_search_dist = 0; - max_lpc_order = 8; - } - else if(0 == strcmp(argv[i], "-6")) { - do_exhaustive_model_search = false; - do_escape_coding = false; - do_mid_side = true; - loose_mid_side = false; - qlp_coeff_precision = 0; - min_residual_partition_order = 0; - max_residual_partition_order = 4; - rice_parameter_search_dist = 0; - max_lpc_order = 8; - } - else if(0 == strcmp(argv[i], "-7")) { - do_exhaustive_model_search = true; - do_escape_coding = false; - do_mid_side = true; - loose_mid_side = false; - qlp_coeff_precision = 0; - min_residual_partition_order = 0; - max_residual_partition_order = 6; - rice_parameter_search_dist = 0; - max_lpc_order = 8; - } - else if(0 == strcmp(argv[i], "-8") || 0 == strcmp(argv[i], "--best")) { - do_exhaustive_model_search = true; - do_escape_coding = false; - do_mid_side = true; - loose_mid_side = false; - qlp_coeff_precision = 0; - min_residual_partition_order = 0; - max_residual_partition_order = 6; - rice_parameter_search_dist = 0; - max_lpc_order = 12; - } - else if(0 == strcmp(argv[i], "--super-secret-impractical-compression-level")) { - do_exhaustive_model_search = true; - do_escape_coding = true; - do_mid_side = true; - loose_mid_side = false; - do_qlp_coeff_prec_search = true; - min_residual_partition_order = 0; - max_residual_partition_order = 16; - rice_parameter_search_dist = 0; - max_lpc_order = 32; - } - else if(isdigit((int)(argv[i][1]))) { - return long_usage("ERROR: compression level '%s' is reserved\n", argv[i]); - } - else { - return long_usage("ERROR: invalid option '%s'\n", argv[i]); - } - } + if((retval = parse_options(argc, argv)) == 0) + retval = do_it(); - /* tweak options; validate the values */ - if(!mode_decode) { - if(blocksize < 0) { - if(max_lpc_order == 0) - blocksize = 1152; - else - blocksize = 4608; - } - if(max_residual_partition_order < 0) { - if(blocksize <= 1152) - max_residual_partition_order = 2; - else if(blocksize <= 2304) - max_residual_partition_order = 3; - else if(blocksize <= 4608) - max_residual_partition_order = 3; - else - max_residual_partition_order = 4; - min_residual_partition_order = max_residual_partition_order; - } - if(rice_parameter_search_dist < 0) { - rice_parameter_search_dist = 0; - } + free_options(); + + return retval; +} + +int do_it() +{ + int retval = 0; + + if(option_values.show_long_help) { + long_usage(); + return 0; } else { - if(test_only) { - if(skip > 0) - return long_usage("ERROR: --skip is not allowed in test mode\n"); + if(option_values.num_files == 0) { + short_usage(); + return 0; } - } - FLAC__ASSERT(blocksize >= 0 || mode_decode); + /* + * tweak options; validate the values + */ + if(!option_values.mode_decode) { + if(option_values.blocksize < 0) { + if(option_values.max_lpc_order == 0) + option_values.blocksize = 1152; + else + option_values.blocksize = 4608; + } + if(option_values.max_residual_partition_order < 0) { + if(option_values.blocksize <= 1152) + option_values.max_residual_partition_order = 2; + else if(option_values.blocksize <= 2304) + option_values.max_residual_partition_order = 3; + else if(option_values.blocksize <= 4608) + option_values.max_residual_partition_order = 3; + else + option_values.max_residual_partition_order = 4; + option_values.min_residual_partition_order = option_values.max_residual_partition_order; + } + if(option_values.rice_parameter_search_dist < 0) { + option_values.rice_parameter_search_dist = 0; + } + } + else { + if(option_values.test_only) { + if(option_values.skip > 0) + return usage_error("ERROR: --skip is not allowed in test mode\n"); + } + } - if(format_channels >= 0) { - if(format_channels == 0 || (unsigned)format_channels > FLAC__MAX_CHANNELS) - return long_usage("ERROR: invalid number of channels '%u', must be > 0 and <= %u\n", format_channels, FLAC__MAX_CHANNELS); - } - if(format_bps >= 0) { - if(format_bps != 8 && format_bps != 16 && format_bps != 24) - return long_usage("ERROR: invalid bits per sample '%u' (must be 8/16/24)\n", format_bps); - } - if(format_sample_rate >= 0) { - if(!FLAC__format_sample_rate_is_valid(format_sample_rate)) - return long_usage("ERROR: invalid sample rate '%u', must be > 0 and <= %u\n", format_sample_rate, FLAC__MAX_SAMPLE_RATE); - } - if(!mode_decode && ((unsigned)blocksize < FLAC__MIN_BLOCK_SIZE || (unsigned)blocksize > FLAC__MAX_BLOCK_SIZE)) { - return long_usage("ERROR: invalid blocksize '%u', must be >= %u and <= %u\n", (unsigned)blocksize, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE); - } - if(qlp_coeff_precision > 0 && qlp_coeff_precision < FLAC__MIN_QLP_COEFF_PRECISION) { - return long_usage("ERROR: invalid value for -q '%u', must be 0 or >= %u\n", qlp_coeff_precision, FLAC__MIN_QLP_COEFF_PRECISION); - } + FLAC__ASSERT(option_values.blocksize >= 0 || option_values.mode_decode); - if(sector_align) { - if(mode_decode) - return long_usage("ERROR: --sector-align only allowed for encoding\n"); - else if(skip > 0) - return long_usage("ERROR: --sector-align not allowed with --skip\n"); - else if(format_channels >= 0 && format_channels != 2) - return long_usage("ERROR: --sector-align can only be done with stereo input\n"); - else if(format_bps >= 0 && format_bps != 16) - return long_usage("ERROR: --sector-align can only be done with 16-bit samples\n"); - else if(format_sample_rate >= 0 && format_sample_rate != 44100) - return long_usage("ERROR: --sector-align can only be done with a sample rate of 44100\n"); - } - if(argc - i > 1 && cmdline_forced_outfilename) { - return long_usage("ERROR: -o cannot be used with multiple files\n"); - } - if(cmdline_forced_outfilename && output_prefix) { - return long_usage("ERROR: --output-prefix conflicts with -o\n"); - } + if(option_values.format_channels >= 0) { + if(option_values.format_channels == 0 || (unsigned)option_values.format_channels > FLAC__MAX_CHANNELS) + return usage_error("ERROR: invalid number of channels '%u', must be > 0 and <= %u\n", option_values.format_channels, FLAC__MAX_CHANNELS); + } + if(option_values.format_bps >= 0) { + if(option_values.format_bps != 8 && option_values.format_bps != 16 && option_values.format_bps != 24) + return usage_error("ERROR: invalid bits per sample '%u' (must be 8/16/24)\n", option_values.format_bps); + } + if(option_values.format_sample_rate >= 0) { + if(!FLAC__format_sample_rate_is_valid(option_values.format_sample_rate)) + return usage_error("ERROR: invalid sample rate '%u', must be > 0 and <= %u\n", option_values.format_sample_rate, FLAC__MAX_SAMPLE_RATE); + } + if(!option_values.mode_decode && ((unsigned)option_values.blocksize < FLAC__MIN_BLOCK_SIZE || (unsigned)option_values.blocksize > FLAC__MAX_BLOCK_SIZE)) { + return usage_error("ERROR: invalid blocksize '%u', must be >= %u and <= %u\n", (unsigned)option_values.blocksize, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE); + } + if(option_values.qlp_coeff_precision > 0 && option_values.qlp_coeff_precision < FLAC__MIN_QLP_COEFF_PRECISION) { + return usage_error("ERROR: invalid value for -q '%u', must be 0 or >= %u\n", option_values.qlp_coeff_precision, FLAC__MIN_QLP_COEFF_PRECISION); + } - if(verbose) { + if(option_values.sector_align) { + if(option_values.mode_decode) + return usage_error("ERROR: --sector-align only allowed for encoding\n"); + else if(option_values.skip > 0) + return usage_error("ERROR: --sector-align not allowed with --skip\n"); + else 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"); + else if(option_values.format_bps >= 0 && option_values.format_bps != 16) + return usage_error("ERROR: --sector-align can only be done with 16-bit samples\n"); + else if(option_values.format_sample_rate >= 0 && option_values.format_sample_rate != 44100) + return usage_error("ERROR: --sector-align can only be done with a sample rate of 44100\n"); + } + if(option_values.num_files > 1 && option_values.cmdline_forced_outfilename) { + return usage_error("ERROR: -o cannot be used with multiple files\n"); + } + if(option_values.cmdline_forced_outfilename && option_values.output_prefix) { + return usage_error("ERROR: --output-prefix conflicts with -o\n"); + } + + return 0; + } + if(option_values.verbose) { fprintf(stderr, "\n"); fprintf(stderr, "flac %s, Copyright (C) 2000,2001,2002 Josh Coalson\n", FLAC__VERSION_STRING); fprintf(stderr, "flac comes with ABSOLUTELY NO WARRANTY. This is free software, and you are\n"); fprintf(stderr, "welcome to redistribute it under certain conditions. Type `flac' for details.\n\n"); - if(!mode_decode) { + if(!option_values.mode_decode) { char padopt[16]; - if(padding < 0) + if(option_values.padding < 0) strcpy(padopt, "-"); else - sprintf(padopt, " %d", padding); + sprintf(padopt, " %d", option_values.padding); fprintf(stderr, "options:%s%s" #ifdef FLAC__HAS_OGG "%s" #endif "%s -P%s -b %u%s -l %u%s%s%s -q %u -r %u,%u -R %u%s\n", - delete_input?" --delete-input-file":"", sector_align?" --sector-align":"", + option_values.delete_input?" --delete-input-file":"", + option_values.sector_align?" --sector-align":"", #ifdef FLAC__HAS_OGG - use_ogg?" --ogg":"", + option_values.use_ogg?" --ogg":"", #endif - lax?" --lax":"", - padopt, (unsigned)blocksize, loose_mid_side?" -M":do_mid_side?" -m":"", max_lpc_order, - do_exhaustive_model_search?" -e":"", do_escape_coding?" -E":"", do_qlp_coeff_prec_search?" -p":"", - qlp_coeff_precision, - (unsigned)min_residual_partition_order, (unsigned)max_residual_partition_order, (unsigned)rice_parameter_search_dist, - verify? " -V":"" + option_values.lax?" --lax":"", + padopt, + (unsigned)option_values.blocksize, + option_values.loose_mid_side?" -M":option_values.do_mid_side?" -m":"", + option_values.max_lpc_order, + option_values.do_exhaustive_model_search?" -e":"", + option_values.do_escape_coding?" -E":"", + option_values.do_qlp_coeff_prec_search?" -p":"", + option_values.qlp_coeff_precision, + (unsigned)option_values.min_residual_partition_order, + (unsigned)option_values.max_residual_partition_order, + (unsigned)option_values.rice_parameter_search_dist, + option_values.verify? " -V":"" ); } } - if(mode_decode) { + if(option_values.mode_decode) { FLAC__bool first = true; - if(i == argc) { + if(option_values.num_files == 0) { retval = decode_file("-", 0); } else { - if(i + 1 != argc) - cmdline_forced_outfilename = 0; - for(retval = 0; i < argc && retval == 0; i++) { - if(0 == strcmp(argv[i], "-") && !first) + unsigned i; + if(option_values.num_files > 1) + option_values.cmdline_forced_outfilename = 0; + for(i = 0, retval = 0; i < option_values.num_files && retval == 0; i++) { + if(0 == strcmp(option_values.filenames[i], "-") && !first) continue; - retval = decode_file(argv[i], 0); + retval = decode_file(option_values.filenames[i], 0); first = false; } } @@ -477,16 +389,17 @@ int main(int argc, char *argv[]) else { /* encode */ FLAC__bool first = true; - if(i == argc) { + if(option_values.num_files == 0) { retval = encode_file("-", 0, true); } else { - if(i + 1 != argc) - cmdline_forced_outfilename = 0; - for(retval = 0; i < argc && retval == 0; i++) { - if(0 == strcmp(argv[i], "-") && !first) + unsigned i; + if(option_values.num_files > 1) + option_values.cmdline_forced_outfilename = 0; + for(i = 0, retval = 0; i < option_values.num_files && retval == 0; i++) { + if(0 == strcmp(option_values.filenames[i], "-") && !first) continue; - retval = encode_file(argv[i], 0, i == (argc-1)); + retval = encode_file(option_values.filenames[i], 0, i == (option_values.num_files-1)); first = false; } } @@ -495,190 +408,610 @@ int main(int argc, char *argv[]) return retval; } +void init_options() +{ + option_values.show_long_help = false; + option_values.mode_decode = false; + option_values.verify = false; + option_values.verbose = true; + option_values.continue_through_decode_errors = false; + option_values.lax = false; + option_values.test_only = false; + option_values.analyze = false; + option_values.use_ogg = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_qlp_coeff_prec_search = false; + option_values.force_to_stdout = false; + option_values.force_raw_format = false; + option_values.delete_input = false; + option_values.sector_align = false; + option_values.cmdline_forced_outfilename = 0; + option_values.output_prefix = 0; + option_values.aopts.do_residual_text = false; + option_values.aopts.do_residual_gnuplot = false; + option_values.padding = -1; + option_values.max_lpc_order = 8; + option_values.qlp_coeff_precision = 0; + option_values.skip = 0; + option_values.format_is_big_endian = -1; + option_values.format_is_unsigned_samples = false; + option_values.format_channels = -1; + option_values.format_bps = -1; + option_values.format_sample_rate = -1; + option_values.blocksize = -1; + option_values.min_residual_partition_order = -1; + option_values.max_residual_partition_order = -1; + option_values.rice_parameter_search_dist = -1; + option_values.requested_seek_points[0] = '\0'; + option_values.num_requested_seek_points = -1; + + option_values.num_files = 0; + option_values.filenames = 0; +} + +int parse_options(int argc, char *argv[]) +{ + int short_option; + int option_index = 1; + FLAC__bool had_error = false; + /*@@@ E and R: are deprecated */ + const char *short_opts = "0123456789ab:cdeFHl:mMo:pP:q:r:sS:tV"; + + while ((short_option = FLAC__share__getopt_long(argc, argv, short_opts, long_options_, &option_index)) != -1) { + switch (short_option) { + case 0: /* long option with no equivalent short option */ + had_error |= (parse_option(short_option, long_options_[option_index].name, FLAC__share__optarg) != 0); + break; + case '?': + case ':': + had_error = true; + break; + default: /* short option */ + had_error |= (parse_option(short_option, 0, FLAC__share__optarg) != 0); + break; + } + } + + if(had_error) { + return 1; + } + + FLAC__ASSERT(FLAC__share__optind <= argc); + + option_values.num_files = argc - FLAC__share__optind; + + if(option_values.num_files > 0) { + unsigned i = 0; + if(0 == (option_values.filenames = malloc(sizeof(char *) * option_values.num_files))) + die("out of memory allocating space for file names list"); + while(FLAC__share__optind < argc) + option_values.filenames[i++] = local_strdup(argv[FLAC__share__optind++]); + } + + return 0; +} + +int parse_option(int short_option, const char *long_option, const char *option_argument) +{ + FLAC__bool ok = true; + char *p; + + if(short_option == 0) { + FLAC__ASSERT(0 != long_option); + if(0 == strcmp(long_option, "--delete-input-file")) { + option_values.delete_input = true; + } + else if(0 == strcmp(long_option, "--output-prefix")) { + FLAC__ASSERT(0 != option_argument); + option_values.output_prefix = option_argument; + } + 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 */ + } + else if(0 == strcmp(long_option, "--super-secret-impractical-compression-level")) { + option_values.do_exhaustive_model_search = true; + option_values.do_escape_coding = true; + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + option_values.do_qlp_coeff_prec_search = true; + option_values.min_residual_partition_order = 0; + option_values.max_residual_partition_order = 16; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 32; + } + else if(0 == strcmp(long_option, "--force-raw-input")) { + option_values.force_raw_format = true; + } + else if(0 == strcmp(long_option, "--lax")) { + option_values.lax = true; + } + else if(0 == strcmp(long_option, "--sector-align")) { + option_values.sector_align = true; + } +#ifdef FLAC__HAS_OGG + else if(0 == strcmp(long_option, "--ogg")) { + option_values.use_ogg = true; + } +#endif + else if(0 == strcmp(long_option, "--endian")) { + FLAC__ASSERT(0 != option_argument); + if(0 == strncmp(option_argument, "big", strlen(option_argument))) + option_values.format_is_big_endian = true; + else if(0 == strncmp(option_argument, "little", strlen(option_argument))) + option_values.format_is_big_endian = false; + else { + return usage_error("ERROR: argument to --endian must be \"big\" or \"little\"\n"); + ok = false; + } + } + else if(0 == strcmp(long_option, "--channels")) { + FLAC__ASSERT(0 != option_argument); + option_values.format_channels = atoi(option_argument); + } + else if(0 == strcmp(long_option, "--bps")) { + FLAC__ASSERT(0 != option_argument); + option_values.format_bps = atoi(option_argument); + } + else if(0 == strcmp(long_option, "--sample-rate")) { + FLAC__ASSERT(0 != option_argument); + option_values.format_sample_rate = atoi(option_argument); + } + else if(0 == strcmp(long_option, "--sign")) { + FLAC__ASSERT(0 != option_argument); + if(0 == strncmp(option_argument, "signed", strlen(option_argument))) + option_values.format_is_unsigned_samples = false; + else if(0 == strncmp(option_argument, "unsigned", strlen(option_argument))) + option_values.format_is_unsigned_samples = true; + else { + return usage_error("ERROR: argument to --sign must be \"signed\" or \"unsigned\"\n"); + ok = false; + } + } + else if(0 == strcmp(long_option, "--residual-gnu-plot")) { + option_values.aopts.do_residual_gnuplot = true; + } + else if(0 == strcmp(long_option, "--residual-text")) { + option_values.aopts.do_residual_text = true; + } + /* + * negatives + */ + else if(0 == strcmp(long_option, "--no-decode-through-errors")) { + option_values.continue_through_decode_errors = false; + } + else if(0 == strcmp(long_option, "--no-silent")) { + option_values.verbose = true; + } + else if(0 == strcmp(long_option, "--no-seektable")) { + option_values.num_requested_seek_points = 0; + option_values.requested_seek_points[0] = '\0'; + } + else if(0 == strcmp(long_option, "--no-delete-input-file")) { + option_values.delete_input = false; + } + else if(0 == strcmp(long_option, "--no-sector-align")) { + option_values.sector_align = false; + } + else if(0 == strcmp(long_option, "--no-lax")) { + option_values.lax = false; + } +#ifdef FLAC__HAS_OGG + else if(0 == strcmp(long_option, "--no-ogg")) { + option_values.use_ogg = false; + } +#endif + else if(0 == strcmp(long_option, "--no-exhaustive-model-search")) { + option_values.do_exhaustive_model_search = false; + } +#if 0 + /* @@@ deprecated: */ + else if(0 == strcmp(long_option, "--no-escape-coding")) { + option_values.do_escape_coding = false; + } +#endif + else if(0 == strcmp(long_option, "--no-mid-side")) { + option_values.do_mid_side = option_values.loose_mid_side = false; + } + else if(0 == strcmp(long_option, "--no-adaptive-mid-side")) { + option_values.loose_mid_side = option_values.do_mid_side = false; + } + else if(0 == strcmp(long_option, "--no-qlp-coeff-prec-search")) { + option_values.do_qlp_coeff_prec_search = false; + } + else if(0 == strcmp(long_option, "--no-padding")) { + option_values.padding = -1; + } + else if(0 == strcmp(long_option, "--no-verify")) { + option_values.verify = false; + } + else if(0 == strcmp(long_option, "--no-residual-gnuplot")) { + option_values.aopts.do_residual_gnuplot = false; + } + else if(0 == strcmp(long_option, "--no-residual-text")) { + option_values.aopts.do_residual_text = false; + } + } + else { + switch(short_option) { + case 'H': + option_values.show_long_help = true; + break; + case 'd': + option_values.mode_decode = true; + break; + case 'a': + option_values.mode_decode = true; + option_values.analyze = true; + break; + case 't': + option_values.mode_decode = true; + option_values.test_only = true; + break; + case 'c': + option_values.force_to_stdout = true; + break; + case 's': + option_values.verbose = false; + break; + case 'o': + FLAC__ASSERT(0 != option_argument); + option_values.cmdline_forced_outfilename = option_argument; + break; + case 'F': + option_values.continue_through_decode_errors = true; + break; + case '0': + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_mid_side = false; + option_values.loose_mid_side = false; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = option_values.max_residual_partition_order = 2; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 0; + break; + case '1': + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = true; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = option_values.max_residual_partition_order = 2; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 0; + break; + case '2': + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = 0; + option_values.max_residual_partition_order = 3; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 0; + break; + case '3': + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_mid_side = false; + option_values.loose_mid_side = false; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = option_values.max_residual_partition_order = 3; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 6; + break; + case '4': + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = true; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = option_values.max_residual_partition_order = 3; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 8; + break; + case '5': + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = option_values.max_residual_partition_order = 3; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 8; + break; + case '6': + option_values.do_exhaustive_model_search = false; + option_values.do_escape_coding = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = 0; + option_values.max_residual_partition_order = 4; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 8; + break; + case '7': + option_values.do_exhaustive_model_search = true; + option_values.do_escape_coding = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = 0; + option_values.max_residual_partition_order = 6; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 8; + break; + case '8': + option_values.do_exhaustive_model_search = true; + option_values.do_escape_coding = false; + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + option_values.qlp_coeff_precision = 0; + option_values.min_residual_partition_order = 0; + option_values.max_residual_partition_order = 6; + option_values.rice_parameter_search_dist = 0; + option_values.max_lpc_order = 12; + break; + case '9': + return usage_error("ERROR: compression level '9' is reserved\n"); + break; + case 'V': + option_values.verify = true; + break; + case 'S': + FLAC__ASSERT(0 != option_argument); + 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, "<"); + break; + case 'P': + FLAC__ASSERT(0 != option_argument); + option_values.padding = atoi(option_argument); + if(option_values.padding < 0) { + return usage_error("ERROR: argument to -P must be >= 0\n"); + } + break; + case 'b': + FLAC__ASSERT(0 != option_argument); + option_values.blocksize = atoi(option_argument); + break; + case 'e': + option_values.do_exhaustive_model_search = true; + break; + case 'E': + option_values.do_escape_coding = true; + break; + case 'l': + FLAC__ASSERT(0 != option_argument); + option_values.max_lpc_order = atoi(option_argument); + break; + case 'm': + option_values.do_mid_side = true; + option_values.loose_mid_side = false; + break; + case 'M': + option_values.loose_mid_side = option_values.do_mid_side = true; + break; + case 'p': + option_values.do_qlp_coeff_prec_search = true; + break; + case 'q': + FLAC__ASSERT(0 != option_argument); + option_values.qlp_coeff_precision = atoi(option_argument); + break; + case 'r': + FLAC__ASSERT(0 != option_argument); + p = strchr(option_argument, ','); + if(0 == p) { + option_values.min_residual_partition_order = 0; + option_values.max_residual_partition_order = atoi(option_argument); + } + else { + option_values.min_residual_partition_order = atoi(option_argument); + option_values.max_residual_partition_order = atoi(++p); + } + break; + case 'R': + FLAC__ASSERT(0 != option_argument); + option_values.rice_parameter_search_dist = atoi(option_argument); + break; + default: + FLAC__ASSERT(0); + } + } + + return ok; +} + +void free_options() +{ + if(0 != option_values.filenames) + free(option_values.filenames); +} + +int usage_error(const char *message, ...) +{ + va_list args; + + FLAC__ASSERT(0); + + va_start(args, message); + + (void) vfprintf(stderr, message, args); + + va_end(args); + + printf("Type \"flac\" for a usage summary or \"flac --help\" for all options\n"); + + return 1; +} + static void usage_header() { - fprintf(stderr, "===============================================================================\n"); - fprintf(stderr, "flac - Command-line FLAC encoder/decoder version %s\n", FLAC__VERSION_STRING); - fprintf(stderr, "Copyright (C) 2000,2001,2002 Josh Coalson\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "This program is free software; you can redistribute it and/or\n"); - fprintf(stderr, "modify it under the terms of the GNU General Public License\n"); - fprintf(stderr, "as published by the Free Software Foundation; either version 2\n"); - fprintf(stderr, "of the License, or (at your option) any later version.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "This program is distributed in the hope that it will be useful,\n"); - fprintf(stderr, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); - fprintf(stderr, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); - fprintf(stderr, "GNU General Public License for more details.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "You should have received a copy of the GNU General Public License\n"); - fprintf(stderr, "along with this program; if not, write to the Free Software\n"); - fprintf(stderr, "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"); - fprintf(stderr, "===============================================================================\n"); + printf("===============================================================================\n"); + printf("flac - Command-line FLAC encoder/decoder version %s\n", FLAC__VERSION_STRING); + printf("Copyright (C) 2000,2001,2002 Josh Coalson\n"); + printf("\n"); + printf("This program is free software; you can redistribute it and/or\n"); + printf("modify it under the terms of the GNU General Public License\n"); + printf("as published by the Free Software Foundation; either version 2\n"); + printf("of the License, or (at your option) any later version.\n"); + printf("\n"); + printf("This program is distributed in the hope that it will be useful,\n"); + printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); + printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); + printf("GNU General Public License for more details.\n"); + printf("\n"); + printf("You should have received a copy of the GNU General Public License\n"); + printf("along with this program; if not, write to the Free Software\n"); + printf("Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"); + printf("===============================================================================\n"); } -int short_usage(const char *message, ...) +void short_usage() { - va_list args; - - if(message) { - va_start(args, message); - - (void) vfprintf(stderr, message, args); - - va_end(args); - - } usage_header(); - fprintf(stderr, "\n"); - fprintf(stderr, "This is the short help; for full help use 'flac --help'\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "To encode:\n"); - fprintf(stderr, " flac [-#] [infile [...]]\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -# is -0 (fastest compression) to -8 (highest compression); -5 is the default\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "To decode:\n"); - fprintf(stderr, " flac -d [infile [...]]\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "To test:\n"); - fprintf(stderr, " flac -t [infile [...]]\n"); - - return message? 1 : 0; + printf("\n"); + printf("This is the short help; for full help use 'flac --help'\n"); + printf("\n"); + printf("To encode:\n"); + printf(" flac [-#] [infile [...]]\n"); + printf("\n"); + printf(" -# is -0 (fastest compression) to -8 (highest compression); -5 is the default\n"); + printf("\n"); + printf("To decode:\n"); + printf(" flac -d [infile [...]]\n"); + printf("\n"); + printf("To test:\n"); + printf(" flac -t [infile [...]]\n"); } -int long_usage(const char *message, ...) +void long_usage() { - FILE *out = (message? stderr : stdout); - va_list args; - - if(message) { - va_start(args, message); - - (void) vfprintf(stderr, message, args); - - va_end(args); - - } usage_header(); - fprintf(out, "Usage:\n"); - fprintf(out, " flac [options] [infile [...]]\n"); - fprintf(out, "\n"); - fprintf(out, "For encoding:\n"); - fprintf(out, " the input file(s) may be a PCM RIFF WAVE file, AIFF file, or raw samples\n"); - fprintf(out, " the output file(s) will be in FLAC format\n"); - fprintf(out, "For decoding, the reverse is true\n"); - fprintf(out, "\n"); - fprintf(out, "A single 'infile' may be - for stdin. No 'infile' implies stdin. Use of\n"); - fprintf(out, "stdin implies -c (write to stdout). Normally you should use:\n"); - fprintf(out, " flac [options] -o outfilename or flac -d [options] -o outfilename\n"); - fprintf(out, "instead of:\n"); - fprintf(out, " flac [options] > outfilename or flac -d [options] > outfilename\n"); - fprintf(out, "since the former allows flac to seek backwards to write the STREAMINFO or\n"); - fprintf(out, "RIFF WAVE header contents when necessary.\n"); - fprintf(out, "\n"); - fprintf(out, "flac checks for the presence of a AIFF/RIFF WAVE header to decide whether or\n"); - fprintf(out, "not to treat an input file as AIFF/WAVE format or raw samples. If any infile\n"); - fprintf(out, "is raw you must specify the format options {-fb|fl} -fc -fp and -fs, which will\n"); - fprintf(out, "apply to all raw files. You can force AIFF/WAVE files to be treated as a raw\n"); - fprintf(out, "files using -fr.\n"); - fprintf(out, "\n"); - fprintf(out, "generic options:\n"); - fprintf(out, " -d : decode (default behavior is encode)\n"); - fprintf(out, " -t : test (same as -d except no decoded file is written)\n"); - fprintf(out, " -a : analyze (same as -d except an analysis file is written)\n"); - fprintf(out, " -c : write output to stdout\n"); - fprintf(out, " -s : silent (do not write runtime encode/decode statistics)\n"); - fprintf(out, " -o filename : force the output file name (usually flac just changes the\n"); - fprintf(out, " extension)\n"); - fprintf(out, " --output-prefix string : prefix each output file name with the given string.\n"); - fprintf(out, " This can be useful for encoding/decoding files to a different directory.\n"); - fprintf(out, " Make sure if your string is a path name that it ends with a '/' slash.\n"); - fprintf(out, " --delete-input-file : deletes the input file after a successful encode/decode\n"); - fprintf(out, " --skip samples : can be used both for encoding and decoding\n"); - fprintf(out, "analyze options:\n"); - fprintf(out, " --a-rtext : include residual signal in text output\n"); - fprintf(out, " --a-rgp : generate gnuplot files of residual distribution of each subframe\n"); - fprintf(out, "decoding options:\n"); - fprintf(out, " -F : force decoder to continue decoding through stream errors\n"); - fprintf(out, "encoding options:\n"); + printf("Usage:\n"); + printf(" flac [options] [infile [...]]\n"); + printf("\n"); + printf("For encoding:\n"); + printf(" the input file(s) may be a PCM RIFF WAVE file, AIFF file, or raw samples\n"); + printf(" the output file(s) will be in FLAC format\n"); + printf("For decoding, the reverse is true\n"); + printf("\n"); + printf("A single 'infile' may be - for stdin. No 'infile' implies stdin. Use of\n"); + printf("stdin implies -c (write to stdout). Normally you should use:\n"); + printf(" flac [options] -o outfilename or flac -d [options] -o outfilename\n"); + printf("instead of:\n"); + printf(" flac [options] > outfilename or flac -d [options] > outfilename\n"); + printf("since the former allows flac to seek backwards to write the STREAMINFO or\n"); + printf("RIFF WAVE header contents when necessary.\n"); + printf("\n"); + printf("flac checks for the presence of a AIFF/RIFF WAVE header to decide whether or\n"); + printf("not to treat an input file as AIFF/WAVE format or raw samples. If any infile\n"); + printf("is raw you must specify the format options {-fb|fl} -fc -fp and -fs, which will\n"); + printf("apply to all raw files. You can force AIFF/WAVE files to be treated as a raw\n"); + printf("files using -fr.\n"); + printf("\n"); + printf("generic options:\n"); + printf(" -d : decode (default behavior is encode)\n"); + printf(" -t : test (same as -d except no decoded file is written)\n"); + printf(" -a : analyze (same as -d except an analysis file is written)\n"); + printf(" -c : write output to stdout\n"); + printf(" -s : silent (do not write runtime encode/decode statistics)\n"); + printf(" -o filename : force the output file name (usually flac just changes the\n"); + printf(" extension)\n"); + printf(" --output-prefix string : prefix each output file name with the given string.\n"); + printf(" This can be useful for encoding/decoding files to a different directory.\n"); + printf(" Make sure if your string is a path name that it ends with a '/' slash.\n"); + printf(" --delete-input-file : deletes the input file after a successful encode/decode\n"); + printf(" --skip samples : can be used both for encoding and decoding\n"); + printf("analyze options:\n"); + printf(" --a-rtext : include residual signal in text output\n"); + printf(" --a-rgp : generate gnuplot files of residual distribution of each subframe\n"); + printf("decoding options:\n"); + printf(" -F : force decoder to continue decoding through stream errors\n"); + printf("encoding options:\n"); #ifdef FLAC__HAS_OGG - fprintf(out, " --ogg : output Ogg-FLAC stream instead of native FLAC\n"); + printf(" --ogg : output Ogg-FLAC stream instead of native FLAC\n"); #endif - fprintf(out, " --lax : allow encoder to generate non-Subset files\n"); - fprintf(out, " --sector-align : align encoding of multiple files on sector boundaries\n"); - fprintf(out, " -S { # | X | #x } : include a point or points in a SEEKTABLE\n"); - fprintf(out, " # : a specific sample number for a seek point\n"); - fprintf(out, " X : a placeholder point (always goes at the end of the SEEKTABLE)\n"); - fprintf(out, " #x : # evenly spaced seekpoints, the first being at sample 0\n"); - fprintf(out, " You may use many -S options; the resulting SEEKTABLE will be the unique-\n"); - fprintf(out, " ified union of all such values.\n"); - fprintf(out, " With no -S options, flac defaults to '-S 100x'. Use -S- for no SEEKTABLE.\n"); - fprintf(out, " Note: -S #x will not work if the encoder can't determine the input size\n"); - fprintf(out, " before starting.\n"); - fprintf(out, " Note: if you use -S # and # is >= samples in the input, there will be\n"); - fprintf(out, " either no seek point entered (if the input size is determinable\n"); - fprintf(out, " before encoding starts) or a placeholder point (if input size is not\n"); - fprintf(out, " determinable)\n"); - fprintf(out, " -P # : write a PADDING block of length # (goes after SEEKTABLE)\n"); - fprintf(out, " (# must be >= 0; default is -P-). Note that the overall size in bytes\n"); - fprintf(out, " of the PADDING block will be # + 4 because of the metadata header.\n"); - fprintf(out, " -b # : specify blocksize in samples; default is 1152 for -l 0, else 4608;\n"); - fprintf(out, " must be 192/576/1152/2304/4608/256/512/1024/2048/4096/8192/16384/32768\n"); - fprintf(out, " (unless --lax is used)\n"); - fprintf(out, " -m : try mid-side coding for each frame (stereo input only)\n"); - fprintf(out, " -M : adaptive mid-side coding for all frames (stereo input only)\n"); - fprintf(out, " -0 .. -8 : fastest compression .. highest compression, default is -5\n"); - fprintf(out, " these are synonyms for other options:\n"); - fprintf(out, " -0 : synonymous with -l 0 -b 1152 -r 2,2\n"); - fprintf(out, " -1 : synonymous with -l 0 -b 1152 -M -r 2,2\n"); - fprintf(out, " -2 : synonymous with -l 0 -b 1152 -m -r 3\n"); - fprintf(out, " -3 : synonymous with -l 6 -b 4608 -r 3,3\n"); - fprintf(out, " -4 : synonymous with -l 8 -b 4608 -M -r 3,3\n"); - fprintf(out, " -5 : synonymous with -l 8 -b 4608 -m -r 3,3\n"); - fprintf(out, " -6 : synonymous with -l 8 -b 4608 -m -r 4\n"); - fprintf(out, " -7 : synonymous with -l 8 -b 4608 -m -e -r 6\n"); - fprintf(out, " -8 : synonymous with -l 12 -b 4608 -m -e -r 6\n"); - fprintf(out, " --fast, --best : synonymous with -0 and -8 respectively\n"); - fprintf(out, " -e : do exhaustive model search (expensive!)\n"); - fprintf(out, " -E : include escape coding in the entropy coder\n"); - fprintf(out, " -l # : specify max LPC order; 0 => use only fixed predictors\n"); - fprintf(out, " -p : do exhaustive search of LP coefficient quantization (expensive!);\n"); - fprintf(out, " overrides -q, does nothing if using -l 0\n"); - fprintf(out, " -q # : specify precision in bits of quantized linear-predictor coefficients;\n"); - fprintf(out, " 0 => let encoder decide (min is %u, default is -q 0)\n", FLAC__MIN_QLP_COEFF_PRECISION); - fprintf(out, " -r [#,]# : [min,]max residual partition order (# is 0..16; min defaults to 0;\n"); - fprintf(out, " default is -r 0; above 4 doesn't usually help much)\n"); + printf(" --lax : allow encoder to generate non-Subset files\n"); + printf(" --sector-align : align encoding of multiple files on sector boundaries\n"); + printf(" -S { # | X | #x } : 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"); + printf(" #x : # evenly spaced seekpoints, the first being at sample 0\n"); + printf(" You may use many -S options; the resulting SEEKTABLE will be the unique-\n"); + printf(" ified union of all such values.\n"); + printf(" With no -S options, flac defaults to '-S 100x'. Use -S- for no SEEKTABLE.\n"); + printf(" Note: -S #x will not work if the encoder can't determine the input size\n"); + printf(" before starting.\n"); + printf(" Note: if you use -S # and # is >= samples in the input, there will be\n"); + printf(" either no seek point entered (if the input size is determinable\n"); + printf(" before encoding starts) or a placeholder point (if input size is not\n"); + printf(" determinable)\n"); + printf(" -P # : write a PADDING block of length # (goes after SEEKTABLE)\n"); + printf(" (# must be >= 0; default is -P-). Note that the overall size in bytes\n"); + printf(" of the PADDING block will be # + 4 because of the metadata header.\n"); + printf(" -b # : specify blocksize in samples; default is 1152 for -l 0, else 4608;\n"); + printf(" must be 192/576/1152/2304/4608/256/512/1024/2048/4096/8192/16384/32768\n"); + printf(" (unless --lax is used)\n"); + printf(" -m : try mid-side coding for each frame (stereo input only)\n"); + printf(" -M : adaptive mid-side coding for all frames (stereo input only)\n"); + printf(" -0 .. -8 : fastest compression .. highest compression, default is -5\n"); + printf(" these are synonyms for other options:\n"); + printf(" -0 : synonymous with -l 0 -b 1152 -r 2,2\n"); + printf(" -1 : synonymous with -l 0 -b 1152 -M -r 2,2\n"); + printf(" -2 : synonymous with -l 0 -b 1152 -m -r 3\n"); + printf(" -3 : synonymous with -l 6 -b 4608 -r 3,3\n"); + printf(" -4 : synonymous with -l 8 -b 4608 -M -r 3,3\n"); + printf(" -5 : synonymous with -l 8 -b 4608 -m -r 3,3\n"); + printf(" -6 : synonymous with -l 8 -b 4608 -m -r 4\n"); + printf(" -7 : synonymous with -l 8 -b 4608 -m -e -r 6\n"); + printf(" -8 : synonymous with -l 12 -b 4608 -m -e -r 6\n"); + printf(" --fast, --best : synonymous with -0 and -8 respectively\n"); + printf(" -e : do exhaustive model search (expensive!)\n"); + printf(" -E : include escape coding in the entropy coder\n"); + printf(" -l # : specify max LPC order; 0 => use only fixed predictors\n"); + printf(" -p : do exhaustive search of LP coefficient quantization (expensive!);\n"); + printf(" overrides -q, does nothing if using -l 0\n"); + printf(" -q # : specify precision in bits of quantized linear-predictor coefficients;\n"); + printf(" 0 => let encoder decide (min is %u, default is -q 0)\n", FLAC__MIN_QLP_COEFF_PRECISION); + printf(" -r [#,]# : [min,]max residual partition order (# is 0..16; min defaults to 0;\n"); + printf(" default is -r 0; above 4 doesn't usually help much)\n"); #if 0 @@@ removed because it doesnt work yet and is too dangerous for users - fprintf(out, " -R # : Rice parameter search distance (# is 0..32; above 2 doesn't help much)\n"); + printf(" -R # : Rice parameter search distance (# is 0..32; above 2 doesn't help much)\n"); #endif - fprintf(out, " -V : verify a correct encoding by decoding the output in parallel and\n"); - fprintf(out, " comparing to the original\n"); - fprintf(out, " -S-, -P-, -m-, -M-, -e-, -E-, -p-, -V-, --delete-input-file-,%s --lax-,\n", + printf(" -V : verify a correct encoding by decoding the output in parallel and\n"); + printf(" comparing to the original\n"); + printf(" -S-, -P-, -m-, -M-, -e-, -E-, -p-, -V-, --delete-input-file-,%s --lax-,\n", #ifdef FLAC__HAS_OGG " --ogg-," #else "" #endif ); - fprintf(out, " --sector-align- can all be used to turn off a particular option\n"); - fprintf(out, "format options:\n"); - fprintf(out, " -fb | -fl : big-endian | little-endian byte order\n"); - fprintf(out, " -fc channels\n"); - fprintf(out, " -fp bits_per_sample\n"); - fprintf(out, " -fs sample_rate : in Hz\n"); - fprintf(out, " -fu : unsigned samples (default is signed)\n"); - fprintf(out, " -fr : force input to be treated as raw samples\n"); - - return message? 1 : 0; + printf(" --sector-align- can all be used to turn off a particular option\n"); + printf("format options:\n"); + printf(" -fb | -fl : big-endian | little-endian byte order\n"); + printf(" -fc channels\n"); + printf(" -fp bits_per_sample\n"); + printf(" -fs sample_rate : in Hz\n"); + printf(" -fu : unsigned samples (default is signed)\n"); + printf(" -fr : force input to be treated as raw samples\n"); } void format_mistake(const char *infilename, const char *wrong, const char *right) { - fprintf(stderr, "WARNING: %s is not a %s file; treating as a %s file\n", - infilename, wrong, right); + fprintf(stderr, "WARNING: %s is not a %s file; treating as a %s file\n", infilename, wrong, right); } int encode_file(const char *infilename, const char *forced_outfilename, FLAC__bool is_last_file) @@ -705,7 +1038,7 @@ int encode_file(const char *infilename, const char *forced_outfilename, FLAC__bo } } - if(!force_raw_format) { + if(!option_values.force_raw_format) { /* first set format based on name */ if(strlen(infilename) > 3 && 0 == strcasecmp(infilename+(strlen(infilename)-4), ".wav")) fmt= WAV; @@ -733,21 +1066,21 @@ int encode_file(const char *infilename, const char *forced_outfilename, FLAC__bo } } - if(sector_align && fmt == RAW && infilesize < 0) { + if(option_values.sector_align && fmt == RAW && infilesize < 0) { fprintf(stderr, "ERROR: can't --sector-align when the input size is unknown\n"); return 1; } if(fmt == RAW) { - if(format_is_big_endian < 0 || format_channels < 0 || format_bps < 0 || format_sample_rate < 0) - return long_usage("ERROR: for encoding a raw file you must specify { -fb or -fl }, -fc, -fp, and -fs\n"); + if(option_values.format_is_big_endian < 0 || option_values.format_channels < 0 || option_values.format_bps < 0 || option_values.format_sample_rate < 0) + return usage_error("ERROR: for encoding a raw file you must specify { -fb or -fl }, -fc, -fp, and -fs\n"); } - if(encode_infile == stdin || force_to_stdout) + if(encode_infile == stdin || option_values.force_to_stdout) strcpy(outfilename, "-"); else { - const char *suffix = (use_ogg? ogg_suffix : flac_suffix); - strcpy(outfilename, output_prefix? output_prefix : ""); + const char *suffix = (option_values.use_ogg? ogg_suffix : flac_suffix); + strcpy(outfilename, option_values.output_prefix? option_values.output_prefix : ""); strcat(outfilename, infilename); if(0 == (p = strrchr(outfilename, '.'))) strcat(outfilename, suffix); @@ -762,44 +1095,44 @@ int encode_file(const char *infilename, const char *forced_outfilename, FLAC__bo } if(0 == forced_outfilename) forced_outfilename = outfilename; - if(0 != cmdline_forced_outfilename) - forced_outfilename = cmdline_forced_outfilename; + if(0 != option_values.cmdline_forced_outfilename) + forced_outfilename = option_values.cmdline_forced_outfilename; - common_options.verbose = verbose; - common_options.skip = skip; - common_options.verify = verify; + 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 = use_ogg; + common_options.use_ogg = option_values.use_ogg; #endif - common_options.lax = lax; - common_options.do_mid_side = do_mid_side; - common_options.loose_mid_side = loose_mid_side; - common_options.do_exhaustive_model_search = do_exhaustive_model_search; - common_options.do_escape_coding = do_escape_coding; - common_options.do_qlp_coeff_prec_search = do_qlp_coeff_prec_search; - common_options.min_residual_partition_order = min_residual_partition_order; - common_options.max_residual_partition_order = max_residual_partition_order; - common_options.rice_parameter_search_dist = rice_parameter_search_dist; - common_options.max_lpc_order = max_lpc_order; - common_options.blocksize = (unsigned)blocksize; - common_options.qlp_coeff_precision = qlp_coeff_precision; - common_options.padding = padding; - common_options.requested_seek_points = requested_seek_points; - common_options.num_requested_seek_points = num_requested_seek_points; + common_options.lax = option_values.lax; + common_options.do_mid_side = option_values.do_mid_side; + common_options.loose_mid_side = option_values.loose_mid_side; + common_options.do_exhaustive_model_search = option_values.do_exhaustive_model_search; + common_options.do_escape_coding = option_values.do_escape_coding; + common_options.do_qlp_coeff_prec_search = option_values.do_qlp_coeff_prec_search; + common_options.min_residual_partition_order = option_values.min_residual_partition_order; + common_options.max_residual_partition_order = option_values.max_residual_partition_order; + common_options.rice_parameter_search_dist = option_values.rice_parameter_search_dist; + common_options.max_lpc_order = option_values.max_lpc_order; + common_options.blocksize = (unsigned)option_values.blocksize; + common_options.qlp_coeff_precision = option_values.qlp_coeff_precision; + 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.is_last_file = is_last_file; common_options.align_reservoir = align_reservoir; common_options.align_reservoir_samples = &align_reservoir_samples; - common_options.sector_align = sector_align; + common_options.sector_align = option_values.sector_align; if(fmt == RAW) { raw_encode_options_t options; options.common = common_options; - options.is_big_endian = format_is_big_endian; - options.is_unsigned_samples = format_is_unsigned_samples; - options.channels = format_channels; - options.bps = format_bps; - options.sample_rate = format_sample_rate; + options.is_big_endian = option_values.format_is_big_endian; + options.is_unsigned_samples = option_values.format_is_unsigned_samples; + options.channels = option_values.format_channels; + options.bps = option_values.format_bps; + options.sample_rate = option_values.format_sample_rate; retval = flac__encode_raw(encode_infile, infilesize, infilename, forced_outfilename, lookahead, lookahead_length, options); } @@ -817,7 +1150,7 @@ int encode_file(const char *infilename, const char *forced_outfilename, FLAC__bo if(retval == 0 && strcmp(infilename, "-")) { if(strcmp(forced_outfilename, "-")) flac__file_copy_metadata(infilename, forced_outfilename); - if(delete_input) + if(option_values.delete_input) unlink(infilename); } @@ -833,16 +1166,16 @@ int decode_file(const char *infilename, const char *forced_outfilename) FLAC__bool treat_as_ogg = false; decode_options_t common_options; - if(!test_only && !analyze) { - if(force_raw_format && format_is_big_endian < 0) - return long_usage("ERROR: for decoding to a raw file you must specify -fb or -fl\n"); + if(!option_values.test_only && !option_values.analyze) { + if(option_values.force_raw_format && option_values.format_is_big_endian < 0) + return usage_error("ERROR: for decoding to a raw file you must specify -fb or -fl\n"); } - if(0 == strcmp(infilename, "-") || force_to_stdout) + if(0 == strcmp(infilename, "-") || option_values.force_to_stdout) strcpy(outfilename, "-"); else { - const char *suffix = suffixes[analyze? 2 : force_raw_format? 1 : 0]; - strcpy(outfilename, output_prefix? output_prefix : ""); + const char *suffix = suffixes[option_values.analyze? 2 : option_values.force_raw_format? 1 : 0]; + strcpy(outfilename, option_values.output_prefix? option_values.output_prefix : ""); strcat(outfilename, infilename); if(0 == (p = strrchr(outfilename, '.'))) strcat(outfilename, suffix); @@ -857,10 +1190,10 @@ int decode_file(const char *infilename, const char *forced_outfilename) } if(0 == forced_outfilename) forced_outfilename = outfilename; - if(0 != cmdline_forced_outfilename) - forced_outfilename = cmdline_forced_outfilename; + if(0 != option_values.cmdline_forced_outfilename) + forced_outfilename = option_values.cmdline_forced_outfilename; - if(use_ogg) + if(option_values.use_ogg) treat_as_ogg = true; else if(0 == strcasecmp(infilename+(strlen(infilename)-4), ".ogg")) treat_as_ogg = true; @@ -874,36 +1207,52 @@ int decode_file(const char *infilename, const char *forced_outfilename) } #endif - common_options.verbose = verbose; - common_options.continue_through_decode_errors = continue_through_decode_errors; + common_options.verbose = option_values.verbose; + common_options.continue_through_decode_errors = option_values.continue_through_decode_errors; #ifdef FLAC__HAS_OGG common_options.is_ogg = treat_as_ogg; #endif - common_options.skip = skip; + common_options.skip = option_values.skip; - if(!force_raw_format) { + if(!option_values.force_raw_format) { wav_decode_options_t options; options.common = common_options; - retval = flac__decode_wav(infilename, test_only? 0 : forced_outfilename, analyze, aopts, options); + retval = flac__decode_wav(infilename, option_values.test_only? 0 : forced_outfilename, option_values.analyze, option_values.aopts, options); } else { raw_decode_options_t options; options.common = common_options; - options.is_big_endian = format_is_big_endian; - options.is_unsigned_samples = format_is_unsigned_samples; + options.is_big_endian = option_values.format_is_big_endian; + options.is_unsigned_samples = option_values.format_is_unsigned_samples; - retval = flac__decode_raw(infilename, test_only? 0 : forced_outfilename, analyze, aopts, options); + retval = flac__decode_raw(infilename, option_values.test_only? 0 : forced_outfilename, option_values.analyze, option_values.aopts, options); } if(retval == 0 && strcmp(infilename, "-")) { if(strcmp(forced_outfilename, "-")) flac__file_copy_metadata(infilename, forced_outfilename); - if(delete_input) + if(option_values.delete_input) unlink(infilename); } return retval; } + +void die(const char *message) +{ + FLAC__ASSERT(0 != message); + fprintf(stderr, "ERROR: %s\n", message); + exit(1); +} + +char *local_strdup(const char *source) +{ + char *ret; + FLAC__ASSERT(0 != source); + if(0 == (ret = strdup(source))) + die("out of memory during strdup()"); + return ret; +}