mirror of
https://github.com/claunia/flac.git
synced 2025-12-16 18:54:26 +00:00
add support for specifying seek points
This commit is contained in:
@@ -74,6 +74,7 @@ typedef struct {
|
||||
uint64 samples_written;
|
||||
unsigned current_frame;
|
||||
verify_fifo_struct verify_fifo;
|
||||
FLAC__StreamMetaData_SeekTable seek_table;
|
||||
} encoder_wrapper_struct;
|
||||
|
||||
static bool is_big_endian_host;
|
||||
@@ -88,7 +89,10 @@ static int32 *input[FLAC__MAX_CHANNELS];
|
||||
|
||||
/* local routines */
|
||||
static bool init(encoder_wrapper_struct *encoder_wrapper);
|
||||
static bool init_encoder(bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned channels, unsigned bps, unsigned sample_rate, unsigned padding, encoder_wrapper_struct *encoder_wrapper);
|
||||
static bool init_encoder(bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned channels, unsigned bps, unsigned sample_rate, unsigned padding, char *requested_seek_points, int num_requested_seek_points, encoder_wrapper_struct *encoder_wrapper);
|
||||
static bool convert_to_seek_table(char *requested_seek_points, int num_requested_seek_points, uint64 stream_samples, unsigned blocksize, FLAC__StreamMetaData_SeekTable *seek_table);
|
||||
static void append_point_to_seek_table(FLAC__StreamMetaData_SeekTable *seek_table, uint64 sample, uint64 stream_samples, uint64 blocksize);
|
||||
static int seekpoint_compare(const FLAC__StreamMetaData_SeekPoint *l, const FLAC__StreamMetaData_SeekPoint *r);
|
||||
static void format_input(unsigned wide_samples, bool is_big_endian, bool is_unsigned_samples, unsigned channels, unsigned bps, encoder_wrapper_struct *encoder_wrapper);
|
||||
static FLAC__EncoderWriteStatus write_callback(const FLAC__Encoder *encoder, const byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
|
||||
static void metadata_callback(const FLAC__Encoder *encoder, const FLAC__StreamMetaData *metadata, void *client_data);
|
||||
@@ -100,7 +104,7 @@ static void print_stats(const encoder_wrapper_struct *encoder_wrapper);
|
||||
static bool read_little_endian_uint16(FILE *f, uint16 *val, bool eof_ok);
|
||||
static bool read_little_endian_uint32(FILE *f, uint32 *val, bool eof_ok);
|
||||
|
||||
int encode_wav(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding)
|
||||
int encode_wav(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding, char *requested_seek_points, int num_requested_seek_points)
|
||||
{
|
||||
encoder_wrapper_struct encoder_wrapper;
|
||||
FILE *fin;
|
||||
@@ -252,7 +256,7 @@ int encode_wav(const char *infile, const char *outfile, bool verbose, uint64 ski
|
||||
encoder_wrapper.total_samples_to_encode = data_bytes / bytes_per_wide_sample - skip;
|
||||
encoder_wrapper.unencoded_size = encoder_wrapper.total_samples_to_encode * bytes_per_wide_sample + 44; /* 44 for the size of the WAV headers */
|
||||
|
||||
if(!init_encoder(lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, blocksize, qlp_coeff_precision, channels, bps, sample_rate, padding, &encoder_wrapper))
|
||||
if(!init_encoder(lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, blocksize, qlp_coeff_precision, channels, bps, sample_rate, padding, requested_seek_points, num_requested_seek_points, &encoder_wrapper))
|
||||
goto wav_abort_;
|
||||
|
||||
encoder_wrapper.verify_fifo.into_frames = true;
|
||||
@@ -329,7 +333,7 @@ wav_abort_:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int encode_raw(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding, bool is_big_endian, bool is_unsigned_samples, unsigned channels, unsigned bps, unsigned sample_rate)
|
||||
int encode_raw(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding, char *requested_seek_points, int num_requested_seek_points, bool is_big_endian, bool is_unsigned_samples, unsigned channels, unsigned bps, unsigned sample_rate)
|
||||
{
|
||||
encoder_wrapper_struct encoder_wrapper;
|
||||
FILE *fin;
|
||||
@@ -405,7 +409,7 @@ int encode_raw(const char *infile, const char *outfile, bool verbose, uint64 ski
|
||||
fseek(fin, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
if(!init_encoder(lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, blocksize, qlp_coeff_precision, channels, bps, sample_rate, padding, &encoder_wrapper))
|
||||
if(!init_encoder(lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, blocksize, qlp_coeff_precision, channels, bps, sample_rate, padding, requested_seek_points, num_requested_seek_points, &encoder_wrapper))
|
||||
goto raw_abort_;
|
||||
|
||||
encoder_wrapper.verify_fifo.into_frames = true;
|
||||
@@ -493,7 +497,7 @@ bool init(encoder_wrapper_struct *encoder_wrapper)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init_encoder(bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned channels, unsigned bps, unsigned sample_rate, unsigned padding, encoder_wrapper_struct *encoder_wrapper)
|
||||
bool init_encoder(bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned channels, unsigned bps, unsigned sample_rate, unsigned padding, char *requested_seek_points, int num_requested_seek_points, encoder_wrapper_struct *encoder_wrapper)
|
||||
{
|
||||
if(channels != 2)
|
||||
do_mid_side = loose_mid_side = false;
|
||||
@@ -525,6 +529,11 @@ bool init_encoder(bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhau
|
||||
}
|
||||
}
|
||||
|
||||
if(!convert_to_seek_table(requested_seek_points, num_requested_seek_points, encoder_wrapper->total_samples_to_encode, blocksize, &encoder_wrapper->seek_table)) {
|
||||
fprintf(stderr, "ERROR allocating seek table\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder_wrapper->encoder->streamable_subset = !lax;
|
||||
encoder_wrapper->encoder->channels = channels;
|
||||
encoder_wrapper->encoder->bits_per_sample = bps;
|
||||
@@ -538,6 +547,7 @@ bool init_encoder(bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhau
|
||||
encoder_wrapper->encoder->do_qlp_coeff_prec_search = do_qlp_coeff_prec_search;
|
||||
encoder_wrapper->encoder->rice_optimization_level = rice_optimization_level;
|
||||
encoder_wrapper->encoder->total_samples_estimate = encoder_wrapper->total_samples_to_encode;
|
||||
encoder_wrapper->encoder->seek_table = (encoder_wrapper->seek_table.num_points > 0)? &encoder_wrapper->seek_table : 0;
|
||||
encoder_wrapper->encoder->padding = padding;
|
||||
|
||||
if(FLAC__encoder_init(encoder_wrapper->encoder, write_callback, metadata_callback, encoder_wrapper) != FLAC__ENCODER_OK) {
|
||||
@@ -548,6 +558,113 @@ bool init_encoder(bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhau
|
||||
return true;
|
||||
}
|
||||
|
||||
bool convert_to_seek_table(char *requested_seek_points, int num_requested_seek_points, uint64 stream_samples, unsigned blocksize, FLAC__StreamMetaData_SeekTable *seek_table)
|
||||
{
|
||||
unsigned i, j, real_points, placeholders;
|
||||
char *pt = requested_seek_points, *q;
|
||||
bool first;
|
||||
|
||||
seek_table->num_points = 0;
|
||||
|
||||
if(num_requested_seek_points == 0)
|
||||
return true;
|
||||
|
||||
if(num_requested_seek_points < 0) {
|
||||
strcpy(requested_seek_points, ",100x");
|
||||
num_requested_seek_points = 100;
|
||||
}
|
||||
|
||||
/* first count how many individual seek point we may need */
|
||||
real_points = placeholders = 0;
|
||||
for(i = 0; i < (unsigned)num_requested_seek_points; i++) {
|
||||
q = strchr(pt, ',');
|
||||
assert(0 != q);
|
||||
*q = '\0';
|
||||
|
||||
if(0 == strcmp(pt, "X")) { /* -S X */
|
||||
placeholders++;
|
||||
}
|
||||
else if(pt[strlen(pt)-1] == 'x') { /* -S #x */
|
||||
if(stream_samples > 0) /* we can only do these if we know the number of samples to encode up front */
|
||||
real_points += (unsigned)atoi(pt);
|
||||
}
|
||||
else { /* -S # */
|
||||
real_points++;
|
||||
}
|
||||
*q++ = ',';
|
||||
|
||||
pt = q;
|
||||
}
|
||||
|
||||
/* make some space */
|
||||
if(0 == (seek_table->points = (FLAC__StreamMetaData_SeekPoint*)malloc(sizeof(FLAC__StreamMetaData_SeekPoint) * (real_points+placeholders))))
|
||||
return false;
|
||||
|
||||
for(i = 0; i < (unsigned)num_requested_seek_points; i++) {
|
||||
q = strchr(pt, ',');
|
||||
assert(0 != q);
|
||||
*q++ = '\0';
|
||||
|
||||
if(0 == strcmp(pt, "X")) { /* -S X */
|
||||
placeholders++;
|
||||
}
|
||||
else if(pt[strlen(pt)-1] == 'x') { /* -S #x */
|
||||
if(stream_samples > 0) { /* we can only do these if we know the number of samples to encode up front */
|
||||
unsigned j, n;
|
||||
n = (unsigned)atoi(pt);
|
||||
for(j = 0; j < n; j++)
|
||||
append_point_to_seek_table(seek_table, stream_samples * (uint64)j / (uint64)n, stream_samples, blocksize);
|
||||
}
|
||||
}
|
||||
else { /* -S # */
|
||||
append_point_to_seek_table(seek_table, (uint64)atoi(pt), stream_samples, blocksize);
|
||||
}
|
||||
|
||||
pt = q;
|
||||
}
|
||||
|
||||
/* sort the seekpoints */
|
||||
qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetaData_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare);
|
||||
|
||||
/* uniqify the seekpoints */
|
||||
first = false;
|
||||
for(i = j = 0; i < seek_table->num_points; i++) {
|
||||
if(!first) {
|
||||
if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number)
|
||||
continue;
|
||||
}
|
||||
first = false;
|
||||
seek_table->points[j++] = seek_table->points[i];
|
||||
}
|
||||
seek_table->num_points = j;
|
||||
|
||||
/* append placeholders */
|
||||
for(i = 0, j = seek_table->num_points; i < placeholders; i++, j++)
|
||||
seek_table->points[j].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
|
||||
seek_table->num_points += placeholders;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void append_point_to_seek_table(FLAC__StreamMetaData_SeekTable *seek_table, uint64 sample, uint64 stream_samples, uint64 blocksize)
|
||||
{
|
||||
const uint64 target_sample = (sample / blocksize) * blocksize;
|
||||
|
||||
if(stream_samples == 0 || target_sample < stream_samples)
|
||||
seek_table->points[seek_table->num_points++].sample_number = target_sample;
|
||||
}
|
||||
|
||||
int seekpoint_compare(const FLAC__StreamMetaData_SeekPoint *l, const FLAC__StreamMetaData_SeekPoint *r)
|
||||
{
|
||||
/* we don't just 'return l->sample_number - r->sample_number' since the result (int64) might overflow an 'int' */
|
||||
if(l->sample_number == r->sample_number)
|
||||
return 0;
|
||||
else if(l->sample_number < r->sample_number)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
void format_input(unsigned wide_samples, bool is_big_endian, bool is_unsigned_samples, unsigned channels, unsigned bps, encoder_wrapper_struct *encoder_wrapper)
|
||||
{
|
||||
unsigned wide_sample, sample, channel, byte;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "FLAC/ordinals.h"
|
||||
|
||||
int encode_wav(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding);
|
||||
int encode_raw(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding, bool is_big_endian, bool is_unsigned_samples, unsigned channels, unsigned bps, unsigned sample_rate);
|
||||
int encode_wav(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding, char *requested_seek_points, int num_requested_seek_points);
|
||||
int encode_raw(const char *infile, const char *outfile, bool verbose, uint64 skip, bool verify, bool lax, bool do_mid_side, bool loose_mid_side, bool do_exhaustive_model_search, bool do_qlp_coeff_prec_search, unsigned rice_optimization_level, unsigned max_lpc_order, unsigned blocksize, unsigned qlp_coeff_precision, unsigned padding, char *requested_seek_points, int num_requested_seek_points, bool is_big_endian, bool is_unsigned_samples, unsigned channels, unsigned bps, unsigned sample_rate);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,6 +42,8 @@ int main(int argc, char *argv[])
|
||||
int format_is_wave = -1, format_is_big_endian = -1, format_is_unsigned_samples = false;
|
||||
int format_channels = -1, format_bps = -1, format_sample_rate = -1;
|
||||
int blocksize = -1, rice_optimization_level = -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 */
|
||||
|
||||
aopts.do_residual_text = false;
|
||||
aopts.do_residual_gnuplot = false;
|
||||
@@ -67,6 +69,17 @@ int main(int argc, char *argv[])
|
||||
verbose = false;
|
||||
else if(0 == strcmp(argv[i], "-s-"))
|
||||
verbose = true;
|
||||
else if(0 == strcmp(argv[i], "-S")) {
|
||||
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], "--skip"))
|
||||
skip = (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"))
|
||||
@@ -290,9 +303,9 @@ int main(int argc, char *argv[])
|
||||
return decode_raw(argv[i], test_only? 0 : argv[i+1], analyze, aopts, verbose, skip, format_is_big_endian, format_is_unsigned_samples);
|
||||
else
|
||||
if(format_is_wave)
|
||||
return encode_wav(argv[i], argv[i+1], verbose, skip, verify, lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, (unsigned)blocksize, qlp_coeff_precision, padding);
|
||||
return encode_wav(argv[i], argv[i+1], verbose, skip, verify, lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, (unsigned)blocksize, qlp_coeff_precision, padding, requested_seek_points, num_requested_seek_points);
|
||||
else
|
||||
return encode_raw(argv[i], argv[i+1], verbose, skip, verify, lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, (unsigned)blocksize, qlp_coeff_precision, padding, format_is_big_endian, format_is_unsigned_samples, format_channels, format_bps, format_sample_rate);
|
||||
return encode_raw(argv[i], argv[i+1], verbose, skip, verify, lax, do_mid_side, loose_mid_side, do_exhaustive_model_search, do_qlp_coeff_prec_search, rice_optimization_level, max_lpc_order, (unsigned)blocksize, qlp_coeff_precision, padding, requested_seek_points, num_requested_seek_points, format_is_big_endian, format_is_unsigned_samples, format_channels, format_bps, format_sample_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -353,7 +366,16 @@ int usage(const char *message, ...)
|
||||
printf(" --a-rgp : generate gnuplot files of residual distribution of each subframe\n");
|
||||
printf("encoding options:\n");
|
||||
printf(" --lax : allow encoder to generate non-Subset files\n");
|
||||
printf(" -P bytes : write a PADDING block of the given length (0 => no PADDING block, default is -P 0)\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 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 before starting.\n");
|
||||
printf(" Note: if you use -S # and # is >= samples in the input, there will be either no seek point entered (if the input size\n");
|
||||
printf(" is determinable) or a placeholder point (if input size is not determinable)\n");
|
||||
printf(" -P bytes : write a PADDING block of the given length (goes after SEEKTABLE) (0 => no PADDING block, default is -P 0)\n");
|
||||
printf(" -b blocksize : default is 1152 for -l 0, else 4608; should be 192/576/1152/2304/4608 (unless --lax is used)\n");
|
||||
printf(" -m : try mid-side coding for each frame (stereo input only)\n");
|
||||
printf(" -M : loose mid-side coding for all frames (stereo input only)\n");
|
||||
@@ -375,7 +397,7 @@ int usage(const char *message, ...)
|
||||
printf(" -q bits : precision of the quantized linear-predictor coefficients, 0 => let encoder decide (min is %u, default is -q 0)\n", FLAC__MIN_QLP_COEFF_PRECISION);
|
||||
printf(" -r level : rice parameter optimization level (level is 0..99, 0 => none, default is -r 0, above 4 doesn't usually help much)\n");
|
||||
printf(" -V : verify a correct encoding by decoding the output in parallel and comparing to the original\n");
|
||||
printf(" -m-, -M-, -e-, -p-, -V-, --lax- can all be used to turn off a particular option\n");
|
||||
printf(" -S-, -m-, -M-, -e-, -p-, -V-, --lax- 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");
|
||||
|
||||
Reference in New Issue
Block a user