add support for specifying seek points
authorJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 13 Apr 2001 21:55:17 +0000 (21:55 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 13 Apr 2001 21:55:17 +0000 (21:55 +0000)
src/flac/encode.c
src/flac/encode.h
src/flac/main.c

index 8277362..ad41300 100644 (file)
@@ -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;
index 5b146d0..ea7075a 100644 (file)
@@ -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
index 5639020..4b1a7c5 100644 (file)
@@ -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");