about summary refs log tree commit
path: root/src/flac
diff options
context:
space:
mode:
Diffstat (limited to 'src/flac')
-rw-r--r--src/flac/encode.c534
-rw-r--r--src/flac/encode.h7
-rw-r--r--src/flac/main.c119
3 files changed, 598 insertions, 62 deletions
diff --git a/src/flac/encode.c b/src/flac/encode.c
index 6528c7a4..8bafa5bd 100644
--- a/src/flac/encode.c
+++ b/src/flac/encode.c
@@ -103,6 +103,19 @@ typedef struct {
         FLAC__StreamMetadata *seek_table_template;
 } EncoderSession;
 
+/* this is data attached to the FLAC decoder when encoding from a FLAC file */
+typedef struct {
+        EncoderSession *encoder_session;
+        off_t filesize;
+        const FLAC__byte *lookahead;
+        unsigned lookahead_length;
+        size_t num_metadata_blocks;
+        FLAC__StreamMetadata *metadata_blocks[1024]; /*@@@ BAD MAGIC number */
+        FLAC__uint64 samples_left_to_process;
+        FLAC__bool fatal_error;
+} FLACDecoderData;
+
+const int FLAC_ENCODE__DEFAULT_PADDING = 4096;
 
 static FLAC__bool is_big_endian_host_;
 
@@ -140,7 +153,7 @@ static FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg
 static void EncoderSession_destroy(EncoderSession *e);
 static int EncoderSession_finish_ok(EncoderSession *e, int info_align_carry, int info_align_zero);
 static int EncoderSession_finish_error(EncoderSession *e);
-static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate);
+static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate, FLACDecoderData *flac_decoder_data);
 static FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const buffer[], unsigned samples);
 static FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e);
 static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, unsigned sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input);
@@ -153,7 +166,15 @@ static void ogg_file_encoder_progress_callback(const OggFLAC__FileEncoder *encod
 static FLAC__StreamEncoderWriteStatus flac_stream_encoder_write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
 static void flac_stream_encoder_metadata_callback(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data);
 static void flac_file_encoder_progress_callback(const FLAC__FileEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data);
-static FLAC__bool parse_cuesheet_(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset);
+static FLAC__SeekableStreamDecoderReadStatus flac_decoder_read_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
+static FLAC__SeekableStreamDecoderSeekStatus flac_decoder_seek_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+static FLAC__SeekableStreamDecoderTellStatus flac_decoder_tell_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+static FLAC__SeekableStreamDecoderLengthStatus flac_decoder_length_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
+static FLAC__bool flac_decoder_eof_callback(const FLAC__SeekableStreamDecoder *decoder, void *client_data);
+static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+static void flac_decoder_metadata_callback(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static void flac_decoder_error_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+static FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset);
 static void print_stats(const EncoderSession *encoder_session);
 static void print_error_with_state(const EncoderSession *e, const char *message);
 static void print_verify_error(EncoderSession *e);
@@ -386,7 +407,7 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
                         /* +54 for the size of the AIFF headers; this is just an estimate for the progress indicator and doesn't need to be exact */
                         encoder_session.unencoded_size= encoder_session.total_samples_to_encode*bytes_per_frame+54;
 
-                        if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate))
+                        if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate, /*flac_decoder_data=*/0))
                                 return EncoderSession_finish_error(&encoder_session);
 
                         /* first do any samples in the reservoir */
@@ -714,7 +735,7 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
                         /* +44 for the size of the WAV headers; this is just an estimate for the progress indicator and doesn't need to be exact */
                         encoder_session.unencoded_size = encoder_session.total_samples_to_encode * bytes_per_wide_sample + 44;
 
-                        if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate))
+                        if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate, /*flac_decoder_data=*/0))
                                 return EncoderSession_finish_error(&encoder_session);
 
                         /*
@@ -963,7 +984,7 @@ int flac__encode_raw(FILE *infile, off_t infilesize, const char *infilename, con
                 }
         }
 
-        if(!EncoderSession_init_encoder(&encoder_session, options.common, options.channels, options.bps, options.sample_rate))
+        if(!EncoderSession_init_encoder(&encoder_session, options.common, options.channels, options.bps, options.sample_rate, /*flac_decoder_data=*/0))
                 return EncoderSession_finish_error(&encoder_session);
 
         /*
@@ -1036,7 +1057,7 @@ int flac__encode_raw(FILE *infile, off_t infilesize, const char *infilename, con
                 while(total_input_bytes_read < max_input_bytes) {
                         {
                                 size_t wanted = (CHUNK_OF_SAMPLES * bytes_per_wide_sample);
-                                wanted = (size_t) min((off_t)wanted, max_input_bytes - total_input_bytes_read);
+                                wanted = (size_t) min((FLAC__uint64)wanted, max_input_bytes - total_input_bytes_read);
 
                                 if(lookahead_length > 0) {
                                         FLAC__ASSERT(lookahead_length <= wanted);
@@ -1126,6 +1147,163 @@ int flac__encode_raw(FILE *infile, off_t infilesize, const char *infilename, con
         return EncoderSession_finish_ok(&encoder_session, info_align_carry, info_align_zero);
 }
 
+int flac__encode_flac(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, flac_encode_options_t options)
+{
+        EncoderSession encoder_session;
+        FLAC__SeekableStreamDecoder *decoder = 0;
+        FLACDecoderData decoder_data;
+        size_t i;
+        int retval;
+
+        if(!
+                EncoderSession_construct(
+                        &encoder_session,
+#ifdef FLAC__HAS_OGG
+                        options.common.use_ogg,
+#else
+                        /*use_ogg=*/false,
+#endif
+                        options.common.verify,
+                        infile,
+                        infilename,
+                        outfilename
+                )
+        )
+                return 1;
+
+        decoder_data.encoder_session = &encoder_session;
+        decoder_data.filesize = (infilesize == (off_t)(-1)? 0 : infilesize);
+        decoder_data.lookahead = lookahead;
+        decoder_data.lookahead_length = lookahead_length;
+        decoder_data.num_metadata_blocks = 0;
+        decoder_data.samples_left_to_process = 0;
+        decoder_data.fatal_error = false;
+
+        /*
+         * set up FLAC decoder for the input
+         */
+        if (0 == (decoder = FLAC__seekable_stream_decoder_new())) {
+                flac__utils_printf(stderr, 1, "%s: ERROR: creating decoder for FLAC input\n", encoder_session.inbasefilename);
+                return EncoderSession_finish_error(&encoder_session);
+        }
+        if (!(
+                FLAC__seekable_stream_decoder_set_md5_checking(decoder, false) &&
+                FLAC__seekable_stream_decoder_set_read_callback(decoder, flac_decoder_read_callback) &&
+                FLAC__seekable_stream_decoder_set_seek_callback(decoder, flac_decoder_seek_callback) &&
+                FLAC__seekable_stream_decoder_set_tell_callback(decoder, flac_decoder_tell_callback) &&
+                FLAC__seekable_stream_decoder_set_length_callback(decoder, flac_decoder_length_callback) &&
+                FLAC__seekable_stream_decoder_set_eof_callback(decoder, flac_decoder_eof_callback) &&
+                FLAC__seekable_stream_decoder_set_write_callback(decoder, flac_decoder_write_callback) &&
+                FLAC__seekable_stream_decoder_set_metadata_callback(decoder, flac_decoder_metadata_callback) &&
+                FLAC__seekable_stream_decoder_set_error_callback(decoder, flac_decoder_error_callback) &&
+                FLAC__seekable_stream_decoder_set_client_data(decoder, &decoder_data) &&
+                FLAC__seekable_stream_decoder_set_metadata_respond_all(decoder)
+        )) {
+                flac__utils_printf(stderr, 1, "%s: ERROR: setting up decoder for FLAC input\n", encoder_session.inbasefilename);
+                goto fubar1; /*@@@ yuck */
+        }
+
+        if (FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) {
+                flac__utils_printf(stderr, 1, "%s: ERROR: initializing decoder for FLAC input, state = %s\n", encoder_session.inbasefilename, FLAC__seekable_stream_decoder_get_resolved_state_string(decoder));
+                goto fubar1; /*@@@ yuck */
+        }
+
+        if (!FLAC__seekable_stream_decoder_process_until_end_of_metadata(decoder) || decoder_data.fatal_error) {
+                if (decoder_data.fatal_error)
+                        flac__utils_printf(stderr, 1, "%s: ERROR: out of memory or too many metadata blocks while reading metadata in FLAC input\n", encoder_session.inbasefilename);
+                else
+                        flac__utils_printf(stderr, 1, "%s: ERROR: reading metadata in FLAC input, state = %s\n", encoder_session.inbasefilename, FLAC__seekable_stream_decoder_get_resolved_state_string(decoder));
+                goto fubar1; /*@@@ yuck */
+        }
+
+        if (decoder_data.num_metadata_blocks == 0) {
+                flac__utils_printf(stderr, 1, "%s: ERROR: reading metadata in FLAC input, got no metadata blocks\n", encoder_session.inbasefilename);
+                goto fubar2; /*@@@ yuck */
+        }
+        else if (decoder_data.metadata_blocks[0]->type != FLAC__METADATA_TYPE_STREAMINFO) {
+                flac__utils_printf(stderr, 1, "%s: ERROR: reading metadata in FLAC input, first metadata block is not STREAMINFO\n", encoder_session.inbasefilename);
+                goto fubar2; /*@@@ yuck */
+        }
+        else if (decoder_data.metadata_blocks[0]->data.stream_info.total_samples == 0) {
+                flac__utils_printf(stderr, 1, "%s: ERROR: FLAC input has STREAMINFO with unknown total samples which is not supported\n", encoder_session.inbasefilename);
+                goto fubar2; /*@@@ yuck */
+        }
+
+        /*
+         * now that we have the STREAMINFO and know the sample rate,
+         * canonicalize the --skip string to a number of samples:
+         */
+        flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, decoder_data.metadata_blocks[0]->data.stream_info.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); /* --sector-align with FLAC input is not supported */
+
+        {
+                FLAC__uint64 total_samples_in_input, trim = 0;
+
+                total_samples_in_input = decoder_data.metadata_blocks[0]->data.stream_info.total_samples;
+
+                /*
+                 * now that we know the input size, canonicalize the
+                 * --until string to an absolute sample number:
+                 */
+                if(!canonicalize_until_specification(&options.common.until_specification, encoder_session.inbasefilename, decoder_data.metadata_blocks[0]->data.stream_info.sample_rate, encoder_session.skip, total_samples_in_input))
+                        goto fubar2; /*@@@ yuck */
+                encoder_session.until = (FLAC__uint64)options.common.until_specification.value.samples;
+
+                encoder_session.total_samples_to_encode = total_samples_in_input - encoder_session.skip;
+                if(encoder_session.until > 0) {
+                        trim = total_samples_in_input - encoder_session.until;
+                        FLAC__ASSERT(total_samples_in_input > 0);
+                        encoder_session.total_samples_to_encode -= trim;
+                }
+
+                encoder_session.unencoded_size = decoder_data.filesize;
+
+                if(!EncoderSession_init_encoder(&encoder_session, options.common, decoder_data.metadata_blocks[0]->data.stream_info.channels, decoder_data.metadata_blocks[0]->data.stream_info.bits_per_sample, decoder_data.metadata_blocks[0]->data.stream_info.sample_rate, &decoder_data))
+                        return EncoderSession_finish_error(&encoder_session);
+
+                /*
+                 * have to wait until the FLAC encoder is set up for writing
+                 * before any seeking in the input FLAC file, because the seek
+                 * itself will usually call the decoder's write callback, and
+                 * our decoder's write callback passes samples to our FLAC
+                 * encoder
+                 */
+                decoder_data.samples_left_to_process = encoder_session.total_samples_to_encode;
+                if(encoder_session.skip > 0) {
+                        if(!FLAC__seekable_stream_decoder_seek_absolute(decoder, encoder_session.skip)) {
+                                flac__utils_printf(stderr, 1, "%s: ERROR while skipping samples, FLAC decoder state = %s\n", encoder_session.inbasefilename, FLAC__seekable_stream_decoder_get_resolved_state_string(decoder));
+                                goto fubar2; /*@@@ yuck */
+                        }
+                }
+
+                /*
+                 * now do samples from the file
+                 */
+                while(!decoder_data.fatal_error && decoder_data.samples_left_to_process > 0) {
+                        if(!FLAC__seekable_stream_decoder_process_single(decoder)) {
+                                flac__utils_printf(stderr, 1, "%s: ERROR: while decoding FLAC input, state = %s\n", encoder_session.inbasefilename, FLAC__seekable_stream_decoder_get_resolved_state_string(decoder));
+                                goto fubar2; /*@@@ yuck */
+                        }
+                }
+        }
+
+        FLAC__seekable_stream_decoder_delete(decoder);
+        retval = EncoderSession_finish_ok(&encoder_session, -1, -1);
+        /* have to wail until encoder is completely finished before deleting because of the final step of writing the seekpoint offsets */
+        for(i = 0; i < decoder_data.num_metadata_blocks; i++)
+                free(decoder_data.metadata_blocks[i]);
+        return retval;
+
+fubar2:
+        for(i = 0; i < decoder_data.num_metadata_blocks; i++)
+                free(decoder_data.metadata_blocks[i]);
+fubar1:
+        FLAC__seekable_stream_decoder_delete(decoder);
+        return EncoderSession_finish_error(&encoder_session);
+}
+
 FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg, FLAC__bool verify, FILE *infile, const char *infilename, const char *outfilename)
 {
         unsigned i;
@@ -1355,6 +1533,7 @@ int EncoderSession_finish_error(EncoderSession *e)
         if(fse_state == FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA)
                 print_verify_error(e);
         else
+                /*@@@@@@@@@ BUG: if error was caused because the output file already exists but the file encoder could not write on top of it (i.e. it's not writable), this will delete the pre-existing file, which is not what we want */
                 unlink(e->outfilename);
 
         EncoderSession_destroy(e);
@@ -1362,11 +1541,12 @@ int EncoderSession_finish_error(EncoderSession *e)
         return 1;
 }
 
-FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate)
+FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate, FLACDecoderData *flac_decoder_data)
 {
         unsigned num_metadata;
         FLAC__StreamMetadata padding, *cuesheet = 0;
-        FLAC__StreamMetadata *metadata[4];
+        FLAC__StreamMetadata *static_metadata[4];
+        FLAC__StreamMetadata **metadata = static_metadata;
         const FLAC__bool is_cdda = (channels == 1 || channels == 2) && (bps == 16) && (sample_rate == 44100);
 
         e->replay_gain = options.replay_gain;
@@ -1394,7 +1574,7 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio
         if(channels != 2)
                 options.do_mid_side = options.loose_mid_side = false;
 
-        if(!parse_cuesheet_(&cuesheet, options.cuesheet_filename, e->inbasefilename, is_cdda, e->total_samples_to_encode))
+        if(!parse_cuesheet(&cuesheet, options.cuesheet_filename, e->inbasefilename, is_cdda, e->total_samples_to_encode))
                 return false;
 
         if(!convert_to_seek_table_template(options.requested_seek_points, options.num_requested_seek_points, options.cued_seekpoints? cuesheet : 0, e)) {
@@ -1404,19 +1584,206 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio
                 return false;
         }
 
-        num_metadata = 0;
-        if(e->seek_table_template->data.seek_table.num_points > 0) {
-                e->seek_table_template->is_last = false; /* the encoder will set this for us */
-                metadata[num_metadata++] = e->seek_table_template;
+        if(flac_decoder_data) {
+                /*
+                 * we're encoding from FLAC so we will use the FLAC file's
+                 * metadata as the basic for the encoded file
+                 */
+                {
+                        /*
+                         * first handle padding: if --no-padding was specified,
+                         * then delete all padding; else if -P was specified,
+                         * use that instead of existing padding (if any); else
+                         * if existing file has padding, move all existing
+                         * padding blocks to one padding block at the end; else
+                         * use default padding.
+                         */
+                        int p = -1;
+                        size_t i, j;
+                        for(i = 0, j = 0; i < flac_decoder_data->num_metadata_blocks; i++) {
+                                if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_PADDING) {
+                                        if(p < 0)
+                                                p = 0;
+                                        p += flac_decoder_data->metadata_blocks[i]->length;
+                                        FLAC__metadata_object_delete(flac_decoder_data->metadata_blocks[i]);
+                                        flac_decoder_data->metadata_blocks[i] = 0;
+                                }
+                                else
+                                        flac_decoder_data->metadata_blocks[j++] = flac_decoder_data->metadata_blocks[i];
+                        }
+                        flac_decoder_data->num_metadata_blocks = j;
+                        if(options.padding > 0)
+                                p = options.padding;
+                        if(p < 0)
+                                p = FLAC_ENCODE__DEFAULT_PADDING;
+                        if(options.padding != 0) {
+                                if(p > 0 && flac_decoder_data->num_metadata_blocks < sizeof(flac_decoder_data->metadata_blocks)/sizeof(flac_decoder_data->metadata_blocks[0])) {
+                                        flac_decoder_data->metadata_blocks[flac_decoder_data->num_metadata_blocks] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+                                        if(0 == flac_decoder_data->metadata_blocks[flac_decoder_data->num_metadata_blocks]) {
+                                                flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for PADDING block\n", e->inbasefilename);
+                                                if(0 != cuesheet)
+                                                        FLAC__metadata_object_delete(cuesheet);
+                                                return false;
+                                        }
+                                        flac_decoder_data->metadata_blocks[flac_decoder_data->num_metadata_blocks]->is_last = false; /* the encoder will set this for us */
+                                        flac_decoder_data->metadata_blocks[flac_decoder_data->num_metadata_blocks]->length = p;
+                                        flac_decoder_data->num_metadata_blocks++;
+                                }
+                        }
+                }
+                {
+                        /*
+                         * next handle vorbis comment: if any tags were specified
+                         * or there is no existing vorbis comment, we create a
+                         * new vorbis comment (discarding any existing one); else
+                         * we keep the existing one
+                         */
+                        size_t i, j;
+                        FLAC__bool vc_found = false;
+                        for(i = 0, j = 0; i < flac_decoder_data->num_metadata_blocks; i++) {
+                                if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+                                        vc_found = true;
+                                if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT && options.vorbis_comment->data.vorbis_comment.num_comments > 0) {
+                                        if(options.vorbis_comment->data.vorbis_comment.num_comments > 0)
+                                                flac__utils_printf(stderr, 1, "%s: WARNING, replacing tags from input FLAC file with those given on the command-line\n", e->inbasefilename);
+                                        FLAC__metadata_object_delete(flac_decoder_data->metadata_blocks[i]);
+                                        flac_decoder_data->metadata_blocks[i] = 0;
+                                }
+                                else
+                                        flac_decoder_data->metadata_blocks[j++] = flac_decoder_data->metadata_blocks[i];
+                        }
+                        flac_decoder_data->num_metadata_blocks = j;
+                        if((!vc_found || options.vorbis_comment->data.vorbis_comment.num_comments > 0) && flac_decoder_data->num_metadata_blocks < sizeof(flac_decoder_data->metadata_blocks)/sizeof(flac_decoder_data->metadata_blocks[0])) {
+                                /* prepend ours */
+                                FLAC__StreamMetadata *vc = FLAC__metadata_object_clone(options.vorbis_comment);
+                                if(0 == vc) {
+                                        flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for VORBIS_COMMENT block\n", e->inbasefilename);
+                                        if(0 != cuesheet)
+                                                FLAC__metadata_object_delete(cuesheet);
+                                        return false;
+                                }
+                                for(i = flac_decoder_data->num_metadata_blocks; i > 1; i--)
+                                        flac_decoder_data->metadata_blocks[i] = flac_decoder_data->metadata_blocks[i-1];
+                                flac_decoder_data->metadata_blocks[1] = vc;
+                                flac_decoder_data->num_metadata_blocks++;
+                        }
+                }
+                {
+                        /*
+                         * next handle cuesheet: if --cuesheet was specified, use
+                         * it; else if file has existing CUESHEET and cuesheet's
+                         * lead-out offset is correct, keep it; else no CUESHEET
+                         */
+                        size_t i, j;
+                        for(i = 0, j = 0; i < flac_decoder_data->num_metadata_blocks; i++) {
+                                FLAC__bool existing_cuesheet_is_bad = false;
+                                /* check if existing cuesheet matches the input audio */
+                                if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_CUESHEET && 0 == cuesheet) {
+                                        const FLAC__StreamMetadata_CueSheet *cs = &flac_decoder_data->metadata_blocks[i]->data.cue_sheet;
+                                        if(e->total_samples_to_encode == 0) {
+                                                flac__utils_printf(stderr, 1, "%s: WARNING, cuesheet in input FLAC file cannot be kept if input size is not known, dropping it...\n", e->inbasefilename);
+                                                existing_cuesheet_is_bad = true;
+                                        }
+                                        else if(e->total_samples_to_encode != cs->tracks[cs->num_tracks-1].offset) {
+                                                flac__utils_printf(stderr, 1, "%s: WARNING, lead-out offset of cuesheet in input FLAC file does not match input length, dropping existing cuesheet...\n", e->inbasefilename);
+                                                existing_cuesheet_is_bad = true;
+                                        }
+                                }
+                                if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_CUESHEET && (existing_cuesheet_is_bad || 0 != cuesheet)) {
+                                        if(0 != cuesheet)
+                                                flac__utils_printf(stderr, 1, "%s: WARNING, replacing cuesheet in input FLAC file with the one given on the command-line\n", e->inbasefilename);
+                                        FLAC__metadata_object_delete(flac_decoder_data->metadata_blocks[i]);
+                                        flac_decoder_data->metadata_blocks[i] = 0;
+                                }
+                                else
+                                        flac_decoder_data->metadata_blocks[j++] = flac_decoder_data->metadata_blocks[i];
+                        }
+                        flac_decoder_data->num_metadata_blocks = j;
+                        if(0 != cuesheet && flac_decoder_data->num_metadata_blocks < sizeof(flac_decoder_data->metadata_blocks)/sizeof(flac_decoder_data->metadata_blocks[0])) {
+                                /* prepend ours */
+                                FLAC__StreamMetadata *cs = FLAC__metadata_object_clone(cuesheet);
+                                if(0 == cs) {
+                                        flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for CUESHEET block\n", e->inbasefilename);
+                                        if(0 != cuesheet)
+                                                FLAC__metadata_object_delete(cuesheet);
+                                        return false;
+                                }
+                                for(i = flac_decoder_data->num_metadata_blocks; i > 1; i--)
+                                        flac_decoder_data->metadata_blocks[i] = flac_decoder_data->metadata_blocks[i-1];
+                                flac_decoder_data->metadata_blocks[1] = cs;
+                                flac_decoder_data->num_metadata_blocks++;
+                        }
+                }
+                {
+                        /*
+                         * finally handle seektable: if -S- was specified, no
+                         * SEEKTABLE; else if -S was specified, use it/them;
+                         * else if file has existing SEEKTABLE and input size is
+                         * preserved (no --skip/--until/etc specified), keep it;
+                         * else use default seektable options
+                         *
+                         * note: meanings of num_requested_seek_points:
+                         *  -1 : no -S option given, default to some value
+                         *   0 : -S- given (no seektable)
+                         *  >0 : one or more -S options given
+                         */
+                        size_t i, j;
+                        FLAC__bool existing_seektable = false;
+                        for(i = 0, j = 0; i < flac_decoder_data->num_metadata_blocks; i++) {
+                                if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_SEEKTABLE)
+                                        existing_seektable = true;
+                                if(flac_decoder_data->metadata_blocks[i]->type == FLAC__METADATA_TYPE_SEEKTABLE && (e->total_samples_to_encode != flac_decoder_data->metadata_blocks[0]->data.stream_info.total_samples || options.num_requested_seek_points >= 0)) {
+                                        if(options.num_requested_seek_points > 0)
+                                                flac__utils_printf(stderr, 1, "%s: WARNING, replacing seektable in input FLAC file with the one given on the command-line\n", e->inbasefilename);
+                                        else if(options.num_requested_seek_points == 0)
+                                                ; /* no warning, silently delete existing SEEKTABLE since user specified --no-seektable (-S-) */
+                                        else
+                                                flac__utils_printf(stderr, 1, "%s: WARNING, can't use existing seektable in input FLAC since the input size is changing or unknown, dropping existing SEEKTABLE block...\n", e->inbasefilename);
+                                        FLAC__metadata_object_delete(flac_decoder_data->metadata_blocks[i]);
+                                        flac_decoder_data->metadata_blocks[i] = 0;
+                                        existing_seektable = false;
+                                }
+                                else
+                                        flac_decoder_data->metadata_blocks[j++] = flac_decoder_data->metadata_blocks[i];
+                        }
+                        flac_decoder_data->num_metadata_blocks = j;
+                        if((options.num_requested_seek_points > 0 || (options.num_requested_seek_points < 0 && !existing_seektable)) && flac_decoder_data->num_metadata_blocks < sizeof(flac_decoder_data->metadata_blocks)/sizeof(flac_decoder_data->metadata_blocks[0])) {
+                                /* prepend ours */
+                                FLAC__StreamMetadata *st = FLAC__metadata_object_clone(e->seek_table_template);
+                                if(0 == st) {
+                                        flac__utils_printf(stderr, 1, "%s: ERROR allocating memory for SEEKTABLE block\n", e->inbasefilename);
+                                        if(0 != cuesheet)
+                                                FLAC__metadata_object_delete(cuesheet);
+                                        return false;
+                                }
+                                for(i = flac_decoder_data->num_metadata_blocks; i > 1; i--)
+                                        flac_decoder_data->metadata_blocks[i] = flac_decoder_data->metadata_blocks[i-1];
+                                flac_decoder_data->metadata_blocks[1] = st;
+                                flac_decoder_data->num_metadata_blocks++;
+                        }
+                }
+                metadata = &flac_decoder_data->metadata_blocks[1]; /* don't include STREAMINFO */
+                num_metadata = flac_decoder_data->num_metadata_blocks - 1;
         }
-        if(0 != cuesheet)
-                metadata[num_metadata++] = cuesheet;
-        metadata[num_metadata++] = options.vorbis_comment;
-        if(options.padding > 0) {
-                padding.is_last = false; /* the encoder will set this for us */
-                padding.type = FLAC__METADATA_TYPE_PADDING;
-                padding.length = (unsigned)options.padding;
-                metadata[num_metadata++] = &padding;
+        else {
+                /*
+                 * we're not encoding from FLAC so we will build the metadata
+                 * from scratch
+                 */
+                num_metadata = 0;
+                if(e->seek_table_template->data.seek_table.num_points > 0) {
+                        e->seek_table_template->is_last = false; /* the encoder will set this for us */
+                        metadata[num_metadata++] = e->seek_table_template;
+                }
+                if(0 != cuesheet)
+                        metadata[num_metadata++] = cuesheet;
+                metadata[num_metadata++] = options.vorbis_comment;
+                if(options.padding != 0) {
+                        padding.is_last = false; /* the encoder will set this for us */
+                        padding.type = FLAC__METADATA_TYPE_PADDING;
+                        padding.length = (unsigned)(options.padding>0? options.padding : FLAC_ENCODE__DEFAULT_PADDING);
+                        metadata[num_metadata++] = &padding;
+                }
         }
 
         e->blocksize = options.blocksize;
@@ -1850,7 +2217,122 @@ void flac_file_encoder_progress_callback(const FLAC__FileEncoder *encoder, FLAC_
                 print_stats(encoder_session);
 }
 
-FLAC__bool parse_cuesheet_(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
+FLAC__SeekableStreamDecoderReadStatus flac_decoder_read_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+{
+        size_t n = 0;
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        (void)decoder;
+
+        if (data->fatal_error)
+                return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
+
+        /* use up lookahead first */
+        if (data->lookahead_length) {
+                n = min(data->lookahead_length, *bytes);
+                memcpy(buffer, data->lookahead, n);
+                buffer += n;
+                data->lookahead += n;
+                data->lookahead_length -= n;
+        }
+
+        /* get the rest from file */
+        if (*bytes > n) {
+                *bytes = n + fread(buffer, 1, *bytes-n, data->encoder_session->fin);
+                return ferror(data->encoder_session->fin)? FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR : FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
+        }
+        else
+                return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
+}
+
+FLAC__SeekableStreamDecoderSeekStatus flac_decoder_seek_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        (void)decoder;
+
+        if(fseeko(data->encoder_session->fin, (off_t)absolute_byte_offset, SEEK_SET) < 0)
+                return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
+        else
+                return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__SeekableStreamDecoderTellStatus flac_decoder_tell_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        off_t pos;
+        (void)decoder;
+
+        if((pos = ftello(data->encoder_session->fin)) < 0)
+                return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
+        else {
+                *absolute_byte_offset = (FLAC__uint64)pos;
+                return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
+        }
+}
+
+FLAC__SeekableStreamDecoderLengthStatus flac_decoder_length_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+{
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        (void)decoder;
+
+        if(0 == data->filesize)
+                return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;
+        else {
+                *stream_length = (FLAC__uint64)data->filesize;
+                return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
+        }
+}
+
+FLAC__bool flac_decoder_eof_callback(const FLAC__SeekableStreamDecoder *decoder, void *client_data)
+{
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        (void)decoder;
+
+        return feof(data->encoder_session->fin)? true : false;
+}
+
+FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        FLAC__uint64 n = min(data->samples_left_to_process, frame->header.blocksize);
+        (void)decoder;
+
+        if(!EncoderSession_process(data->encoder_session, buffer, n)) {
+                print_error_with_state(data->encoder_session, "ERROR during encoding");
+                data->fatal_error = true;
+                return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+        }
+
+        data->samples_left_to_process -= n;
+        return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+void flac_decoder_metadata_callback(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        (void)decoder;
+
+        if (data->fatal_error)
+                return;
+
+        if (
+                data->num_metadata_blocks == sizeof(data->metadata_blocks)/sizeof(data->metadata_blocks[0]) ||
+                0 == (data->metadata_blocks[data->num_metadata_blocks] = FLAC__metadata_object_clone(metadata))
+        )
+                data->fatal_error = true;
+        else
+                data->num_metadata_blocks++;
+}
+
+void flac_decoder_error_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+        FLACDecoderData *data = (FLACDecoderData*)client_data;
+        (void)decoder;
+
+        flac__utils_printf(stderr, 1, "%s: ERROR got %s while decoding FLAC input\n", data->encoder_session->inbasefilename, FLAC__StreamDecoderErrorStatusString[status]);
+        data->fatal_error = true;
+}
+
+FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
 {
         FILE *f;
         unsigned last_line_read;
@@ -1951,7 +2433,7 @@ void print_error_with_state(const EncoderSession *e, const char *message)
                         "\n"
                         "The encoding parameters specified do not conform to the FLAC Subset and may not\n"
                         "be streamable or playable in hardware devices.  Add --lax to the command-line\n"
-                        "options to encode with these parameters.\n"
+                        "options to encode with these parameters anyway.\n"
                 );
         }
         else if(
@@ -2010,13 +2492,13 @@ void print_verify_error(EncoderSession *e)
         flac__utils_printf(stderr, 1, "%s: ERROR: mismatch in decoded data, verify FAILED!\n", e->inbasefilename);
         flac__utils_printf(stderr, 1, "       Absolute sample=%u, frame=%u, channel=%u, sample=%u, expected %d, got %d\n", (unsigned)absolute_sample, frame_number, channel, sample, expected, got);
         flac__utils_printf(stderr, 1, "       In all known cases, verify errors are caused by hardware problems,\n");
-        flac__utils_printf(stderr, 1, "       usually overclocking or bad RAM.  Delete %s\n", e->inbasefilename);
+        flac__utils_printf(stderr, 1, "       usually overclocking or bad RAM.  Delete %s\n", e->outfilename);
         flac__utils_printf(stderr, 1, "       and repeat the flac command exactly as before.  If it does not give a\n");
         flac__utils_printf(stderr, 1, "       verify error in the exact same place each time you try it, then there is\n");
         flac__utils_printf(stderr, 1, "       a problem with your hardware; please see the FAQ:\n");
         flac__utils_printf(stderr, 1, "           http://flac.sourceforge.net/faq.html#tools__hardware_prob\n");
-        flac__utils_printf(stderr, 1, "       If it does fail in the exact same place every time, keep the bad FLAC\n");
-        flac__utils_printf(stderr, 1, "       file and submit a bug report to:\n");
+        flac__utils_printf(stderr, 1, "       If it does fail in the exact same place every time, keep\n");
+        flac__utils_printf(stderr, 1, "       %s and submit a bug report to:\n", e->outfilename);
         flac__utils_printf(stderr, 1, "           https://sourceforge.net/bugs/?func=addbug&group_id=13478\n");
         flac__utils_printf(stderr, 1, "       Make sure to upload the FLAC file and use the \"Monitor\" feature to\n");
         flac__utils_printf(stderr, 1, "       monitor the bug status.\n");
diff --git a/src/flac/encode.h b/src/flac/encode.h
index cfda5434..7a274d7f 100644
--- a/src/flac/encode.h
+++ b/src/flac/encode.h
@@ -31,6 +31,8 @@
 #include <config.h>
 #endif
 
+extern const int FLAC_ENCODE__DEFAULT_PADDING;
+
 typedef struct {
         utils__SkipUntilSpecification skip_specification;
         utils__SkipUntilSpecification until_specification;
@@ -89,8 +91,13 @@ typedef struct {
         unsigned sample_rate;
 } raw_encode_options_t;
 
+typedef struct {
+        encode_options_t common;
+} flac_encode_options_t;
+
 int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options, FLAC__bool is_aifc);
 int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options);
 int flac__encode_raw(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, raw_encode_options_t options);
+int flac__encode_flac(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, flac_encode_options_t options);
 
 #endif
diff --git a/src/flac/main.c b/src/flac/main.c
index 14db8daf..07e98647 100644
--- a/src/flac/main.c
+++ b/src/flac/main.c
@@ -56,7 +56,7 @@
 #  include "share/getopt.h"
 #endif
 
-typedef enum { RAW, WAV, AIF } FileFormat;
+typedef enum { RAW, WAV, AIF, FLAC } FileFormat;
 
 static int do_it();
 
@@ -234,7 +234,7 @@ static struct {
         const char *cmdline_forced_outfilename;
         const char *output_prefix;
         analysis_options aopts;
-        int padding;
+        int padding; /* -1 => no -P options were given, 0 => -P- was given, else -P value */
         char apodizations[1000]; /* bad MAGIC NUMBER but buffer overflow is checked */
         unsigned max_lpc_order;
         unsigned qlp_coeff_precision;
@@ -287,6 +287,7 @@ int main(int argc, char *argv[])
         _wildcard(&argc, &argv);
 #endif
 
+        srand(time(0));
         setlocale(LC_ALL, "");
         if(!init_options()) {
                 flac__utils_printf(stderr, 1, "ERROR: allocating memory\n");
@@ -435,7 +436,7 @@ int do_it()
                          * tags that we will set later, to avoid rewriting the
                          * whole file.
                          */
-                        if(option_values.padding < 0) {
+                        if(option_values.padding <= 0) {
                                 flac__utils_printf(stderr, 1, "NOTE: --replay-gain may leave a small PADDING block even with --no-padding\n");
                                 option_values.padding = GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED;
                         }
@@ -461,10 +462,10 @@ int do_it()
 
         if(!option_values.mode_decode) {
                 char padopt[16];
-                if(option_values.padding < 0)
+                if(option_values.padding == 0)
                         strcpy(padopt, "-");
                 else
-                        sprintf(padopt, " %d", option_values.padding);
+                        sprintf(padopt, " %d", option_values.padding > 0? option_values.padding : FLAC_ENCODE__DEFAULT_PADDING);
                 flac__utils_printf(stderr, 2,
                         "options:%s%s%s%s -P%s -b %u%s -l %u%s%s%s -q %u -r %u,%u%s\n",
                         option_values.delete_input?" --delete-input-file":"",
@@ -580,7 +581,7 @@ FLAC__bool init_options()
         option_values.output_prefix = 0;
         option_values.aopts.do_residual_text = false;
         option_values.aopts.do_residual_gnuplot = false;
-        option_values.padding = 4096;
+        option_values.padding = -1;
         option_values.apodizations[0] = '\0';
         option_values.max_lpc_order = 8;
         option_values.qlp_coeff_precision = 0;
@@ -860,7 +861,7 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                         option_values.do_qlp_coeff_prec_search = false;
                 }
                 else if(0 == strcmp(long_option, "no-padding")) {
-                        option_values.padding = -1;
+                        option_values.padding = 0;
                 }
                 else if(0 == strcmp(long_option, "no-verify")) {
                         option_values.verify = false;
@@ -1040,7 +1041,7 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                                 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");
+                                        return usage_error("ERROR: argument to -P must be >= 0; for no padding use -P-\n");
                                 break;
                         case 'b':
                                 FLAC__ASSERT(0 != option_argument);
@@ -1552,22 +1553,15 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
         int retval;
         off_t infilesize;
         encode_options_t common_options;
-        const char *outfilename = get_encoded_outfilename(infilename);
+        const char *outfilename = get_encoded_outfilename(infilename); /* the final name of the encoded file */
+        /* internal_outfilename is the file we will actually write to; it will be a temporary name if infilename==outfilename */
+        char *internal_outfilename = 0; /* NULL implies 'use outfilename' */
 
         if(0 == outfilename) {
                 flac__utils_printf(stderr, 1, "ERROR: filename too long: %s", infilename);
                 return 1;
         }
 
-        /*
-         * Error if output file already exists (and -f not used).
-         * Use grabbag__file_get_filesize() as a cheap way to check.
-         */
-        if(!option_values.test_only && !option_values.force_file_overwrite && grabbag__file_get_filesize(outfilename) != (off_t)(-1)) {
-                flac__utils_printf(stderr, 1, "ERROR: output file %s already exists, use -f to override\n", outfilename);
-                return 1;
-        }
-
         if(0 == strcmp(infilename, "-")) {
                 infilesize = (off_t)(-1);
                 encode_infile = grabbag__file_get_binary_stdin();
@@ -1588,6 +1582,8 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                         fmt= AIF;
                 else if(strlen(infilename) >= 5 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-5), ".aiff"))
                         fmt= AIF;
+                else if(strlen(infilename) >= 5 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-5), ".flac"))
+                        fmt= FLAC;
 
                 /* attempt to guess the file type based on the first 12 bytes */
                 if((lookahead_length = fread(lookahead, 1, 12, encode_infile)) < 12) {
@@ -1604,6 +1600,8 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                                 fmt= AIF;
                                 is_aifc = true;
                         }
+                        else if(!strncmp((const char *)lookahead, FLAC__STREAM_SYNC_STRING, sizeof(FLAC__STREAM_SYNC_STRING)))
+                                fmt= FLAC;
                         else {
                                 if(fmt != RAW)
                                         format_mistake(infilename, fmt == AIF ? "AIFF" : "WAVE", "raw");
@@ -1612,6 +1610,27 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                 }
         }
 
+        /*
+         * Error if output file already exists (and -f not used).
+         * Use grabbag__file_get_filesize() as a cheap way to check.
+         */
+        if(!option_values.test_only && !option_values.force_file_overwrite && strcmp(outfilename, "-") && grabbag__file_get_filesize(outfilename) != (off_t)(-1)) {
+                if(fmt == FLAC) {
+                        /* need more detailed error message when re-flac'ing to avoid confusing the user */
+                        flac__utils_printf(stderr, 1,
+                                "ERROR: output file %s already exists.\n\n"
+                                "By default flac encodes files to FLAC format; if you meant to decode this file\n"
+                                "from FLAC to something else, use -d.  If you meant to re-encode this file from\n"
+                                "FLAC to FLAC again, use -f to force writing to the same file, or -o to specify\n"
+                                "a different output filename.\n",
+                                outfilename
+                        );
+                }
+                else
+                        flac__utils_printf(stderr, 1, "ERROR: output file %s already exists, use -f to override\n", outfilename);
+                return 1;
+        }
+
         if(option_values.format_input_size >= 0) {
                    if (fmt != RAW || infilesize >= 0) {
                         flac__utils_printf(stderr, 1, "ERROR: can only use --input-size when encoding raw samples from stdin\n");
@@ -1622,8 +1641,12 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                 }
         }
 
+        if(option_values.sector_align && fmt == FLAC) {
+                flac__utils_printf(stderr, 1, "ERROR: can't use --sector-align when the input file is FLAC\n");
+                return 1;
+        }
         if(option_values.sector_align && fmt == RAW && infilesize < 0) {
-                flac__utils_printf(stderr, 1, "ERROR: can't --sector-align when the input size is unknown\n");
+                flac__utils_printf(stderr, 1, "ERROR: can't use --sector-align when the input size is unknown\n");
                 return 1;
         }
 
@@ -1651,7 +1674,6 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
         common_options.use_ogg = option_values.use_ogg;
         /* set a random serial number if one has not yet been specified */
         if(!option_values.has_serial_number) {
-                srand(time(0));
                 option_values.serial_number = rand();
                 option_values.has_serial_number = true;
         }
@@ -1686,6 +1708,17 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
         common_options.debug.disable_fixed_subframes = option_values.debug.disable_fixed_subframes;
         common_options.debug.disable_verbatim_subframes = option_values.debug.disable_verbatim_subframes;
 
+        /* if infilename==outfilename, we need to write to a temporary file */
+        if(encode_infile != stdin && 0 == strcmp(infilename, outfilename)) { /*@@@@@@ BUG strcmp is not adequate to check if infilename and outfilename are the same file */
+                static const char *tmp_suffix = ".tmp";
+                if(0 == (internal_outfilename = malloc(strlen(outfilename)+strlen(tmp_suffix)+1))) {
+                        flac__utils_printf(stderr, 1, "ERROR allocating memory for tempfile name\n");
+                        return 1;
+                }
+                strcpy(internal_outfilename, outfilename);
+                strcat(internal_outfilename, tmp_suffix);
+        }
+
         if(fmt == RAW) {
                 raw_encode_options_t options;
 
@@ -1696,7 +1729,14 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                 options.bps = option_values.format_bps;
                 options.sample_rate = option_values.format_sample_rate;
 
-                retval = flac__encode_raw(encode_infile, infilesize, infilename, outfilename, lookahead, lookahead_length, options);
+                retval = flac__encode_raw(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options);
+        }
+        else if(fmt == FLAC) {
+                flac_encode_options_t options;
+
+                options.common = common_options;
+
+                retval = flac__encode_flac(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options);
         }
         else {
                 wav_encode_options_t options;
@@ -1704,27 +1744,41 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                 options.common = common_options;
 
                 if(fmt == AIF)
-                        retval = flac__encode_aif(encode_infile, infilesize, infilename, outfilename, lookahead, lookahead_length, options, is_aifc);
+                        retval = flac__encode_aif(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options, is_aifc);
                 else
-                        retval = flac__encode_wav(encode_infile, infilesize, infilename, outfilename, lookahead, lookahead_length, options);
+                        retval = flac__encode_wav(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options);
         }
 
-        if(retval == 0 && strcmp(infilename, "-")) {
+        if(retval == 0) {
                 if(strcmp(outfilename, "-")) {
                         if(option_values.replay_gain) {
                                 float title_gain, title_peak;
                                 const char *error;
                                 grabbag__replaygain_get_title(&title_gain, &title_peak);
-                                if(0 != (error = grabbag__replaygain_store_to_file_title(outfilename, title_gain, title_peak, /*preserve_modtime=*/true))) {
+                                if(0 != (error = grabbag__replaygain_store_to_file_title(internal_outfilename, title_gain, title_peak, /*preserve_modtime=*/true))) {
                                         flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain title tags\n", outfilename);
                                 }
                         }
-                        grabbag__file_copy_metadata(infilename, outfilename);
+                        if(strcmp(infilename, "-"))
+                                grabbag__file_copy_metadata(infilename, internal_outfilename);
                 }
-                if(option_values.delete_input)
-                        unlink(infilename);
         }
 
+        /* rename temporary file if necessary */
+        if(retval == 0 && internal_outfilename != 0) {
+                if(rename(internal_outfilename, outfilename) < 0) {
+                        flac__utils_printf(stderr, 1, "ERROR: moving new FLAC file %s back on top of original FLAC file %s, keeping both\n", internal_outfilename, outfilename);
+                        retval = 1;
+                }
+        }
+
+        /* handle --delete-input-file, but don't want to delete if piping from stdin, or if input filename and output filename are the same */
+        if(retval == 0 && option_values.delete_input && strcmp(infilename, "-") && internal_outfilename == 0)
+                unlink(infilename);
+
+        if(internal_outfilename != 0)
+                free(internal_outfilename);
+
         return retval;
 }
 
@@ -1870,14 +1924,7 @@ const char *get_outfilename(const char *infilename, const char *suffix)
                                         return 0;
                         }
                         else {
-                                if(0 == strcmp(p, suffix)) {
-                                        *p = '\0';
-                                        if (flac__strlcat(buffer, "_new", sizeof buffer) >= sizeof buffer)
-                                                return 0;
-                                }
-                                else {
-                                        *p = '\0';
-                                }
+                                *p = '\0';
                                 if (flac__strlcat(buffer, suffix, sizeof buffer) >= sizeof buffer)
                                         return 0;
                         }