Commit c80bdc6a authored by Josh Coalson's avatar Josh Coalson
Browse files

revamp --skip processing, allow new mm:ss.sss form

parent d4f75bf0
......@@ -48,7 +48,7 @@ typedef struct {
FLAC__bool test_only;
FLAC__bool analysis_mode;
analysis_options aopts;
FLAC__uint64 skip;
utils__SkipUntilSpecification *skip_specification;
const char *inbasefilename;
const char *outfilename;
......@@ -94,7 +94,7 @@ static FLAC__bool is_big_endian_host_;
/*
* local routines
*/
static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, FLAC__uint64 skip, const char *infilename, const char *outfilename);
static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, const char *infilename, const char *outfilename);
static void DecoderSession_destroy(DecoderSession *d, FLAC__bool error_occurred);
static FLAC__bool DecoderSession_init_decoder(DecoderSession *d, decode_options_t decode_options, const char *infilename);
static FLAC__bool DecoderSession_process(DecoderSession *d);
......@@ -138,7 +138,7 @@ int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool
options.common.continue_through_decode_errors,
analysis_mode,
aopts,
options.common.skip,
&options.common.skip_specification,
infilename,
outfilename
)
......@@ -174,7 +174,7 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool
options.common.continue_through_decode_errors,
analysis_mode,
aopts,
options.common.skip,
&options.common.skip_specification,
infilename,
outfilename
)
......@@ -190,7 +190,7 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool
return DecoderSession_finish_ok(&decoder_session);
}
FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, FLAC__uint64 skip, const char *infilename, const char *outfilename)
FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool verbose, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, const char *infilename, const char *outfilename)
{
#ifdef FLAC__HAS_OGG
d->is_ogg = is_ogg;
......@@ -204,7 +204,7 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__
d->test_only = (0 == outfilename);
d->analysis_mode = analysis_mode;
d->aopts = aopts;
d->skip = skip;
d->skip_specification = skip_specification;
d->inbasefilename = grabbag__file_get_basename(infilename);
d->outfilename = outfilename;
......@@ -341,20 +341,46 @@ FLAC__bool DecoderSession_init_decoder(DecoderSession *decoder_session, decode_o
FLAC__bool DecoderSession_process(DecoderSession *d)
{
if(d->skip > 0) {
#ifdef FLAC__HAS_OGG
if(d->is_ogg) { /*@@@ (move this check into main.c) */
fprintf(stderr, "%s: ERROR, can't skip when decoding Ogg-FLAC yet; convert to native-FLAC first\n", d->inbasefilename);
if(d->is_ogg) {
if(!OggFLAC__stream_decoder_process_until_end_of_metadata(d->decoder.ogg.stream)) {
if(d->verbose) fprintf(stderr, "\n");
print_error_with_state(d, "ERROR while decoding metadata");
return false;
}
if(OggFLAC__stream_decoder_get_FLAC_stream_decoder_state(d->decoder.ogg.stream) != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && OggFLAC__stream_decoder_get_FLAC_stream_decoder_state(d->decoder.ogg.stream) != FLAC__STREAM_DECODER_END_OF_STREAM) {
if(d->verbose) fprintf(stderr, "\n");
print_error_with_state(d, "ERROR during metadata decoding");
return false;
}
}
else
#endif
{
if(!FLAC__file_decoder_process_until_end_of_metadata(d->decoder.flac.file)) {
if(d->verbose) fprintf(stderr, "\n");
print_error_with_state(d, "ERROR while decoding metadata");
return false;
}
if(d->abort_flag)
if(FLAC__file_decoder_get_state(d->decoder.flac.file) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_get_state(d->decoder.flac.file) != FLAC__FILE_DECODER_END_OF_FILE) {
if(d->verbose) fprintf(stderr, "\n");
print_error_with_state(d, "ERROR during metadata decoding");
return false;
if(!FLAC__file_decoder_seek_absolute(d->decoder.flac.file, d->skip)) {
}
}
if(d->abort_flag)
return false;
if(d->skip_specification->value.samples > 0) {
const FLAC__uint64 skip = (FLAC__uint64)d->skip_specification->value.samples;
#ifdef FLAC__HAS_OGG
if(d->is_ogg) { /*@@@ (move this check into main.c) */
fprintf(stderr, "%s: ERROR, can't skip when decoding Ogg-FLAC yet; convert to native-FLAC first\n", d->inbasefilename);
return false;
}
#endif
if(!FLAC__file_decoder_seek_absolute(d->decoder.flac.file, skip)) {
print_error_with_state(d, "ERROR seeking while skipping bytes");
return false;
}
......@@ -679,22 +705,28 @@ void metadata_callback(const void *decoder, const FLAC__StreamMetadata *metadata
DecoderSession *decoder_session = (DecoderSession*)client_data;
(void)decoder;
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
FLAC__uint64 skip;
decoder_session->bps = metadata->data.stream_info.bits_per_sample;
decoder_session->channels = metadata->data.stream_info.channels;
decoder_session->sample_rate = metadata->data.stream_info.sample_rate;
flac__utils_canonicalize_skip_until_specification(decoder_session->skip_specification, decoder_session->sample_rate);
FLAC__ASSERT(decoder_session->skip_specification->value.samples >= 0);
skip = (FLAC__uint64)decoder_session->skip_specification->value.samples;
/* remember, metadata->data.stream_info.total_samples can be 0, meaning 'unknown' */
if(metadata->data.stream_info.total_samples > 0 && decoder_session->skip >= metadata->data.stream_info.total_samples) {
if(metadata->data.stream_info.total_samples > 0 && skip >= metadata->data.stream_info.total_samples) {
fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", decoder_session->inbasefilename);
decoder_session->abort_flag = true;
return;
}
else if(metadata->data.stream_info.total_samples == 0 && decoder_session->skip > 0) {
else if(metadata->data.stream_info.total_samples == 0 && skip > 0) {
fprintf(stderr, "%s: ERROR, can't skip when FLAC metadata has total sample count of 0\n", decoder_session->inbasefilename);
decoder_session->abort_flag = true;
return;
}
else
decoder_session->total_samples = metadata->data.stream_info.total_samples - decoder_session->skip;
decoder_session->bps = metadata->data.stream_info.bits_per_sample;
decoder_session->channels = metadata->data.stream_info.channels;
decoder_session->sample_rate = metadata->data.stream_info.sample_rate;
decoder_session->total_samples = metadata->data.stream_info.total_samples - skip;
if(decoder_session->bps != 8 && decoder_session->bps != 16 && decoder_session->bps != 24) {
fprintf(stderr, "%s: ERROR: bits per sample is not 8/16/24\n", decoder_session->inbasefilename);
......
......@@ -20,6 +20,7 @@
#define flac__decode_h
#include "analyze.h"
#include "utils.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
......@@ -33,7 +34,7 @@ typedef struct {
FLAC__bool use_first_serial_number;
long serial_number;
#endif
FLAC__uint64 skip;
utils__SkipUntilSpecification skip_specification;
} decode_options_t;
typedef struct {
......
......@@ -58,6 +58,7 @@ typedef struct {
const char *inbasefilename;
const char *outfilename;
FLAC__uint64 skip;
FLAC__bool replay_gain;
unsigned channels;
unsigned bits_per_sample;
......@@ -157,8 +158,6 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch
FLAC__bool got_comm_chunk= false, got_ssnd_chunk= false;
int info_align_carry= -1, info_align_zero= -1;
FLAC__ASSERT(!options.common.sector_align || options.common.skip == 0);
(void)infilesize; /* silence compiler warning about unused parameter */
(void)lookahead; /* silence compiler warning about unused parameter */
(void)lookahead_length; /* silence compiler warning about unused parameter */
......@@ -263,6 +262,15 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch
skip-= need;
}
/*
* now that we know the sample rate, canonicalize the
* --skip string to a number of samples:
*/
flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate);
FLAC__ASSERT(options.common.skip_specification.value.samples >= 0);
encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples;
FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0);
got_comm_chunk= true;
}
else if(got_ssnd_chunk==false && !strncmp(chunk_id, "SSND", 4)) { /* sound data chunk */
......@@ -303,8 +311,8 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch
}
block_size= xx;
if(options.common.skip>0U) {
FLAC__uint64 remaining= options.common.skip*bytes_per_frame;
if(encoder_session.skip>0U) {
FLAC__uint64 remaining= encoder_session.skip*bytes_per_frame;
/* do 1<<30 bytes at a time, since 1<<30 is a nice round number, and */
/* is guaranteed to be less than LONG_MAX */
......@@ -324,7 +332,7 @@ flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const ch
}
}
data_bytes-= (8U + (unsigned int)options.common.skip*bytes_per_frame); /*@@@ WATCHOUT: 4GB limit */
data_bytes-= (8U + (unsigned int)encoder_session.skip*bytes_per_frame); /*@@@ WATCHOUT: 4GB limit */
encoder_session.total_samples_to_encode= data_bytes/bytes_per_frame + *options.common.align_reservoir_samples;
if(options.common.sector_align) {
align_remainder= (unsigned int)(encoder_session.total_samples_to_encode % 588U);
......@@ -487,8 +495,6 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons
unsigned align_remainder = 0;
int info_align_carry = -1, info_align_zero = -1;
FLAC__ASSERT(!options.common.sector_align || options.common.skip == 0);
(void)infilesize;
(void)lookahead;
(void)lookahead_length;
......@@ -595,6 +601,15 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons
}
}
/*
* now that we know the sample rate, canonicalize the
* --skip string to a number of samples:
*/
flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate);
FLAC__ASSERT(options.common.skip_specification.value.samples >= 0);
encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples;
FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0);
got_fmt_chunk = true;
}
else if(xx == 0x61746164 && !got_data_chunk && got_fmt_chunk) { /* "data" */
......@@ -605,11 +620,11 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons
bytes_per_wide_sample = channels * (bps >> 3);
if(options.common.skip > 0) {
if(fseek(infile, bytes_per_wide_sample * (unsigned)options.common.skip, SEEK_CUR) < 0) {
if(encoder_session.skip > 0) {
if(fseek(infile, bytes_per_wide_sample * (unsigned)encoder_session.skip, SEEK_CUR) < 0) {
/* can't seek input, read ahead manually... */
unsigned left, need;
for(left = (unsigned)options.common.skip; left > 0; ) { /*@@@ WATCHOUT: 4GB limit */
for(left = (unsigned)encoder_session.skip; left > 0; ) { /*@@@ WATCHOUT: 4GB limit */
need = min(left, CHUNK_OF_SAMPLES);
if(fread(ucbuffer_, bytes_per_wide_sample, need, infile) < need) {
fprintf(stderr, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
......@@ -620,7 +635,7 @@ int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, cons
}
}
data_bytes -= (unsigned)options.common.skip * bytes_per_wide_sample; /*@@@ WATCHOUT: 4GB limit */
data_bytes -= (unsigned)encoder_session.skip * bytes_per_wide_sample; /*@@@ WATCHOUT: 4GB limit */
encoder_session.total_samples_to_encode = data_bytes / bytes_per_wide_sample + *options.common.align_reservoir_samples;
if(options.common.sector_align) {
align_remainder = (unsigned)(encoder_session.total_samples_to_encode % 588);
......@@ -783,12 +798,10 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons
unsigned align_remainder = 0;
int info_align_carry = -1, info_align_zero = -1;
FLAC__ASSERT(!options.common.sector_align || options.common.skip == 0);
FLAC__ASSERT(!options.common.sector_align || options.channels == 2);
FLAC__ASSERT(!options.common.sector_align || options.bps == 16);
FLAC__ASSERT(!options.common.sector_align || options.sample_rate == 44100);
FLAC__ASSERT(!options.common.sector_align || infilesize >= 0);
FLAC__ASSERT(!options.common.replay_gain || options.common.skip == 0);
FLAC__ASSERT(!options.common.replay_gain || options.channels <= 2);
FLAC__ASSERT(!options.common.replay_gain || grabbag__replaygain_is_valid_sample_frequency(options.sample_rate));
......@@ -809,13 +822,23 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons
)
return 1;
/*
* now that we know the sample rate, canonicalize the
* --skip string to a number of samples:
*/
flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, options.sample_rate);
FLAC__ASSERT(options.common.skip_specification.value.samples >= 0);
encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples;
FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0);
FLAC__ASSERT(!options.common.replay_gain || encoder_session.skip == 0);
/* get the file length */
if(infilesize < 0) {
encoder_session.total_samples_to_encode = encoder_session.unencoded_size = 0;
}
else {
if(options.common.sector_align) {
FLAC__ASSERT(options.common.skip == 0);
FLAC__ASSERT(encoder_session.skip == 0);
encoder_session.total_samples_to_encode = (unsigned)infilesize / bytes_per_wide_sample + *options.common.align_reservoir_samples;
align_remainder = (unsigned)(encoder_session.total_samples_to_encode % 588);
if(options.common.is_last_file)
......@@ -824,7 +847,7 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons
encoder_session.total_samples_to_encode -= align_remainder; /* will stop short and carry over to next file */
}
else {
encoder_session.total_samples_to_encode = (unsigned)infilesize / bytes_per_wide_sample - options.common.skip;
encoder_session.total_samples_to_encode = (unsigned)infilesize / bytes_per_wide_sample - encoder_session.skip;
}
encoder_session.unencoded_size = encoder_session.total_samples_to_encode * bytes_per_wide_sample;
......@@ -833,8 +856,8 @@ int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, cons
if(encoder_session.verbose && encoder_session.total_samples_to_encode <= 0)
fprintf(stderr, "(No runtime statistics possible; please wait for encoding to finish...)\n");
if(options.common.skip > 0) {
unsigned skip_bytes = bytes_per_wide_sample * (unsigned)options.common.skip;
if(encoder_session.skip > 0) {
unsigned skip_bytes = bytes_per_wide_sample * (unsigned)encoder_session.skip;
if(skip_bytes > lookahead_length) {
skip_bytes -= lookahead_length;
lookahead_length = 0;
......@@ -997,6 +1020,7 @@ FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg, FLAC_
e->inbasefilename = grabbag__file_get_basename(infilename);
e->outfilename = outfilename;
e->skip = 0; /* filled in later after the sample_rate is known */
e->unencoded_size = 0;
e->total_samples_to_encode = 0;
e->bytes_written = 0;
......
......@@ -20,6 +20,7 @@
#define flac__encode_h
#include "FLAC/metadata.h"
#include "utils.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
......@@ -27,7 +28,7 @@
typedef struct {
FLAC__bool verbose;
FLAC__uint64 skip;
utils__SkipUntilSpecification skip_specification;
FLAC__bool verify;
#ifdef FLAC__HAS_OGG
FLAC__bool use_ogg;
......
......@@ -38,6 +38,7 @@
#include "analyze.h"
#include "decode.h"
#include "encode.h"
#include "utils.h"
#include "vorbiscomment.h"
#if 0
......@@ -228,7 +229,7 @@ static struct {
int padding;
unsigned max_lpc_order;
unsigned qlp_coeff_precision;
FLAC__uint64 skip;
const char *skip_specification;
int format_is_big_endian;
int format_is_unsigned_samples;
int format_channels;
......@@ -333,7 +334,7 @@ int do_it()
}
else {
if(option_values.test_only) {
if(option_values.skip > 0)
if(0 != option_values.skip_specification)
return usage_error("ERROR: --skip is not allowed in test mode\n");
}
}
......@@ -376,7 +377,7 @@ int do_it()
if(option_values.sector_align) {
if(option_values.mode_decode)
return usage_error("ERROR: --sector-align only allowed for encoding\n");
if(option_values.skip > 0)
if(0 != option_values.skip_specification)
return usage_error("ERROR: --sector-align not allowed with --skip\n");
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");
......@@ -534,7 +535,7 @@ FLAC__bool init_options()
option_values.padding = 4096;
option_values.max_lpc_order = 8;
option_values.qlp_coeff_precision = 0;
option_values.skip = 0;
option_values.skip_specification = 0;
option_values.format_is_big_endian = -1;
option_values.format_is_unsigned_samples = -1;
option_values.format_channels = -1;
......@@ -619,7 +620,7 @@ int parse_option(int short_option, const char *long_option, const char *option_a
}
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 */
option_values.skip_specification = option_argument;
}
else if(0 == strcmp(long_option, "cuesheet")) {
FLAC__ASSERT(0 != option_argument);
......@@ -1082,7 +1083,7 @@ void show_help()
printf(" -o, --output-name=FILENAME Force the output file name\n");
printf(" --output-prefix=STRING Prepend STRING to output names\n");
printf(" --delete-input-file Deletes after a successful encode/decode\n");
printf(" --skip=# Skip the first # samples of each input file\n");
printf(" --skip={#|mm:ss.ss} Skip the given initial samples for each input\n");
printf("analysis options:\n");
printf(" --residual-text Include residual signal in text output\n");
printf(" --residual-gnuplot Generate gnuplot files of residual distribution\n");
......@@ -1203,8 +1204,10 @@ void show_explain()
printf(" successful encode or decode. If there was an\n");
printf(" error (including a verify error) the input file\n");
printf(" is left intact.\n");
printf(" --skip=# Skip the first # samples of each input file; can\n");
printf(" be used both for encoding and decoding\n");
printf(" --skip={#|mm:ss.ss} Skip the first # samples of each input file; can\n");
printf(" be used both for encoding and decoding. The\n");
printf(" alternative form mm:ss.ss can be used to specify\n");
printf(" minutes, seconds, and fractions of a second.\n");
printf("analysis options:\n");
printf(" --residual-text Include residual signal in text output. This\n");
printf(" will make the file very big, much larger than\n");
......@@ -1436,8 +1439,10 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
return usage_error("ERROR: --replay-gain cannot be used when encoding to stdout\n");
}
if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &common_options.skip_specification) || common_options.skip_specification.is_relative)
return usage_error("ERROR: invalid value for --skip\n");
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 = option_values.use_ogg;
......@@ -1540,6 +1545,9 @@ int decode_file(const char *infilename)
}
#endif
if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &common_options.skip_specification) || common_options.skip_specification.is_relative)
return usage_error("ERROR: invalid value for --skip\n");
common_options.verbose = option_values.verbose;
common_options.continue_through_decode_errors = option_values.continue_through_decode_errors;
#ifdef FLAC__HAS_OGG
......@@ -1547,7 +1555,6 @@ int decode_file(const char *infilename)
common_options.use_first_serial_number = !option_values.has_serial_number;
common_options.serial_number = option_values.serial_number;
#endif
common_options.skip = option_values.skip;
if(!option_values.force_raw_format) {
wav_decode_options_t options;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment