fix to ogg-less building
[platform/upstream/flac.git] / src / libFLAC / stream_encoder.c
index 2ff2f1c..2a7a6aa 100644 (file)
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/*@@@@@@*/
-#undef WINDOW_DEBUG_OUTPUT
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
 
+#if defined _MSC_VER || defined __MINGW32__
+#include <io.h> /* for _setmode() */
+#include <fcntl.h> /* for _O_BINARY */
+#endif
+#if defined __CYGWIN__ || defined __EMX__
+#include <io.h> /* for setmode(), O_BINARY */
+#include <fcntl.h> /* for _O_BINARY */
+#endif
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h> /* for malloc() */
 #include <string.h> /* for memcpy() */
+#include <sys/types.h> /* for off_t */
+#if defined _MSC_VER || defined __MINGW32__
+#if _MSC_VER <= 1200 /* @@@ [2G limit] */
+#define fseeko fseek
+#define ftello ftell
+#endif
+#endif
 #include "FLAC/assert.h"
 #include "FLAC/stream_decoder.h"
 #include "protected/stream_encoder.h"
 #include "private/lpc.h"
 #include "private/md5.h"
 #include "private/memory.h"
+#ifdef FLAC__HAS_OGG
+#include "private/ogg_helper.h"
+#endif
 #include "private/stream_encoder_framing.h"
 #include "private/window.h"
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
 #ifdef min
 #undef min
 #endif
@@ -93,6 +108,11 @@ static void set_defaults_(FLAC__StreamEncoder *encoder);
 static void free_(FLAC__StreamEncoder *encoder);
 static FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_size);
 static FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples);
+static FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples);
+static void update_metadata_(const FLAC__StreamEncoder *encoder);
+#if FLAC__HAS_OGG
+static void update_ogg_metadata_(FLAC__StreamEncoder *encoder);
+#endif
 static FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_frame);
 static FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_frame);
 
@@ -112,9 +132,6 @@ static FLAC__bool process_subframe_(
        FLAC__int32 *residual[2],
        unsigned *best_subframe,
        unsigned *best_bits
-#ifdef WINDOW_DEBUG_OUTPUT
-       ,unsigned subframe_number
-#endif
 );
 
 static FLAC__bool add_subframe_(
@@ -123,9 +140,6 @@ static FLAC__bool add_subframe_(
        unsigned subframe_bps,
        const FLAC__Subframe *subframe,
        FLAC__BitBuffer *frame
-#ifdef WINDOW_DEBUG_OUTPUT
-,unsigned subframe_bits
-#endif
 );
 
 static unsigned evaluate_constant_subframe_(
@@ -175,11 +189,6 @@ static unsigned evaluate_lpc_subframe_(
        unsigned rice_parameter_search_dist,
        FLAC__Subframe *subframe,
        FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents
-#ifdef WINDOW_DEBUG_OUTPUT
-       ,unsigned frame_number
-       ,unsigned subframe_number
-       ,FLAC__ApodizationSpecification aspec
-#endif
 );
 #endif
 
@@ -297,51 +306,17 @@ static void append_to_verify_fifo_interleaved_(
        unsigned wide_samples
 );
 
-static FLAC__StreamDecoderReadStatus verify_read_callback_(
-       const FLAC__StreamDecoder *decoder,
-       FLAC__byte buffer[],
-       unsigned *bytes,
-       void *client_data
-);
+static FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
+static FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+static void verify_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
 
-static FLAC__StreamDecoderWriteStatus verify_write_callback_(
-       const FLAC__StreamDecoder *decoder,
-       const FLAC__Frame *frame,
-       const FLAC__int32 * const buffer[],
-       void *client_data
-);
-
-static void verify_metadata_callback_(
-       const FLAC__StreamDecoder *decoder,
-       const FLAC__StreamMetadata *metadata,
-       void *client_data
-);
-
-static void verify_error_callback_(
-       const FLAC__StreamDecoder *decoder,
-       FLAC__StreamDecoderErrorStatus status,
-       void *client_data
-);
+static FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
+static FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+static FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
+static FILE *get_binary_stdout_();
 
-#ifdef WINDOW_DEBUG_OUTPUT
-static const char * const winstr[] = {
-       "bartlett",
-       "bartlett_hann",
-       "blackman",
-       "blackman_harris_4term_92db_sidelobe",
-       "connes",
-       "flattop",
-       "gauss",
-       "hamming",
-       "hann",
-       "kaiser_bessel",
-       "nuttall",
-       "rectangular",
-       "triangle",
-       "tukey",
-       "welch"
-};
-#endif
 
 /***********************************************************************
  *
@@ -382,7 +357,8 @@ typedef struct FLAC__StreamEncoderPrivate {
        unsigned loose_mid_side_stereo_frames;            /* rounded number of frames the encoder will use before trying both independent and mid/side frames again */
        unsigned loose_mid_side_stereo_frame_count;       /* number of frames using the current channel assignment */
        FLAC__ChannelAssignment last_channel_assignment;
-       FLAC__StreamMetadata metadata;
+       FLAC__StreamMetadata streaminfo;                  /* scratchpad for STREAMINFO as it is built */
+       FLAC__StreamMetadata_SeekTable *seek_table;       /* pointer into encoder->protected_->metadata_ where the seek table is */
        unsigned current_sample_number;
        unsigned current_frame_number;
        struct FLAC__MD5Context md5context;
@@ -405,9 +381,22 @@ typedef struct FLAC__StreamEncoderPrivate {
        FLAC__bool disable_constant_subframes;
        FLAC__bool disable_fixed_subframes;
        FLAC__bool disable_verbatim_subframes;
+#if FLAC__HAS_OGG
+       FLAC__bool is_ogg;
+#endif
+       FLAC__StreamEncoderReadCallback read_callback; /* currently only needed for Ogg FLAC */
+       FLAC__StreamEncoderSeekCallback seek_callback;
+       FLAC__StreamEncoderTellCallback tell_callback;
        FLAC__StreamEncoderWriteCallback write_callback;
        FLAC__StreamEncoderMetadataCallback metadata_callback;
+       FLAC__StreamEncoderProgressCallback progress_callback;
        void *client_data;
+       unsigned first_seekpoint_to_check;
+       FILE *file;                            /* only used when encoding to a file */
+       FLAC__uint64 bytes_written;
+       FLAC__uint64 samples_written;
+       unsigned frames_written;
+       unsigned total_frames_estimate;
        /* unaligned (original) pointers to allocated data */
        FLAC__int32 *integer_signal_unaligned[FLAC__MAX_CHANNELS];
        FLAC__int32 *integer_signal_mid_side_unaligned[2];
@@ -459,27 +448,40 @@ typedef struct FLAC__StreamEncoderPrivate {
 
 FLAC_API const char * const FLAC__StreamEncoderStateString[] = {
        "FLAC__STREAM_ENCODER_OK",
+       "FLAC__STREAM_ENCODER_UNINITIALIZED",
+       "FLAC__STREAM_ENCODER_OGG_ERROR",
        "FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR",
        "FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA",
-       "FLAC__STREAM_ENCODER_INVALID_CALLBACK",
-       "FLAC__STREAM_ENCODER_INVALID_NUMBER_OF_CHANNELS",
-       "FLAC__STREAM_ENCODER_INVALID_BITS_PER_SAMPLE",
-       "FLAC__STREAM_ENCODER_INVALID_SAMPLE_RATE",
-       "FLAC__STREAM_ENCODER_INVALID_BLOCK_SIZE",
-       "FLAC__STREAM_ENCODER_INVALID_MAX_LPC_ORDER",
-       "FLAC__STREAM_ENCODER_INVALID_QLP_COEFF_PRECISION",
-       "FLAC__STREAM_ENCODER_MID_SIDE_CHANNELS_MISMATCH",
-       "FLAC__STREAM_ENCODER_MID_SIDE_SAMPLE_SIZE_MISMATCH",
-       "FLAC__STREAM_ENCODER_ILLEGAL_MID_SIDE_FORCE",
-       "FLAC__STREAM_ENCODER_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER",
-       "FLAC__STREAM_ENCODER_NOT_STREAMABLE",
+       "FLAC__STREAM_ENCODER_CLIENT_ERROR",
+       "FLAC__STREAM_ENCODER_IO_ERROR",
        "FLAC__STREAM_ENCODER_FRAMING_ERROR",
-       "FLAC__STREAM_ENCODER_INVALID_METADATA",
-       "FLAC__STREAM_ENCODER_FATAL_ERROR_WHILE_ENCODING",
-       "FLAC__STREAM_ENCODER_FATAL_ERROR_WHILE_WRITING",
-       "FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR",
-       "FLAC__STREAM_ENCODER_ALREADY_INITIALIZED",
-       "FLAC__STREAM_ENCODER_UNINITIALIZED"
+       "FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR"
+};
+
+FLAC_API const char * const FLAC__StreamEncoderInitStatusString[] = {
+       "FLAC__STREAM_ENCODER_INIT_STATUS_OK",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_MID_SIDE_CHANNELS_MISMATCH",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_ILLEGAL_MID_SIDE_FORCE",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA",
+       "FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED"
+};
+
+FLAC_API const char * const FLAC__treamEncoderReadStatusString[] = {
+       "FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE",
+       "FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM",
+       "FLAC__STREAM_ENCODER_READ_STATUS_ABORT",
+       "FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED"
 };
 
 FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[] = {
@@ -487,6 +489,18 @@ FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[] = {
        "FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR"
 };
 
+FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[] = {
+       "FLAC__STREAM_ENCODER_SEEK_STATUS_OK",
+       "FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR",
+       "FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED"
+};
+
+FLAC_API const char * const FLAC__StreamEncoderTellStatusString[] = {
+       "FLAC__STREAM_ENCODER_TELL_STATUS_OK",
+       "FLAC__STREAM_ENCODER_TELL_STATUS_ERROR",
+       "FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED"
+};
+
 /***********************************************************************
  *
  * Class constructor/destructor
@@ -525,6 +539,8 @@ FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new()
                return 0;
        }
 
+       encoder->private_->file = 0;
+
        set_defaults_(encoder);
 
        encoder->private_->is_being_deleted = false;
@@ -601,47 +617,59 @@ FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder)
  *
  ***********************************************************************/
 
-FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder *encoder)
+static FLAC__StreamEncoderInitStatus init_stream_internal_(
+       FLAC__StreamEncoder *encoder,
+       FLAC__StreamEncoderReadCallback read_callback,
+       FLAC__StreamEncoderWriteCallback write_callback,
+       FLAC__StreamEncoderSeekCallback seek_callback,
+       FLAC__StreamEncoderTellCallback tell_callback,
+       FLAC__StreamEncoderMetadataCallback metadata_callback,
+       void *client_data,
+       FLAC__bool is_ogg
+)
 {
        unsigned i;
-       FLAC__bool metadata_has_seektable, metadata_has_vorbis_comment;
+       FLAC__bool metadata_has_seektable, metadata_has_vorbis_comment, metadata_picture_has_type1, metadata_picture_has_type2;
 
        FLAC__ASSERT(0 != encoder);
 
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_ALREADY_INITIALIZED;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED;
 
-       encoder->protected_->state = FLAC__STREAM_ENCODER_OK;
+#ifndef FLAC__HAS_OGG
+       if(is_ogg)
+               return FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER;
+#endif
 
-       if(0 == encoder->private_->write_callback || 0 == encoder->private_->metadata_callback)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_CALLBACK;
+       if(0 == write_callback || (seek_callback && 0 == tell_callback))
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS;
 
        if(encoder->protected_->channels == 0 || encoder->protected_->channels > FLAC__MAX_CHANNELS)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_NUMBER_OF_CHANNELS;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS;
 
        if(encoder->protected_->do_mid_side_stereo && encoder->protected_->channels != 2)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_MID_SIDE_CHANNELS_MISMATCH;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_MID_SIDE_CHANNELS_MISMATCH;
 
        if(encoder->protected_->loose_mid_side_stereo && !encoder->protected_->do_mid_side_stereo)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_ILLEGAL_MID_SIDE_FORCE;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ILLEGAL_MID_SIDE_FORCE;
 
        if(encoder->protected_->bits_per_sample >= 32)
-               encoder->protected_->do_mid_side_stereo = false; /* since we do 32-bit math, the side channel would have 33 bps and overflow */
+               encoder->protected_->do_mid_side_stereo = false; /* since we currenty do 32-bit math, the side channel would have 33 bps and overflow */
 
        if(encoder->protected_->bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || encoder->protected_->bits_per_sample > FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_BITS_PER_SAMPLE;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE;
 
        if(!FLAC__format_sample_rate_is_valid(encoder->protected_->sample_rate))
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_SAMPLE_RATE;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE;
 
        if(encoder->protected_->blocksize < FLAC__MIN_BLOCK_SIZE || encoder->protected_->blocksize > FLAC__MAX_BLOCK_SIZE)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_BLOCK_SIZE;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE;
 
        if(encoder->protected_->max_lpc_order > FLAC__MAX_LPC_ORDER)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_MAX_LPC_ORDER;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER;
 
        if(encoder->protected_->blocksize < encoder->protected_->max_lpc_order)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER;
 
        if(encoder->protected_->qlp_coeff_precision == 0) {
                if(encoder->protected_->bits_per_sample < 16) {
@@ -676,7 +704,7 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
                FLAC__ASSERT(encoder->protected_->qlp_coeff_precision <= FLAC__MAX_QLP_COEFF_PRECISION);
        }
        else if(encoder->protected_->qlp_coeff_precision < FLAC__MIN_QLP_COEFF_PRECISION || encoder->protected_->qlp_coeff_precision > FLAC__MAX_QLP_COEFF_PRECISION)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_QLP_COEFF_PRECISION;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION;
 
        if(encoder->protected_->streamable_subset) {
                if(
@@ -693,7 +721,7 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
                        encoder->protected_->blocksize != 8192 &&
                        encoder->protected_->blocksize != 16384
                )
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_NOT_STREAMABLE;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE;
                if(
                        encoder->protected_->sample_rate != 8000 &&
                        encoder->protected_->sample_rate != 16000 &&
@@ -704,7 +732,7 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
                        encoder->protected_->sample_rate != 48000 &&
                        encoder->protected_->sample_rate != 96000
                )
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_NOT_STREAMABLE;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE;
                if(
                        encoder->protected_->bits_per_sample != 8 &&
                        encoder->protected_->bits_per_sample != 12 &&
@@ -712,9 +740,18 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
                        encoder->protected_->bits_per_sample != 20 &&
                        encoder->protected_->bits_per_sample != 24
                )
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_NOT_STREAMABLE;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE;
                if(encoder->protected_->max_residual_partition_order > FLAC__SUBSET_MAX_RICE_PARTITION_ORDER)
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_NOT_STREAMABLE;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE;
+               if(
+                       encoder->protected_->sample_rate <= 48000 &&
+                       (
+                               encoder->protected_->blocksize > FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ ||
+                               encoder->protected_->max_lpc_order > FLAC__SUBSET_MAX_LPC_ORDER_48000HZ
+                       )
+               ) {
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE;
+               }
        }
 
        if(encoder->protected_->max_residual_partition_order >= (1u << FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN))
@@ -722,29 +759,82 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
        if(encoder->protected_->min_residual_partition_order >= encoder->protected_->max_residual_partition_order)
                encoder->protected_->min_residual_partition_order = encoder->protected_->max_residual_partition_order;
 
+#if FLAC__HAS_OGG
+       /* reorder metadata if necessary to ensure that any VORBIS_COMMENT is the first, according to the mapping spec */
+       if(is_ogg && 0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 1) {
+               unsigned i;
+               for(i = 1; i < encoder->protected_->num_metadata_blocks; i++) {
+                       if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+                               FLAC__StreamMetadata *vc = encoder->protected_->metadata[i];
+                               for( ; i > 0; i--)
+                                       encoder->protected_->metadata[i] = encoder->protected_->metadata[i-1];
+                               encoder->protected_->metadata[0] = vc;
+                               break;
+                       }
+               }
+       }
+#endif
+       /* keep track of any SEEKTABLE block */
+       if(0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0) {
+               unsigned i;
+               for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) {
+                       if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_SEEKTABLE) {
+                               encoder->private_->seek_table = &encoder->protected_->metadata[i]->data.seek_table;
+                               break; /* take only the first one */
+                       }
+               }
+       }
+
        /* validate metadata */
        if(0 == encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0)
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
        metadata_has_seektable = false;
        metadata_has_vorbis_comment = false;
+       metadata_picture_has_type1 = false;
+       metadata_picture_has_type2 = false;
        for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) {
-               if(encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_STREAMINFO)
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
-               else if(encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_SEEKTABLE) {
+               const FLAC__StreamMetadata *m = encoder->protected_->metadata[i];
+               if(m->type == FLAC__METADATA_TYPE_STREAMINFO)
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
+               else if(m->type == FLAC__METADATA_TYPE_SEEKTABLE) {
                        if(metadata_has_seektable) /* only one is allowed */
-                               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+                               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
                        metadata_has_seektable = true;
-                       if(!FLAC__format_seektable_is_legal(&encoder->protected_->metadata[i]->data.seek_table))
-                               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+                       if(!FLAC__format_seektable_is_legal(&m->data.seek_table))
+                               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
                }
-               else if(encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+               else if(m->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
                        if(metadata_has_vorbis_comment) /* only one is allowed */
-                               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+                               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
                        metadata_has_vorbis_comment = true;
                }
-               else if(encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_CUESHEET) {
-                       if(!FLAC__format_cuesheet_is_legal(&encoder->protected_->metadata[i]->data.cue_sheet, encoder->protected_->metadata[i]->data.cue_sheet.is_cd, /*violation=*/0))
-                               return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+               else if(m->type == FLAC__METADATA_TYPE_CUESHEET) {
+                       if(!FLAC__format_cuesheet_is_legal(&m->data.cue_sheet, m->data.cue_sheet.is_cd, /*violation=*/0))
+                               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
+               }
+               else if(m->type == FLAC__METADATA_TYPE_PICTURE) {
+                       if(!FLAC__format_picture_is_legal(&m->data.picture, /*violation=*/0))
+                               return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
+                       if(m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD) {
+                               if(metadata_picture_has_type1) /* there should only be 1 per stream */
+                                       return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
+                               metadata_picture_has_type1 = true;
+                               /* standard icon must be 32x32 pixel PNG */
+                               if(
+                                       m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD && 
+                                       (
+                                               (strcmp(m->data.picture.mime_type, "image/png") && strcmp(m->data.picture.mime_type, "-->")) ||
+                                               m->data.picture.width != 32 ||
+                                               m->data.picture.height != 32
+                                       )
+                               )
+                                       return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
+                       }
+                       else if(m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON) {
+                               if(metadata_picture_has_type2) /* there should only be 1 per stream */
+                                       return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA;
+                               metadata_picture_has_type2 = true;
+                       }
                }
        }
 
@@ -861,13 +951,33 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
        /* we require precompute_partition_sums if do_escape_coding because of their intertwined nature */
        encoder->private_->precompute_partition_sums = (encoder->protected_->max_residual_partition_order > encoder->protected_->min_residual_partition_order) || encoder->protected_->do_escape_coding;
 
+       /* set state to OK; from here on, errors are fatal and we'll override the state then */
+       encoder->protected_->state = FLAC__STREAM_ENCODER_OK;
+
+#if FLAC__HAS_OGG
+       encoder->private_->is_ogg = is_ogg;
+       if(is_ogg && !FLAC__ogg_encoder_aspect_init(&encoder->protected_->ogg_encoder_aspect)) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
+#endif
+
+       encoder->private_->read_callback = read_callback;
+       encoder->private_->write_callback = write_callback;
+       encoder->private_->seek_callback = seek_callback;
+       encoder->private_->tell_callback = tell_callback;
+       encoder->private_->metadata_callback = metadata_callback;
+       encoder->private_->client_data = client_data;
+
        if(!resize_buffers_(encoder, encoder->protected_->blocksize)) {
                /* the above function sets the state for us in case of an error */
-               return encoder->protected_->state;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
        }
 
-       if(!FLAC__bitbuffer_init(encoder->private_->frame))
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+       if(!FLAC__bitbuffer_init(encoder->private_->frame)) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
 
        /*
         * Set up the verify stuff if necessary
@@ -879,8 +989,10 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
                 */
                encoder->private_->verify.input_fifo.size = encoder->protected_->blocksize;
                for(i = 0; i < encoder->protected_->channels; i++) {
-                       if(0 == (encoder->private_->verify.input_fifo.data[i] = (FLAC__int32*)malloc(sizeof(FLAC__int32) * encoder->private_->verify.input_fifo.size)))
-                               return encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+                       if(0 == (encoder->private_->verify.input_fifo.data[i] = (FLAC__int32*)malloc(sizeof(FLAC__int32) * encoder->private_->verify.input_fifo.size))) {
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+                               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+                       }
                }
                encoder->private_->verify.input_fifo.tail = 0;
 
@@ -888,16 +1000,15 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
                 * Now set up a stream decoder for verification
                 */
                encoder->private_->verify.decoder = FLAC__stream_decoder_new();
-               if(0 == encoder->private_->verify.decoder)
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR;
+               if(0 == encoder->private_->verify.decoder) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+               }
 
-               FLAC__stream_decoder_set_read_callback(encoder->private_->verify.decoder, verify_read_callback_);
-               FLAC__stream_decoder_set_write_callback(encoder->private_->verify.decoder, verify_write_callback_);
-               FLAC__stream_decoder_set_metadata_callback(encoder->private_->verify.decoder, verify_metadata_callback_);
-               FLAC__stream_decoder_set_error_callback(encoder->private_->verify.decoder, verify_error_callback_);
-               FLAC__stream_decoder_set_client_data(encoder->private_->verify.decoder, encoder);
-               if(FLAC__stream_decoder_init(encoder->private_->verify.decoder) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA)
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR;
+               if(FLAC__stream_decoder_init_stream(encoder->private_->verify.decoder, verify_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, verify_write_callback_, verify_metadata_callback_, verify_error_callback_, /*client_data=*/encoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+               }
        }
        encoder->private_->verify.error_stats.absolute_sample = 0;
        encoder->private_->verify.error_stats.frame_number = 0;
@@ -907,15 +1018,27 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
        encoder->private_->verify.error_stats.got = 0;
 
        /*
+        * These must be done before we write any metadata, because that
+        * calls the write_callback, which uses these values.
+        */
+       encoder->private_->first_seekpoint_to_check = 0;
+       encoder->private_->samples_written = 0;
+       encoder->protected_->streaminfo_offset = 0;
+       encoder->protected_->seektable_offset = 0;
+       encoder->protected_->audio_offset = 0;
+
+       /*
         * write the stream header
         */
        if(encoder->protected_->verify)
                encoder->private_->verify.state_hint = ENCODER_IN_MAGIC;
-       if(!FLAC__bitbuffer_write_raw_uint32(encoder->private_->frame, FLAC__STREAM_SYNC, FLAC__STREAM_SYNC_LEN))
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+       if(!FLAC__bitbuffer_write_raw_uint32(encoder->private_->frame, FLAC__STREAM_SYNC, FLAC__STREAM_SYNC_LEN)) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
        if(!write_bitbuffer_(encoder, 0)) {
                /* the above function sets the state for us in case of an error */
-               return encoder->protected_->state;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
        }
 
        /*
@@ -923,45 +1046,49 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
         */
        if(encoder->protected_->verify)
                encoder->private_->verify.state_hint = ENCODER_IN_METADATA;
-       encoder->private_->metadata.type = FLAC__METADATA_TYPE_STREAMINFO;
-       encoder->private_->metadata.is_last = false; /* we will have at a minimum a VORBIS_COMMENT afterwards */
-       encoder->private_->metadata.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
-       encoder->private_->metadata.data.stream_info.min_blocksize = encoder->protected_->blocksize; /* this encoder uses the same blocksize for the whole stream */
-       encoder->private_->metadata.data.stream_info.max_blocksize = encoder->protected_->blocksize;
-       encoder->private_->metadata.data.stream_info.min_framesize = 0; /* we don't know this yet; have to fill it in later */
-       encoder->private_->metadata.data.stream_info.max_framesize = 0; /* we don't know this yet; have to fill it in later */
-       encoder->private_->metadata.data.stream_info.sample_rate = encoder->protected_->sample_rate;
-       encoder->private_->metadata.data.stream_info.channels = encoder->protected_->channels;
-       encoder->private_->metadata.data.stream_info.bits_per_sample = encoder->protected_->bits_per_sample;
-       encoder->private_->metadata.data.stream_info.total_samples = encoder->protected_->total_samples_estimate; /* we will replace this later with the real total */
-       memset(encoder->private_->metadata.data.stream_info.md5sum, 0, 16); /* we don't know this yet; have to fill it in later */
+       encoder->private_->streaminfo.type = FLAC__METADATA_TYPE_STREAMINFO;
+       encoder->private_->streaminfo.is_last = false; /* we will have at a minimum a VORBIS_COMMENT afterwards */
+       encoder->private_->streaminfo.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
+       encoder->private_->streaminfo.data.stream_info.min_blocksize = encoder->protected_->blocksize; /* this encoder uses the same blocksize for the whole stream */
+       encoder->private_->streaminfo.data.stream_info.max_blocksize = encoder->protected_->blocksize;
+       encoder->private_->streaminfo.data.stream_info.min_framesize = 0; /* we don't know this yet; have to fill it in later */
+       encoder->private_->streaminfo.data.stream_info.max_framesize = 0; /* we don't know this yet; have to fill it in later */
+       encoder->private_->streaminfo.data.stream_info.sample_rate = encoder->protected_->sample_rate;
+       encoder->private_->streaminfo.data.stream_info.channels = encoder->protected_->channels;
+       encoder->private_->streaminfo.data.stream_info.bits_per_sample = encoder->protected_->bits_per_sample;
+       encoder->private_->streaminfo.data.stream_info.total_samples = encoder->protected_->total_samples_estimate; /* we will replace this later with the real total */
+       memset(encoder->private_->streaminfo.data.stream_info.md5sum, 0, 16); /* we don't know this yet; have to fill it in later */
        FLAC__MD5Init(&encoder->private_->md5context);
-       if(!FLAC__bitbuffer_clear(encoder->private_->frame))
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
-       if(!FLAC__add_metadata_block(&encoder->private_->metadata, encoder->private_->frame))
-               return encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+       if(!FLAC__bitbuffer_clear(encoder->private_->frame)) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
+       if(!FLAC__add_metadata_block(&encoder->private_->streaminfo, encoder->private_->frame)) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
        if(!write_bitbuffer_(encoder, 0)) {
                /* the above function sets the state for us in case of an error */
-               return encoder->protected_->state;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
        }
 
        /*
         * Now that the STREAMINFO block is written, we can init this to an
         * absurdly-high value...
         */
-       encoder->private_->metadata.data.stream_info.min_framesize = (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN) - 1;
+       encoder->private_->streaminfo.data.stream_info.min_framesize = (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN) - 1;
        /* ... and clear this to 0 */
-       encoder->private_->metadata.data.stream_info.total_samples = 0;
+       encoder->private_->streaminfo.data.stream_info.total_samples = 0;
 
        /*
         * Check to see if the supplied metadata contains a VORBIS_COMMENT;
         * if not, we will write an empty one (FLAC__add_metadata_block()
         * automatically supplies the vendor string).
         *
-        * WATCHOUT: libOggFLAC depends on us to write this block after the
-        * STREAMINFO since that's what the mapping requires.  (In the case
-        * that metadata_has_vorbis_comment is true it will have already
-        * insured that the metadata list is properly ordered.)
+        * WATCHOUT: the Ogg FLAC mapping requires us to write this block after
+        * the STREAMINFO.  (In the case that metadata_has_vorbis_comment is
+        * true it will have already insured that the metadata list is properly
+        * ordered.)
         */
        if(!metadata_has_vorbis_comment) {
                FLAC__StreamMetadata vorbis_comment;
@@ -972,13 +1099,17 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
                vorbis_comment.data.vorbis_comment.vendor_string.entry = 0;
                vorbis_comment.data.vorbis_comment.num_comments = 0;
                vorbis_comment.data.vorbis_comment.comments = 0;
-               if(!FLAC__bitbuffer_clear(encoder->private_->frame))
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
-               if(!FLAC__add_metadata_block(&vorbis_comment, encoder->private_->frame))
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+               if(!FLAC__bitbuffer_clear(encoder->private_->frame)) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+               }
+               if(!FLAC__add_metadata_block(&vorbis_comment, encoder->private_->frame)) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+               }
                if(!write_bitbuffer_(encoder, 0)) {
                        /* the above function sets the state for us in case of an error */
-                       return encoder->protected_->state;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
                }
        }
 
@@ -987,25 +1118,212 @@ FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder
         */
        for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) {
                encoder->protected_->metadata[i]->is_last = (i == encoder->protected_->num_metadata_blocks - 1);
-               if(!FLAC__bitbuffer_clear(encoder->private_->frame))
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
-               if(!FLAC__add_metadata_block(encoder->protected_->metadata[i], encoder->private_->frame))
-                       return encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+               if(!FLAC__bitbuffer_clear(encoder->private_->frame)) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+               }
+               if(!FLAC__add_metadata_block(encoder->protected_->metadata[i], encoder->private_->frame)) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+               }
                if(!write_bitbuffer_(encoder, 0)) {
                        /* the above function sets the state for us in case of an error */
-                       return encoder->protected_->state;
+                       return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
                }
        }
 
+       /* now that all the metadata is written, we save the stream offset */
+       if(encoder->private_->tell_callback && encoder->private_->tell_callback(encoder, &encoder->protected_->audio_offset, encoder->private_->client_data) == FLAC__STREAM_ENCODER_TELL_STATUS_ERROR) { /* FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED just means we didn't get the offset; no error */
+               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
+
        if(encoder->protected_->verify)
                encoder->private_->verify.state_hint = ENCODER_IN_AUDIO;
 
-       return encoder->protected_->state;
+       return FLAC__STREAM_ENCODER_INIT_STATUS_OK;
+}
+
+FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream(
+       FLAC__StreamEncoder *encoder,
+       FLAC__StreamEncoderWriteCallback write_callback,
+       FLAC__StreamEncoderSeekCallback seek_callback,
+       FLAC__StreamEncoderTellCallback tell_callback,
+       FLAC__StreamEncoderMetadataCallback metadata_callback,
+       void *client_data
+)
+{
+       return init_stream_internal_(
+               encoder,
+               /*read_callback=*/0,
+               write_callback,
+               seek_callback,
+               tell_callback,
+               metadata_callback,
+               client_data,
+               /*is_ogg=*/false
+       );
+}
+
+FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream(
+       FLAC__StreamEncoder *encoder,
+       FLAC__StreamEncoderReadCallback read_callback,
+       FLAC__StreamEncoderWriteCallback write_callback,
+       FLAC__StreamEncoderSeekCallback seek_callback,
+       FLAC__StreamEncoderTellCallback tell_callback,
+       FLAC__StreamEncoderMetadataCallback metadata_callback,
+       void *client_data
+)
+{
+       return init_stream_internal_(
+               encoder,
+               read_callback,
+               write_callback,
+               seek_callback,
+               tell_callback,
+               metadata_callback,
+               client_data,
+               /*is_ogg=*/true
+       );
+}
+static FLAC__StreamEncoderInitStatus init_FILE_internal_(
+       FLAC__StreamEncoder *encoder,
+       FILE *file,
+       FLAC__StreamEncoderProgressCallback progress_callback,
+       void *client_data,
+       FLAC__bool is_ogg
+)
+{
+       FLAC__StreamEncoderInitStatus init_status;
+
+       FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != file);
+
+       if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED;
+
+       /* double protection */
+       if(file == 0) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_IO_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
+
+       /*
+        * To make sure that our file does not go unclosed after an error, we
+        * must assign the FILE pointer before any further error can occur in
+        * this routine.
+        */
+       if(file == stdout)
+               file = get_binary_stdout_(); /* just to be safe */
+
+       encoder->private_->file = file;
+
+       encoder->private_->progress_callback = progress_callback;
+       encoder->private_->bytes_written = 0;
+       encoder->private_->samples_written = 0;
+       encoder->private_->frames_written = 0;
+
+       init_status = init_stream_internal_(
+               encoder,
+               is_ogg? file_read_callback_ : 0,
+               file_write_callback_,
+               file_seek_callback_,
+               file_tell_callback_,
+               /*metadata_callback=*/0,
+               client_data,
+               is_ogg
+       );
+       if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
+               /* the above function sets the state for us in case of an error */
+               return init_status;
+       }
+
+       {
+               unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder);
+
+               FLAC__ASSERT(blocksize != 0);
+               encoder->private_->total_frames_estimate = (unsigned)((FLAC__stream_encoder_get_total_samples_estimate(encoder) + blocksize - 1) / blocksize);
+       }
+
+       return init_status;
+}
+FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_FILE(
+       FLAC__StreamEncoder *encoder,
+       FILE *file,
+       FLAC__StreamEncoderProgressCallback progress_callback,
+       void *client_data
+)
+{
+       return init_FILE_internal_(encoder, file, progress_callback, client_data, /*is_ogg=*/false);
+}
+FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_FILE(
+       FLAC__StreamEncoder *encoder,
+       FILE *file,
+       FLAC__StreamEncoderProgressCallback progress_callback,
+       void *client_data
+)
+{
+       return init_FILE_internal_(encoder, file, progress_callback, client_data, /*is_ogg=*/true);
+}
+
+static FLAC__StreamEncoderInitStatus init_file_internal_(
+       FLAC__StreamEncoder *encoder,
+       const char *filename,
+       FLAC__StreamEncoderProgressCallback progress_callback,
+       void *client_data,
+       FLAC__bool is_ogg
+)
+{
+       FILE *file;
+
+       FLAC__ASSERT(0 != encoder);
+
+       /*
+        * To make sure that our file does not go unclosed after an error, we
+        * have to do the same entrance checks here that are later performed
+        * in FLAC__stream_encoder_init_FILE() before the FILE* is assigned.
+        */
+       if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED;
+
+       file = filename? fopen(filename, "w+b") : stdout;
+
+       if(file == 0) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_IO_ERROR;
+               return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR;
+       }
+
+       return init_FILE_internal_(encoder, file, progress_callback, client_data, is_ogg);
+}
+
+FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file(
+       FLAC__StreamEncoder *encoder,
+       const char *filename,
+       FLAC__StreamEncoderProgressCallback progress_callback,
+       void *client_data
+)
+{
+       return init_file_internal_(encoder, filename, progress_callback, client_data, /*is_ogg=*/false);
+}
+
+FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_file(
+       FLAC__StreamEncoder *encoder,
+       const char *filename,
+       FLAC__StreamEncoderProgressCallback progress_callback,
+       void *client_data
+)
+{
+       return init_file_internal_(encoder, filename, progress_callback, client_data, /*is_ogg=*/true);
 }
 
 FLAC_API void FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
 
        if(encoder->protected_->state == FLAC__STREAM_ENCODER_UNINITIALIZED)
                return;
@@ -1017,24 +1335,63 @@ FLAC_API void FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder)
                }
        }
 
-       FLAC__MD5Final(encoder->private_->metadata.data.stream_info.md5sum, &encoder->private_->md5context);
+       FLAC__MD5Final(encoder->private_->streaminfo.data.stream_info.md5sum, &encoder->private_->md5context);
 
        if(encoder->protected_->state == FLAC__STREAM_ENCODER_OK && !encoder->private_->is_being_deleted) {
-               encoder->private_->metadata_callback(encoder, &encoder->private_->metadata, encoder->private_->client_data);
+               if(encoder->private_->seek_callback) {
+#if FLAC__HAS_OGG
+                       if(encoder->private_->is_ogg)
+                               update_ogg_metadata_(encoder);
+                       else
+#endif
+                       update_metadata_(encoder);
+               }
+               if(encoder->private_->metadata_callback)
+                       encoder->private_->metadata_callback(encoder, &encoder->private_->streaminfo, encoder->private_->client_data);
        }
 
        if(encoder->protected_->verify && 0 != encoder->private_->verify.decoder)
                FLAC__stream_decoder_finish(encoder->private_->verify.decoder);
 
+       if(0 != encoder->private_->file) {
+               if(encoder->private_->file != stdout)
+                       fclose(encoder->private_->file);
+               encoder->private_->file = 0;
+       }
+
+#if FLAC__HAS_OGG
+       if(encoder->private_->is_ogg)
+               FLAC__ogg_encoder_aspect_finish(&encoder->protected_->ogg_encoder_aspect);
+#endif
+
        free_(encoder);
        set_defaults_(encoder);
 
        encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED;
 }
 
+FLAC_API FLAC__bool FLAC__stream_encoder_set_serial_number(FLAC__StreamEncoder *encoder, long value)
+{
+       FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
+       if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
+               return false;
+#ifdef FLAC__HAS_OGG
+       /* can't check encoder->private_->is_ogg since that's not set until init time */
+       FLAC__ogg_encoder_aspect_set_serial_number(&encoder->protected_->ogg_encoder_aspect, value);
+       return true;
+#else
+       (void)value;
+       return false;
+#endif
+}
+
 FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
 #ifndef FLAC__MANDATORY_VERIFY_WHILE_ENCODING
@@ -1046,6 +1403,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder
 FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->streamable_subset = value;
@@ -1055,6 +1414,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncod
 FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->do_mid_side_stereo = value;
@@ -1064,6 +1425,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEnco
 FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->loose_mid_side_stereo = value;
@@ -1073,6 +1436,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamE
 FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->channels = value;
@@ -1082,6 +1447,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encod
 FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->bits_per_sample = value;
@@ -1091,6 +1458,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder
 FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->sample_rate = value;
@@ -1100,6 +1469,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *en
 FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->blocksize = value;
@@ -1109,6 +1480,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *enco
 FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *encoder, const char *specification)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        FLAC__ASSERT(0 != specification);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
@@ -1171,9 +1544,6 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *en
                encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY;
                encoder->protected_->apodizations[0].parameters.tukey.p = 0.5;
        }
-#ifdef WINDOW_DEBUG_OUTPUT
-{unsigned n;for(n=0;n<encoder->protected_->num_apodizations;n++)fprintf(stderr,"@@@@@@ parsed apodization[%zu]: %s\n",n,winstr[encoder->protected_->apodizations[n].type]);}
-#endif
 #endif
        return true;
 }
@@ -1181,6 +1551,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *en
 FLAC_API FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->max_lpc_order = value;
@@ -1190,6 +1562,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder *
 FLAC_API FLAC__bool FLAC__stream_encoder_set_qlp_coeff_precision(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->qlp_coeff_precision = value;
@@ -1199,6 +1573,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_qlp_coeff_precision(FLAC__StreamEnc
 FLAC_API FLAC__bool FLAC__stream_encoder_set_do_qlp_coeff_prec_search(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->do_qlp_coeff_prec_search = value;
@@ -1208,6 +1584,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_do_qlp_coeff_prec_search(FLAC__Stre
 FLAC_API FLAC__bool FLAC__stream_encoder_set_do_escape_coding(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
 #if 0
@@ -1222,6 +1600,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_do_escape_coding(FLAC__StreamEncode
 FLAC_API FLAC__bool FLAC__stream_encoder_set_do_exhaustive_model_search(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->do_exhaustive_model_search = value;
@@ -1231,6 +1611,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_do_exhaustive_model_search(FLAC__St
 FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->min_residual_partition_order = value;
@@ -1240,6 +1622,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__
 FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->max_residual_partition_order = value;
@@ -1249,6 +1633,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__
 FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
 #if 0
@@ -1263,6 +1649,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__St
 FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->total_samples_estimate = value;
@@ -1272,39 +1660,16 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__Stream
 FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->protected_->metadata = metadata;
        encoder->protected_->num_metadata_blocks = num_blocks;
-       return true;
-}
-
-FLAC_API FLAC__bool FLAC__stream_encoder_set_write_callback(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderWriteCallback value)
-{
-       FLAC__ASSERT(0 != encoder);
-       FLAC__ASSERT(0 != value);
-       if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
-               return false;
-       encoder->private_->write_callback = value;
-       return true;
-}
-
-FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata_callback(FLAC__StreamEncoder *encoder, FLAC__StreamEncoderMetadataCallback value)
-{
-       FLAC__ASSERT(0 != encoder);
-       FLAC__ASSERT(0 != value);
-       if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
+#if FLAC__HAS_OGG
+       if(!FLAC__ogg_encoder_aspect_set_num_metadata(&encoder->protected_->ogg_encoder_aspect, num_blocks))
                return false;
-       encoder->private_->metadata_callback = value;
-       return true;
-}
-
-FLAC_API FLAC__bool FLAC__stream_encoder_set_client_data(FLAC__StreamEncoder *encoder, void *value)
-{
-       FLAC__ASSERT(0 != encoder);
-       if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
-               return false;
-       encoder->private_->client_data = value;
+#endif
        return true;
 }
 
@@ -1315,6 +1680,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_client_data(FLAC__StreamEncoder *en
 FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->private_->disable_constant_subframes = value;
@@ -1324,6 +1691,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__Stream
 FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->private_->disable_fixed_subframes = value;
@@ -1333,6 +1702,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEnc
 FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED)
                return false;
        encoder->private_->disable_verbatim_subframes = value;
@@ -1342,12 +1713,16 @@ FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__Stream
 FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->state;
 }
 
 FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->verify)
                return FLAC__stream_decoder_get_state(encoder->private_->verify.decoder);
        else
@@ -1356,6 +1731,9 @@ FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(
 
 FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder)
 {
+       FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(encoder->protected_->state != FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR)
                return FLAC__StreamEncoderStateString[encoder->protected_->state];
        else
@@ -1365,6 +1743,8 @@ FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__
 FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        if(0 != absolute_sample)
                *absolute_sample = encoder->private_->verify.error_stats.absolute_sample;
        if(0 != frame_number)
@@ -1382,102 +1762,136 @@ FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__St
 FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->verify;
 }
 
 FLAC_API FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->streamable_subset;
 }
 
 FLAC_API FLAC__bool FLAC__stream_encoder_get_do_mid_side_stereo(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->do_mid_side_stereo;
 }
 
 FLAC_API FLAC__bool FLAC__stream_encoder_get_loose_mid_side_stereo(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->loose_mid_side_stereo;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->channels;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->bits_per_sample;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->sample_rate;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->blocksize;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->max_lpc_order;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->qlp_coeff_precision;
 }
 
 FLAC_API FLAC__bool FLAC__stream_encoder_get_do_qlp_coeff_prec_search(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->do_qlp_coeff_prec_search;
 }
 
 FLAC_API FLAC__bool FLAC__stream_encoder_get_do_escape_coding(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->do_escape_coding;
 }
 
 FLAC_API FLAC__bool FLAC__stream_encoder_get_do_exhaustive_model_search(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->do_exhaustive_model_search;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->min_residual_partition_order;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->max_residual_partition_order;
 }
 
 FLAC_API unsigned FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->rice_parameter_search_dist;
 }
 
 FLAC_API FLAC__uint64 FLAC__stream_encoder_get_total_samples_estimate(const FLAC__StreamEncoder *encoder)
 {
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        return encoder->protected_->total_samples_estimate;
 }
 
@@ -1488,6 +1902,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c
        const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize;
 
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK);
 
        j = 0;
@@ -1618,6 +2034,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder
        const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize;
 
        FLAC__ASSERT(0 != encoder);
+       FLAC__ASSERT(0 != encoder->private_);
+       FLAC__ASSERT(0 != encoder->protected_);
        FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK);
 
        j = k = 0;
@@ -1780,12 +2198,24 @@ void set_defaults_(FLAC__StreamEncoder *encoder)
        encoder->protected_->metadata = 0;
        encoder->protected_->num_metadata_blocks = 0;
 
+       encoder->private_->seek_table = 0;
        encoder->private_->disable_constant_subframes = false;
        encoder->private_->disable_fixed_subframes = false;
        encoder->private_->disable_verbatim_subframes = false;
+#if FLAC__HAS_OGG
+       encoder->private_->is_ogg = false;
+#endif
+       encoder->private_->read_callback = 0;
        encoder->private_->write_callback = 0;
+       encoder->private_->seek_callback = 0;
+       encoder->private_->tell_callback = 0;
        encoder->private_->metadata_callback = 0;
+       encoder->private_->progress_callback = 0;
        encoder->private_->client_data = 0;
+
+#if FLAC__HAS_OGG
+       FLAC__ogg_encoder_aspect_set_defaults(&encoder->protected_->ogg_encoder_aspect);
+#endif
 }
 
 void free_(FLAC__StreamEncoder *encoder)
@@ -2022,22 +2452,443 @@ FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples)
                }
        }
 
-       if(encoder->private_->write_callback(encoder, buffer, bytes, samples, encoder->private_->current_frame_number, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+       if(write_frame_(encoder, buffer, bytes, samples) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
                FLAC__bitbuffer_release_buffer(encoder->private_->frame);
-               encoder->protected_->state = FLAC__STREAM_ENCODER_FATAL_ERROR_WHILE_WRITING;
+               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
                return false;
        }
 
        FLAC__bitbuffer_release_buffer(encoder->private_->frame);
 
        if(samples > 0) {
-               encoder->private_->metadata.data.stream_info.min_framesize = min(bytes, encoder->private_->metadata.data.stream_info.min_framesize);
-               encoder->private_->metadata.data.stream_info.max_framesize = max(bytes, encoder->private_->metadata.data.stream_info.max_framesize);
+               encoder->private_->streaminfo.data.stream_info.min_framesize = min(bytes, encoder->private_->streaminfo.data.stream_info.min_framesize);
+               encoder->private_->streaminfo.data.stream_info.max_framesize = max(bytes, encoder->private_->streaminfo.data.stream_info.max_framesize);
        }
 
        return true;
 }
 
+FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples)
+{
+       FLAC__StreamEncoderWriteStatus status;
+       FLAC__uint64 output_position = 0;
+
+       /* FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED just means we didn't get the offset; no error */
+       if(encoder->private_->tell_callback && encoder->private_->tell_callback(encoder, &output_position, encoder->private_->client_data) == FLAC__STREAM_ENCODER_TELL_STATUS_ERROR) {
+               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+               return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
+       }
+
+       /*
+        * Watch for the STREAMINFO block and first SEEKTABLE block to go by and store their offsets.
+        */
+       if(samples == 0) {
+               FLAC__MetadataType type = (buffer[0] & 0x7f);
+               if(type == FLAC__METADATA_TYPE_STREAMINFO)
+                       encoder->protected_->streaminfo_offset = output_position;
+               else if(type == FLAC__METADATA_TYPE_SEEKTABLE && encoder->protected_->seektable_offset == 0)
+                       encoder->protected_->seektable_offset = output_position;
+       }
+
+       /*
+        * Mark the current seek point if hit (if audio_offset == 0 that
+        * means we're still writing metadata and haven't hit the first
+        * frame yet)
+        */
+       if(0 != encoder->private_->seek_table && encoder->protected_->audio_offset > 0 && encoder->private_->seek_table->num_points > 0) {
+               const unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder);
+               const FLAC__uint64 frame_first_sample = encoder->private_->samples_written;
+               const FLAC__uint64 frame_last_sample = frame_first_sample + (FLAC__uint64)blocksize - 1;
+               FLAC__uint64 test_sample;
+               unsigned i;
+               for(i = encoder->private_->first_seekpoint_to_check; i < encoder->private_->seek_table->num_points; i++) {
+                       test_sample = encoder->private_->seek_table->points[i].sample_number;
+                       if(test_sample > frame_last_sample) {
+                               break;
+                       }
+                       else if(test_sample >= frame_first_sample) {
+                               encoder->private_->seek_table->points[i].sample_number = frame_first_sample;
+                               encoder->private_->seek_table->points[i].stream_offset = output_position - encoder->protected_->audio_offset;
+                               encoder->private_->seek_table->points[i].frame_samples = blocksize;
+                               encoder->private_->first_seekpoint_to_check++;
+                               /* DO NOT: "break;" and here's why:
+                                * The seektable template may contain more than one target
+                                * sample for any given frame; we will keep looping, generating
+                                * duplicate seekpoints for them, and we'll clean it up later,
+                                * just before writing the seektable back to the metadata.
+                                */
+                       }
+                       else {
+                               encoder->private_->first_seekpoint_to_check++;
+                       }
+               }
+       }
+
+#if FLAC__HAS_OGG
+       if(encoder->private_->is_ogg) {
+               status = FLAC__ogg_encoder_aspect_write_callback_wrapper(
+                       &encoder->protected_->ogg_encoder_aspect,
+                       FLAC__stream_encoder_get_total_samples_estimate(encoder),
+                       buffer,
+                       bytes,
+                       samples,
+                       encoder->private_->current_frame_number,
+                       (FLAC__OggEncoderAspectWriteCallbackProxy)encoder->private_->write_callback,
+                       encoder,
+                       encoder->private_->client_data
+               );
+       }
+       else
+#endif
+       status = encoder->private_->write_callback(encoder, buffer, bytes, samples, encoder->private_->current_frame_number, encoder->private_->client_data);
+
+       if(status == FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+               encoder->private_->bytes_written += bytes;
+               encoder->private_->samples_written += samples;
+               /* we keep a high watermark on the number of frames written because
+                * when the encoder goes back to write metadata, 'current_frame'
+                * will drop back to 0.
+                */
+               encoder->private_->frames_written = max(encoder->private_->frames_written, encoder->private_->current_frame_number+1);
+       }
+       else
+               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+
+       return status;
+}
+
+/* Gets called when the encoding process has finished so that we can update the STREAMINFO and SEEKTABLE blocks.  */
+void update_metadata_(const FLAC__StreamEncoder *encoder)
+{
+       FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)];
+       const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo;
+       const FLAC__uint64 samples = metadata->data.stream_info.total_samples;
+       const unsigned min_framesize = metadata->data.stream_info.min_framesize;
+       const unsigned max_framesize = metadata->data.stream_info.max_framesize;
+       const unsigned bps = metadata->data.stream_info.bits_per_sample;
+       FLAC__StreamEncoderSeekStatus seek_status;
+
+       FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO);
+
+       /* All this is based on intimate knowledge of the stream header
+        * layout, but a change to the header format that would break this
+        * would also break all streams encoded in the previous format.
+        */
+
+       /*
+        * Write MD5 signature
+        */
+       {
+               const unsigned md5_offset =
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN
+                       ) / 8;
+
+               if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + md5_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) {
+                       if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR)
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                       return;
+               }
+               if(encoder->private_->write_callback(encoder, metadata->data.stream_info.md5sum, 16, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                       return;
+               }
+       }
+
+       /*
+        * Write total samples
+        */
+       {
+               const unsigned total_samples_byte_offset =
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN
+                               - 4
+                       ) / 8;
+
+               b[0] = ((FLAC__byte)(bps-1) << 4) | (FLAC__byte)((samples >> 32) & 0x0F);
+               b[1] = (FLAC__byte)((samples >> 24) & 0xFF);
+               b[2] = (FLAC__byte)((samples >> 16) & 0xFF);
+               b[3] = (FLAC__byte)((samples >> 8) & 0xFF);
+               b[4] = (FLAC__byte)(samples & 0xFF);
+               if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + total_samples_byte_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) {
+                       if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR)
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                       return;
+               }
+               if(encoder->private_->write_callback(encoder, b, 5, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                       return;
+               }
+       }
+
+       /*
+        * Write min/max framesize
+        */
+       {
+               const unsigned min_framesize_offset =
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN
+                       ) / 8;
+
+               b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF);
+               b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF);
+               b[2] = (FLAC__byte)(min_framesize & 0xFF);
+               b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF);
+               b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF);
+               b[5] = (FLAC__byte)(max_framesize & 0xFF);
+               if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + min_framesize_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) {
+                       if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR)
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                       return;
+               }
+               if(encoder->private_->write_callback(encoder, b, 6, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                       return;
+               }
+       }
+
+       /*
+        * Write seektable
+        */
+       if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) {
+               unsigned i;
+
+               FLAC__format_seektable_sort(encoder->private_->seek_table);
+
+               FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table));
+
+               if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->seektable_offset + FLAC__STREAM_METADATA_HEADER_LENGTH, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) {
+                       if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR)
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                       return;
+               }
+
+               for(i = 0; i < encoder->private_->seek_table->num_points; i++) {
+                       FLAC__uint64 xx;
+                       unsigned x;
+                       xx = encoder->private_->seek_table->points[i].sample_number;
+                       b[7] = (FLAC__byte)xx; xx >>= 8;
+                       b[6] = (FLAC__byte)xx; xx >>= 8;
+                       b[5] = (FLAC__byte)xx; xx >>= 8;
+                       b[4] = (FLAC__byte)xx; xx >>= 8;
+                       b[3] = (FLAC__byte)xx; xx >>= 8;
+                       b[2] = (FLAC__byte)xx; xx >>= 8;
+                       b[1] = (FLAC__byte)xx; xx >>= 8;
+                       b[0] = (FLAC__byte)xx; xx >>= 8;
+                       xx = encoder->private_->seek_table->points[i].stream_offset;
+                       b[15] = (FLAC__byte)xx; xx >>= 8;
+                       b[14] = (FLAC__byte)xx; xx >>= 8;
+                       b[13] = (FLAC__byte)xx; xx >>= 8;
+                       b[12] = (FLAC__byte)xx; xx >>= 8;
+                       b[11] = (FLAC__byte)xx; xx >>= 8;
+                       b[10] = (FLAC__byte)xx; xx >>= 8;
+                       b[9] = (FLAC__byte)xx; xx >>= 8;
+                       b[8] = (FLAC__byte)xx; xx >>= 8;
+                       x = encoder->private_->seek_table->points[i].frame_samples;
+                       b[17] = (FLAC__byte)x; x >>= 8;
+                       b[16] = (FLAC__byte)x; x >>= 8;
+                       if(encoder->private_->write_callback(encoder, b, 18, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                               return;
+                       }
+               }
+       }
+}
+
+#if FLAC__HAS_OGG
+/* Gets called when the encoding process has finished so that we can update the STREAMINFO and SEEKTABLE blocks.  */
+void update_ogg_metadata_(FLAC__StreamEncoder *encoder)
+{
+       FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)];
+       const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo;
+       const FLAC__uint64 samples = metadata->data.stream_info.total_samples;
+       const unsigned min_framesize = metadata->data.stream_info.min_framesize;
+       const unsigned max_framesize = metadata->data.stream_info.max_framesize;
+       ogg_page page;
+
+       FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO);
+
+       /* All this is based on intimate knowledge of the stream header
+        * layout, but a change to the header format that would break this
+        * would also break all streams encoded in the previous format.
+        */
+
+       /**
+        ** Write STREAMINFO stats
+        **/
+       simple_ogg_page__init(&page);
+       if(!simple_ogg_page__get_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) {
+               simple_ogg_page__clear(&page);
+               return; /* state already set */
+       }
+
+       /*
+        * Write MD5 signature
+        */
+       {
+               const unsigned md5_offset =
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN
+                       ) / 8;
+
+               if(md5_offset + 16 > (unsigned)page.body_len) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
+                       simple_ogg_page__clear(&page);
+                       return;
+               }
+               memcpy(page.body + md5_offset, metadata->data.stream_info.md5sum, 16);
+       }
+
+       /*
+        * Write total samples
+        */
+       {
+               const unsigned total_samples_byte_offset =
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN
+                               - 4
+                       ) / 8;
+
+               if(total_samples_byte_offset + 5 > (unsigned)page.body_len) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
+                       simple_ogg_page__clear(&page);
+                       return;
+               }
+               b[0] = (FLAC__byte)page.body[total_samples_byte_offset] & 0xF0;
+               b[0] |= (FLAC__byte)((samples >> 32) & 0x0F);
+               b[1] = (FLAC__byte)((samples >> 24) & 0xFF);
+               b[2] = (FLAC__byte)((samples >> 16) & 0xFF);
+               b[3] = (FLAC__byte)((samples >> 8) & 0xFF);
+               b[4] = (FLAC__byte)(samples & 0xFF);
+               memcpy(page.body + total_samples_byte_offset, b, 5);
+       }
+
+       /*
+        * Write min/max framesize
+        */
+       {
+               const unsigned min_framesize_offset =
+                       FLAC__STREAM_METADATA_HEADER_LENGTH +
+                       (
+                               FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN +
+                               FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN
+                       ) / 8;
+
+               if(min_framesize_offset + 6 > (unsigned)page.body_len) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
+                       simple_ogg_page__clear(&page);
+                       return;
+               }
+               b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF);
+               b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF);
+               b[2] = (FLAC__byte)(min_framesize & 0xFF);
+               b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF);
+               b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF);
+               b[5] = (FLAC__byte)(max_framesize & 0xFF);
+               memcpy(page.body + min_framesize_offset, b, 6);
+       }
+       if(!simple_ogg_page__set_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) {
+               simple_ogg_page__clear(&page);
+               return; /* state already set */
+       }
+       simple_ogg_page__clear(&page);
+
+       /*
+        * Write seektable
+        */
+       if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) {
+               unsigned i;
+               FLAC__byte *p;
+
+               FLAC__format_seektable_sort(encoder->private_->seek_table);
+
+               FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table));
+
+               simple_ogg_page__init(&page);
+               if(!simple_ogg_page__get_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) {
+                       simple_ogg_page__clear(&page);
+                       return; /* state already set */
+               }
+
+               if(FLAC__STREAM_METADATA_HEADER_LENGTH + (18*encoder->private_->seek_table->num_points) > (unsigned)page.body_len) {
+                       encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
+                       simple_ogg_page__clear(&page);
+                       return;
+               }
+
+               for(i = 0, p = page.body + FLAC__STREAM_METADATA_HEADER_LENGTH; i < encoder->private_->seek_table->num_points; i++, p += 18) {
+                       FLAC__uint64 xx;
+                       unsigned x;
+                       xx = encoder->private_->seek_table->points[i].sample_number;
+                       b[7] = (FLAC__byte)xx; xx >>= 8;
+                       b[6] = (FLAC__byte)xx; xx >>= 8;
+                       b[5] = (FLAC__byte)xx; xx >>= 8;
+                       b[4] = (FLAC__byte)xx; xx >>= 8;
+                       b[3] = (FLAC__byte)xx; xx >>= 8;
+                       b[2] = (FLAC__byte)xx; xx >>= 8;
+                       b[1] = (FLAC__byte)xx; xx >>= 8;
+                       b[0] = (FLAC__byte)xx; xx >>= 8;
+                       xx = encoder->private_->seek_table->points[i].stream_offset;
+                       b[15] = (FLAC__byte)xx; xx >>= 8;
+                       b[14] = (FLAC__byte)xx; xx >>= 8;
+                       b[13] = (FLAC__byte)xx; xx >>= 8;
+                       b[12] = (FLAC__byte)xx; xx >>= 8;
+                       b[11] = (FLAC__byte)xx; xx >>= 8;
+                       b[10] = (FLAC__byte)xx; xx >>= 8;
+                       b[9] = (FLAC__byte)xx; xx >>= 8;
+                       b[8] = (FLAC__byte)xx; xx >>= 8;
+                       x = encoder->private_->seek_table->points[i].frame_samples;
+                       b[17] = (FLAC__byte)x; x >>= 8;
+                       b[16] = (FLAC__byte)x; x >>= 8;
+                       if(encoder->private_->write_callback(encoder, b, 18, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
+                               simple_ogg_page__clear(&page);
+                               return;
+                       }
+                       memcpy(p, b, 18);
+               }
+
+               if(!simple_ogg_page__set_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) {
+                       simple_ogg_page__clear(&page);
+                       return; /* state already set */
+               }
+               simple_ogg_page__clear(&page);
+       }
+}
+#endif
+
 FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_frame)
 {
        FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK);
@@ -2085,7 +2936,7 @@ FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_frame
         */
        encoder->private_->current_sample_number = 0;
        encoder->private_->current_frame_number++;
-       encoder->private_->metadata.data.stream_info.total_samples += (FLAC__uint64)encoder->protected_->blocksize;
+       encoder->private_->streaminfo.data.stream_info.total_samples += (FLAC__uint64)encoder->protected_->blocksize;
 
        return true;
 }
@@ -2192,9 +3043,6 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_f
                                        encoder->private_->residual_workspace[channel],
                                        encoder->private_->best_subframe+channel,
                                        encoder->private_->best_subframe_bits+channel
-#ifdef WINDOW_DEBUG_OUTPUT
-                                       ,channel
-#endif
                                )
                        )
                                return false;
@@ -2225,9 +3073,6 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_f
                                        encoder->private_->residual_workspace_mid_side[channel],
                                        encoder->private_->best_subframe_mid_side+channel,
                                        encoder->private_->best_subframe_bits_mid_side+channel
-#ifdef WINDOW_DEBUG_OUTPUT
-                                       ,channel
-#endif
                                )
                        )
                                return false;
@@ -2241,9 +3086,6 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_f
                unsigned left_bps = 0, right_bps = 0; /* initialized only to prevent superfluous compiler warning */
                FLAC__Subframe *left_subframe = 0, *right_subframe = 0; /* initialized only to prevent superfluous compiler warning */
                FLAC__ChannelAssignment channel_assignment;
-#ifdef WINDOW_DEBUG_OUTPUT
-               unsigned left_bits = 0, right_bits = 0;
-#endif
 
                FLAC__ASSERT(encoder->protected_->channels == 2);
 
@@ -2273,7 +3115,7 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_f
 
                frame_header.channel_assignment = channel_assignment;
 
-               if(!FLAC__frame_add_header(&frame_header, encoder->protected_->streamable_subset, encoder->private_->frame)) {
+               if(!FLAC__frame_add_header(&frame_header, encoder->private_->frame)) {
                        encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
                        return false;
                }
@@ -2282,34 +3124,18 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_f
                        case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT:
                                left_subframe  = &encoder->private_->subframe_workspace         [0][encoder->private_->best_subframe         [0]];
                                right_subframe = &encoder->private_->subframe_workspace         [1][encoder->private_->best_subframe         [1]];
-#ifdef WINDOW_DEBUG_OUTPUT
-                               left_bits      = encoder->private_->best_subframe_bits          [0];
-                               right_bits     = encoder->private_->best_subframe_bits          [1];
-#endif
                                break;
                        case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE:
                                left_subframe  = &encoder->private_->subframe_workspace         [0][encoder->private_->best_subframe         [0]];
                                right_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]];
-#ifdef WINDOW_DEBUG_OUTPUT
-                               left_bits      = encoder->private_->best_subframe_bits          [0];
-                               right_bits     = encoder->private_->best_subframe_bits_mid_side [1];
-#endif
                                break;
                        case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE:
                                left_subframe  = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]];
                                right_subframe = &encoder->private_->subframe_workspace         [1][encoder->private_->best_subframe         [1]];
-#ifdef WINDOW_DEBUG_OUTPUT
-                               left_bits      = encoder->private_->best_subframe_bits_mid_side [1];
-                               right_bits     = encoder->private_->best_subframe_bits          [1];
-#endif
                                break;
                        case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE:
                                left_subframe  = &encoder->private_->subframe_workspace_mid_side[0][encoder->private_->best_subframe_mid_side[0]];
                                right_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]];
-#ifdef WINDOW_DEBUG_OUTPUT
-                               left_bits      = encoder->private_->best_subframe_bits_mid_side [0];
-                               right_bits     = encoder->private_->best_subframe_bits_mid_side [1];
-#endif
                                break;
                        default:
                                FLAC__ASSERT(0);
@@ -2337,31 +3163,19 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_last_f
                }
 
                /* note that encoder_add_subframe_ sets the state for us in case of an error */
-#ifdef WINDOW_DEBUG_OUTPUT
-               if(!add_subframe_(encoder, &frame_header, left_bps , left_subframe , encoder->private_->frame, left_bits))
-                       return false;
-               if(!add_subframe_(encoder, &frame_header, right_bps, right_subframe, encoder->private_->frame, right_bits))
-                       return false;
-#else
                if(!add_subframe_(encoder, &frame_header, left_bps , left_subframe , encoder->private_->frame))
                        return false;
                if(!add_subframe_(encoder, &frame_header, right_bps, right_subframe, encoder->private_->frame))
                        return false;
-#endif
        }
        else {
-               if(!FLAC__frame_add_header(&frame_header, encoder->protected_->streamable_subset, encoder->private_->frame)) {
+               if(!FLAC__frame_add_header(&frame_header, encoder->private_->frame)) {
                        encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
                        return false;
                }
 
                for(channel = 0; channel < encoder->protected_->channels; channel++) {
-#ifdef WINDOW_DEBUG_OUTPUT
-                       if(!add_subframe_(encoder, &frame_header, encoder->private_->subframe_bps[channel], &encoder->private_->subframe_workspace[channel][encoder->private_->best_subframe[channel]], encoder->private_->frame, encoder->private_->best_subframe_bits[channel]))
-#else
-                       if(!add_subframe_(encoder, &frame_header, encoder->private_->subframe_bps[channel], &encoder->private_->subframe_workspace[channel][encoder->private_->best_subframe[channel]], encoder->private_->frame))
-#endif
-                       {
+                       if(!add_subframe_(encoder, &frame_header, encoder->private_->subframe_bps[channel], &encoder->private_->subframe_workspace[channel][encoder->private_->best_subframe[channel]], encoder->private_->frame)) {
                                /* the above function sets the state for us in case of an error */
                                return false;
                        }
@@ -2395,9 +3209,6 @@ FLAC__bool process_subframe_(
        FLAC__int32 *residual[2],
        unsigned *best_subframe,
        unsigned *best_bits
-#ifdef WINDOW_DEBUG_OUTPUT
-       ,unsigned subframe_number
-#endif
 )
 {
 #ifndef FLAC__INTEGER_ONLY_LIBRARY
@@ -2586,11 +3397,6 @@ FLAC__bool process_subframe_(
                                                                                        encoder->protected_->rice_parameter_search_dist,
                                                                                        subframe[!_best_subframe],
                                                                                        partitioned_rice_contents[!_best_subframe]
-#ifdef WINDOW_DEBUG_OUTPUT
-                                                                                       ,frame_header->number.frame_number
-                                                                                       ,subframe_number
-                                                                                       ,encoder->protected_->apodizations[a]
-#endif
                                                                                );
                                                                        if(_candidate_bits > 0) { /* if == 0, there was a problem quantizing the lpcoeffs */
                                                                                if(_candidate_bits < _best_bits) {
@@ -2626,36 +3432,30 @@ FLAC__bool add_subframe_(
        unsigned subframe_bps,
        const FLAC__Subframe *subframe,
        FLAC__BitBuffer *frame
-#ifdef WINDOW_DEBUG_OUTPUT
-,unsigned subframe_bits
-#endif
 )
 {
        switch(subframe->type) {
                case FLAC__SUBFRAME_TYPE_CONSTANT:
                        if(!FLAC__subframe_add_constant(&(subframe->data.constant), subframe_bps, subframe->wasted_bits, frame)) {
-                               encoder->protected_->state = FLAC__STREAM_ENCODER_FATAL_ERROR_WHILE_ENCODING;
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
                                return false;
                        }
                        break;
                case FLAC__SUBFRAME_TYPE_FIXED:
                        if(!FLAC__subframe_add_fixed(&(subframe->data.fixed), frame_header->blocksize - subframe->data.fixed.order, subframe_bps, subframe->wasted_bits, frame)) {
-                               encoder->protected_->state = FLAC__STREAM_ENCODER_FATAL_ERROR_WHILE_ENCODING;
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
                                return false;
                        }
                        break;
                case FLAC__SUBFRAME_TYPE_LPC:
-#ifdef WINDOW_DEBUG_OUTPUT
-                       fprintf(stderr, "WIN:\tframe=%u\tsubframe=?\torder=%u\twindow=%s\tbits=%u\n", frame_header->number.frame_number, subframe->data.lpc.order, subframe->data.lpc.window_type, subframe_bits);
-#endif
                        if(!FLAC__subframe_add_lpc(&(subframe->data.lpc), frame_header->blocksize - subframe->data.lpc.order, subframe_bps, subframe->wasted_bits, frame)) {
-                               encoder->protected_->state = FLAC__STREAM_ENCODER_FATAL_ERROR_WHILE_ENCODING;
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
                                return false;
                        }
                        break;
                case FLAC__SUBFRAME_TYPE_VERBATIM:
                        if(!FLAC__subframe_add_verbatim(&(subframe->data.verbatim), frame_header->blocksize, subframe_bps, subframe->wasted_bits, frame)) {
-                               encoder->protected_->state = FLAC__STREAM_ENCODER_FATAL_ERROR_WHILE_ENCODING;
+                               encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
                                return false;
                        }
                        break;
@@ -2755,11 +3555,6 @@ unsigned evaluate_lpc_subframe_(
        unsigned rice_parameter_search_dist,
        FLAC__Subframe *subframe,
        FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents
-#ifdef WINDOW_DEBUG_OUTPUT
-       ,unsigned frame_number
-       ,unsigned subframe_number
-       ,FLAC__ApodizationSpecification aspec
-#endif
 )
 {
        FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER];
@@ -2774,15 +3569,6 @@ unsigned evaluate_lpc_subframe_(
                qlp_coeff_precision = min(qlp_coeff_precision, 32 - subframe_bps - FLAC__bitmath_ilog2(order));
        }
 
-#ifdef WINDOW_DEBUG_OUTPUT
-       if (aspec.type == FLAC__APODIZATION_GAUSS)
-               snprintf(subframe->data.lpc.window_type, sizeof subframe->data.lpc.window_type, "%s(%0.5f)", winstr[aspec.type], aspec.parameters.gauss.stddev);
-       else if (aspec.type == FLAC__APODIZATION_TUKEY)
-               snprintf(subframe->data.lpc.window_type, sizeof subframe->data.lpc.window_type, "%s(%0.5f)", winstr[aspec.type], aspec.parameters.tukey.p);
-       else
-               strncpy(subframe->data.lpc.window_type, winstr[aspec.type], sizeof subframe->data.lpc.window_type);
-#endif
-
        ret = FLAC__lpc_quantize_coefficients(lp_coeff, order, qlp_coeff_precision, qlp_coeff, &quantization);
        if(ret != 0)
                return 0; /* this is a hack to indicate to the caller that we can't do lp at this order on this subframe */
@@ -2826,9 +3612,6 @@ unsigned evaluate_lpc_subframe_(
        for(i = 0; i < order; i++)
                subframe->data.lpc.warmup[i] = signal[i];
 
-#ifdef WINDOW_DEBUG_OUTPUT
-       fprintf(stderr, "SWIN:\tframe=%u\tsubframe=%u\torder=%u\twindow=%s\tbits=%u\n", frame_number, subframe_number, order, subframe->data.lpc.window_type, FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN + FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN + (order * (qlp_coeff_precision + subframe_bps)) + residual_bits);
-#endif
        return FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN + FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN + (order * (qlp_coeff_precision + subframe_bps)) + residual_bits;
 }
 #endif
@@ -3086,7 +3869,7 @@ void precompute_partition_info_escapes_(
                        raw_bits_per_partition[partition] = max(silog2_min, silog2_max);
                }
                to_partition = partitions;
-               break; /*@@@@@@ yuck, should remove the 'for' loop instead */
+               break; /*@@@ yuck, should remove the 'for' loop instead */
        }
 
        /* now merge partitions for lower orders */
@@ -3612,3 +4395,108 @@ void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDeco
        (void)decoder, (void)status;
        encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR;
 }
+
+FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+{
+       (void)client_data;
+
+       *bytes = (unsigned)fread(buffer, 1, *bytes, encoder->private_->file);
+       if (*bytes == 0) {
+               if (feof(encoder->private_->file))
+                       return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM;
+               else if (ferror(encoder->private_->file))
+                       return FLAC__STREAM_ENCODER_READ_STATUS_ABORT;
+       }
+       return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+       (void)client_data;
+
+       if(fseeko(encoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0)
+               return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR;
+       else
+               return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+       off_t offset;
+
+       (void)client_data;
+
+       offset = ftello(encoder->private_->file);
+
+       if(offset < 0) {
+               return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR;
+       }
+       else {
+               *absolute_byte_offset = (FLAC__uint64)offset;
+               return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
+       }
+}
+
+#ifdef FLAC__VALGRIND_TESTING
+static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       size_t ret = fwrite(ptr, size, nmemb, stream);
+       if(!ferror(stream))
+               fflush(stream);
+       return ret;
+}
+#else
+#define local__fwrite fwrite
+#endif
+
+FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data)
+{
+       (void)client_data, (void)current_frame;
+
+       if(local__fwrite(buffer, sizeof(FLAC__byte), bytes, encoder->private_->file) == bytes) {
+               FLAC__bool call_it = 0 != encoder->private_->progress_callback && (
+#if FLAC__HAS_OGG
+                       /* We would like to be able to use 'samples > 0' in the
+                        * clause here but currently because of the nature of our
+                        * Ogg writing implementation, 'samples' is always 0 (see
+                        * ogg_encoder_aspect.c).  The downside is extra progress
+                        * callbacks.
+                        */
+                       encoder->private_->is_ogg? true :
+#endif
+                       samples > 0
+               );
+               if(call_it) {
+                       /* NOTE: We have to add +bytes, +samples, and +1 to the stats
+                        * because at this point in the callback chain, the stats
+                        * have not been updated.  Only after we return and control
+                        * gets back to write_frame_() are the stats updated
+                        */
+                       encoder->private_->progress_callback(encoder, encoder->private_->bytes_written+bytes, encoder->private_->samples_written+samples, encoder->private_->frames_written+(samples?1:0), encoder->private_->total_frames_estimate, encoder->private_->client_data);
+               }
+               return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+       }
+       else
+               return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
+}
+
+/*
+ * This will forcibly set stdout to binary mode (for OSes that require it)
+ */
+FILE *get_binary_stdout_()
+{
+       /* if something breaks here it is probably due to the presence or
+        * absence of an underscore before the identifiers 'setmode',
+        * 'fileno', and/or 'O_BINARY'; check your system header files.
+        */
+#if defined _MSC_VER || defined __MINGW32__
+       _setmode(_fileno(stdout), _O_BINARY);
+#elif defined __CYGWIN__
+       /* almost certainly not needed for any modern Cygwin, but let's be safe... */
+       setmode(_fileno(stdout), _O_BINARY);
+#elif defined __EMX__
+       setmode(fileno(stdout), O_BINARY);
+#endif
+
+       return stdout;
+}