standardize flac__ names
[platform/upstream/flac.git] / src / flac / decode.c
index d879a62..6859d51 100644 (file)
@@ -1,5 +1,5 @@
 /* flac - Command-line FLAC encoder/decoder
- * Copyright (C) 2000  Josh Coalson
+ * Copyright (C) 2000,2001  Josh Coalson
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -30,6 +30,9 @@
 typedef struct {
        FILE *fout;
        bool abort_flag;
+       bool analysis_mode;
+       analysis_options aopts;
+       bool test_only;
        bool is_wave_out;
        bool is_big_endian;
        bool is_unsigned_samples;
@@ -39,6 +42,7 @@ typedef struct {
        unsigned sample_rate;
        bool verbose;
        uint64 skip;
+       bool skip_count_too_high;
        uint64 samples_processed;
        unsigned frame_counter;
 } stream_info_struct;
@@ -50,74 +54,106 @@ static bool is_big_endian_host;
 static bool init(const char *infile, stream_info_struct *stream_info);
 static bool write_little_endian_uint16(FILE *f, uint16 val);
 static bool write_little_endian_uint32(FILE *f, uint32 val);
-static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__FrameHeader *header, const int32 *buffer[], void *client_data);
+static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data);
 static void metadata_callback(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data);
 static void error_callback(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
 static void print_stats(const stream_info_struct *stream_info);
 
-int decode_wav(const char *infile, const char *outfile, bool verbose, uint64 skip)
+int flac__decode_wav(const char *infile, const char *outfile, bool analysis_mode, analysis_options aopts, bool verbose, uint64 skip)
 {
+       bool md5_failure = false;
        stream_info_struct stream_info;
 
        decoder = 0;
        stream_info.abort_flag = false;
+       stream_info.analysis_mode = analysis_mode;
+       stream_info.aopts = aopts;
+       stream_info.test_only = (outfile == 0);
        stream_info.is_wave_out = true;
        stream_info.verbose = verbose;
        stream_info.skip = skip;
+       stream_info.skip_count_too_high = false;
        stream_info.samples_processed = 0;
        stream_info.frame_counter = 0;
+       stream_info.fout = 0; /* initialized with an open file later if necessary */
 
-       if(0 == strcmp(outfile, "-")) {
-               stream_info.fout = stdout;
-       }
-       else {
-               if(0 == (stream_info.fout = fopen(outfile, "wb"))) {
-                       fprintf(stderr, "ERROR: can't open output file %s\n", outfile);
-                       return false;
+       FLAC__ASSERT(!(stream_info.test_only && stream_info.analysis_mode));
+
+       if(!stream_info.test_only) {
+               if(0 == strcmp(outfile, "-")) {
+                       stream_info.fout = stdout;
+               }
+               else {
+                       if(0 == (stream_info.fout = fopen(outfile, "wb"))) {
+                               fprintf(stderr, "ERROR: can't open output file %s\n", outfile);
+                               return false;
+                       }
                }
        }
 
+       if(analysis_mode)
+               analyze_init(aopts);
+
        if(!init(infile, &stream_info))
                goto wav_abort_;
 
        if(skip > 0) {
                if(!FLAC__file_decoder_process_metadata(decoder)) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
+                       goto wav_abort_;
+               }
+               if(stream_info.skip_count_too_high) {
+                       fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", infile);
                        goto wav_abort_;
                }
                if(!FLAC__file_decoder_seek_absolute(decoder, skip)) {
-                       fprintf(stderr, "ERROR seeking while skipping bytes in input file %s\n", infile);
+                       fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto wav_abort_;
                }
                if(!FLAC__file_decoder_process_remaining_frames(decoder)) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR while decoding frames, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto wav_abort_;
                }
                if(decoder->state != FLAC__FILE_DECODER_OK && decoder->state != FLAC__FILE_DECODER_END_OF_FILE) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto wav_abort_;
                }
        }
        else {
                if(!FLAC__file_decoder_process_whole_file(decoder)) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto wav_abort_;
                }
                if(decoder->state != FLAC__FILE_DECODER_OK && decoder->state != FLAC__FILE_DECODER_END_OF_FILE) {
-                       fprintf(stderr, "ERROR during decoding, state=%d:%s\n", decoder->state, FLAC__FileDecoderStateString[decoder->state]);
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto wav_abort_;
                }
        }
 
        if(decoder) {
                if(decoder->state != FLAC__FILE_DECODER_UNINITIALIZED)
-                       FLAC__file_decoder_finish(decoder);
+                       md5_failure = !FLAC__file_decoder_finish(decoder);
                print_stats(&stream_info);
                FLAC__file_decoder_free_instance(decoder);
        }
-       fclose(stream_info.fout);
+       if(0 != stream_info.fout && stream_info.fout != stdout)
+               fclose(stream_info.fout);
        if(verbose)
-               printf("\n");
+               fprintf(stderr, "\n");
+       if(analysis_mode)
+               analyze_finish(aopts);
+       if(md5_failure) {
+               fprintf(stderr, "%s: WARNING, MD5 signature mismatch\n", infile);
+               return 1;
+       }
+       else {
+               if(stream_info.test_only)
+                       fprintf(stderr, "%s: ok\n", infile);
+       }
        return 0;
 wav_abort_:
        if(decoder) {
@@ -125,76 +161,112 @@ wav_abort_:
                        FLAC__file_decoder_finish(decoder);
                FLAC__file_decoder_free_instance(decoder);
        }
-       fclose(stream_info.fout);
-       unlink(outfile);
+       if(0 != stream_info.fout && stream_info.fout != stdout) {
+               fclose(stream_info.fout);
+               unlink(outfile);
+       }
+       if(analysis_mode)
+               analyze_finish(aopts);
        return 1;
 }
 
-int decode_raw(const char *infile, const char *outfile, bool verbose, uint64 skip, bool is_big_endian, bool is_unsigned_samples)
+int flac__decode_raw(const char *infile, const char *outfile, bool analysis_mode, analysis_options aopts, bool verbose, uint64 skip, bool is_big_endian, bool is_unsigned_samples)
 {
+       bool md5_failure = false;
        stream_info_struct stream_info;
 
        decoder = 0;
        stream_info.abort_flag = false;
+       stream_info.analysis_mode = analysis_mode;
+       stream_info.aopts = aopts;
+       stream_info.test_only = (outfile == 0);
        stream_info.is_wave_out = false;
        stream_info.is_big_endian = is_big_endian;
        stream_info.is_unsigned_samples = is_unsigned_samples;
        stream_info.verbose = verbose;
        stream_info.skip = skip;
+       stream_info.skip_count_too_high = false;
        stream_info.samples_processed = 0;
        stream_info.frame_counter = 0;
+       stream_info.fout = 0; /* initialized with an open file later if necessary */
 
-       if(0 == strcmp(outfile, "-")) {
-               stream_info.fout = stdout;
-       }
-       else {
-               if(0 == (stream_info.fout = fopen(outfile, "wb"))) {
-                       fprintf(stderr, "ERROR: can't open output file %s\n", outfile);
-                       return false;
+       FLAC__ASSERT(!(stream_info.test_only && stream_info.analysis_mode));
+
+       if(!stream_info.test_only) {
+               if(0 == strcmp(outfile, "-")) {
+                       stream_info.fout = stdout;
+               }
+               else {
+                       if(0 == (stream_info.fout = fopen(outfile, "wb"))) {
+                               fprintf(stderr, "ERROR: can't open output file %s\n", outfile);
+                               return false;
+                       }
                }
        }
 
+       if(analysis_mode)
+               analyze_init(aopts);
+
        if(!init(infile, &stream_info))
                goto raw_abort_;
 
        if(skip > 0) {
                if(!FLAC__file_decoder_process_metadata(decoder)) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
+                       goto raw_abort_;
+               }
+               if(stream_info.skip_count_too_high) {
+                       fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", infile);
                        goto raw_abort_;
                }
                if(!FLAC__file_decoder_seek_absolute(decoder, skip)) {
-                       fprintf(stderr, "ERROR seeking while skipping bytes in input file %s\n", infile);
+                       fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto raw_abort_;
                }
                if(!FLAC__file_decoder_process_remaining_frames(decoder)) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR while decoding frames, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto raw_abort_;
                }
                if(decoder->state != FLAC__FILE_DECODER_OK && decoder->state != FLAC__FILE_DECODER_END_OF_FILE) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto raw_abort_;
                }
        }
        else {
                if(!FLAC__file_decoder_process_whole_file(decoder)) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto raw_abort_;
                }
                if(decoder->state != FLAC__FILE_DECODER_OK && decoder->state != FLAC__FILE_DECODER_END_OF_FILE) {
-                       fprintf(stderr, "ERROR during decoding\n");
+                       if(verbose) fprintf(stderr, "\n");
+                       fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                        goto raw_abort_;
                }
        }
 
        if(decoder) {
                if(decoder->state != FLAC__FILE_DECODER_UNINITIALIZED)
-                       FLAC__file_decoder_finish(decoder);
+                       md5_failure = !FLAC__file_decoder_finish(decoder);
                print_stats(&stream_info);
                FLAC__file_decoder_free_instance(decoder);
        }
-       fclose(stream_info.fout);
+       if(0 != stream_info.fout && stream_info.fout != stdout)
+               fclose(stream_info.fout);
        if(verbose)
-               printf("\n");
+               fprintf(stderr, "\n");
+       if(analysis_mode)
+               analyze_finish(aopts);
+       if(md5_failure) {
+               fprintf(stderr, "%s: WARNING, MD5 signature mismatch\n", infile);
+               return 1;
+       }
+       else {
+               if(stream_info.test_only)
+                       fprintf(stderr, "%s: ok\n", infile);
+       }
        return 0;
 raw_abort_:
        if(decoder) {
@@ -202,8 +274,12 @@ raw_abort_:
                        FLAC__file_decoder_finish(decoder);
                FLAC__file_decoder_free_instance(decoder);
        }
-       fclose(stream_info.fout);
-       unlink(outfile);
+       if(0 != stream_info.fout && stream_info.fout != stdout) {
+               fclose(stream_info.fout);
+               unlink(outfile);
+       }
+       if(analysis_mode)
+               analyze_finish(aopts);
        return 1;
 }
 
@@ -218,9 +294,10 @@ bool init(const char *infile, stream_info_struct *stream_info)
                fprintf(stderr, "ERROR creating the decoder instance\n");
                return false;
        }
+       decoder->check_md5 = true;
 
        if(FLAC__file_decoder_init(decoder, infile, write_callback, metadata_callback, error_callback, stream_info) != FLAC__FILE_DECODER_OK) {
-               fprintf(stderr, "ERROR initializing decoder, state = %d\n", decoder->state);
+               fprintf(stderr, "ERROR initializing decoder, state=%d:%s\n", decoder->state, FLAC__FileDecoderStateString[decoder->state]);
                return false;
        }
 
@@ -248,18 +325,21 @@ bool write_little_endian_uint32(FILE *f, uint32 val)
        return fwrite(b, 1, 4, f) == 4;
 }
 
-FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__FrameHeader *header, const int32 *buffer[], void *client_data)
+FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data)
 {
        stream_info_struct *stream_info = (stream_info_struct *)client_data;
        FILE *fout = stream_info->fout;
        unsigned bps = stream_info->bps, channels = stream_info->channels;
        bool is_big_endian = (stream_info->is_wave_out? false : stream_info->is_big_endian);
-       bool is_unsigned_samples = (stream_info->is_wave_out? bps==8 : stream_info->is_unsigned_samples);
-       unsigned wide_samples = header->blocksize, wide_sample, sample, channel, byte;
-       static signed char scbuffer[FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS * ((FLAC__MAX_BITS_PER_SAMPLE+7)>>3)]; /* WATCHOUT: can be up to 2 megs */
-       unsigned char *ucbuffer = (unsigned char *)scbuffer;
-       signed short *ssbuffer = (signed short *)scbuffer;
-       unsigned short *usbuffer = (unsigned short *)scbuffer;
+       bool is_unsigned_samples = (stream_info->is_wave_out? bps<=8 : stream_info->is_unsigned_samples);
+       unsigned wide_samples = frame->header.blocksize, wide_sample, sample, channel, byte;
+       static int8 s8buffer[FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS * sizeof(int32)]; /* WATCHOUT: can be up to 2 megs */
+       /* WATCHOUT: we say 'sizeof(int32)' above instead of '(FLAC__MAX_BITS_PER_SAMPLE+7)/8' because we have to use an array int32 even for 24 bps */
+       uint8  *u8buffer  = (uint8  *)s8buffer;
+       int16  *s16buffer = (int16  *)s8buffer;
+       uint16 *u16buffer = (uint16 *)s8buffer;
+       int32  *s32buffer = (int32  *)s8buffer;
+       uint32 *u32buffer = (uint32 *)s8buffer;
 
        (void)decoder;
 
@@ -269,44 +349,99 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder,
        stream_info->samples_processed += wide_samples;
        stream_info->frame_counter++;
 
-       if(stream_info->verbose && !(stream_info->frame_counter & 0x1f))
+       if(stream_info->verbose && !(stream_info->frame_counter & 0x7f))
                print_stats(stream_info);
 
-       if(bps == 8) {
-               if(is_unsigned_samples) {
-                       for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
-                               for(channel = 0; channel < channels; channel++, sample++)
-                                       ucbuffer[sample] = buffer[channel][wide_sample] + 128;
-               }
-               else {
-                       for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
-                               for(channel = 0; channel < channels; channel++, sample++)
-                                       scbuffer[sample] = buffer[channel][wide_sample];
-               }
-               if(fwrite(ucbuffer, 1, sample, fout) != sample)
-                       return FLAC__STREAM_DECODER_WRITE_ABORT;
+       if(stream_info->analysis_mode) {
+               analyze_frame(frame, stream_info->frame_counter-1, stream_info->aopts, fout);
        }
-       else { /* bps == 16 */
-               if(is_unsigned_samples) {
-                       for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
-                               for(channel = 0; channel < channels; channel++, sample++)
-                                       usbuffer[sample] = buffer[channel][wide_sample] + 32768;
+       else if(!stream_info->test_only) {
+               if(bps == 8) {
+                       if(is_unsigned_samples) {
+                               for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+                                       for(channel = 0; channel < channels; channel++, sample++)
+                                               u8buffer[sample] = buffer[channel][wide_sample] + 0x80;
+                       }
+                       else {
+                               for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+                                       for(channel = 0; channel < channels; channel++, sample++)
+                                               s8buffer[sample] = buffer[channel][wide_sample];
+                       }
+                       if(fwrite(u8buffer, 1, sample, fout) != sample)
+                               return FLAC__STREAM_DECODER_WRITE_ABORT;
                }
-               else {
-                       for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
-                               for(channel = 0; channel < channels; channel++, sample++)
-                                       ssbuffer[sample] = buffer[channel][wide_sample];
+               else if(bps == 16) {
+                       if(is_unsigned_samples) {
+                               for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+                                       for(channel = 0; channel < channels; channel++, sample++)
+                                               u16buffer[sample] = buffer[channel][wide_sample] + 0x8000;
+                       }
+                       else {
+                               for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+                                       for(channel = 0; channel < channels; channel++, sample++)
+                                               s16buffer[sample] = buffer[channel][wide_sample];
+                       }
+                       if(is_big_endian != is_big_endian_host) {
+                               unsigned char tmp;
+                               const unsigned bytes = sample * 2;
+                               for(byte = 0; byte < bytes; byte += 2) {
+                                       tmp = u8buffer[byte];
+                                       u8buffer[byte] = u8buffer[byte+1];
+                                       u8buffer[byte+1] = tmp;
+                               }
+                       }
+                       if(fwrite(u16buffer, 2, sample, fout) != sample)
+                               return FLAC__STREAM_DECODER_WRITE_ABORT;
                }
-               if(is_big_endian != is_big_endian_host) {
-                       unsigned char tmp;
-                       for(byte = 0; byte < sample<<1; byte += 2) {
-                               tmp = ucbuffer[byte];
-                               ucbuffer[byte] = ucbuffer[byte+1];
-                               ucbuffer[byte+1] = tmp;
+               else if(bps == 24) {
+                       if(is_unsigned_samples) {
+                               for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+                                       for(channel = 0; channel < channels; channel++, sample++)
+                                               u32buffer[sample] = buffer[channel][wide_sample] + 0x800000;
+                       }
+                       else {
+                               for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
+                                       for(channel = 0; channel < channels; channel++, sample++)
+                                               s32buffer[sample] = buffer[channel][wide_sample];
+                       }
+                       if(is_big_endian != is_big_endian_host) {
+                               unsigned char tmp;
+                               const unsigned bytes = sample * 4;
+                               for(byte = 0; byte < bytes; byte += 4) {
+                                       tmp = u8buffer[byte];
+                                       u8buffer[byte] = u8buffer[byte+3];
+                                       u8buffer[byte+3] = tmp;
+                                       tmp = u8buffer[byte+1];
+                                       u8buffer[byte+1] = u8buffer[byte+2];
+                                       u8buffer[byte+2] = tmp;
+                               }
                        }
+                       if(is_big_endian) {
+                               unsigned lbyte;
+                               const unsigned bytes = sample * 4;
+                               for(lbyte = byte = 0; byte < bytes; ) {
+                                       byte++;
+                                       u8buffer[lbyte++] = u8buffer[byte++];
+                                       u8buffer[lbyte++] = u8buffer[byte++];
+                                       u8buffer[lbyte++] = u8buffer[byte++];
+                               }
+                       }
+                       else {
+                               unsigned lbyte;
+                               const unsigned bytes = sample * 4;
+                               for(lbyte = byte = 0; byte < bytes; ) {
+                                       u8buffer[lbyte++] = u8buffer[byte++];
+                                       u8buffer[lbyte++] = u8buffer[byte++];
+                                       u8buffer[lbyte++] = u8buffer[byte++];
+                                       byte++;
+                               }
+                       }
+                       if(fwrite(u8buffer, 3, sample, fout) != sample)
+                               return FLAC__STREAM_DECODER_WRITE_ABORT;
+               }
+               else {
+                       FLAC__ASSERT(0);
                }
-               if(fwrite(usbuffer, 2, sample, fout) != sample)
-                       return FLAC__STREAM_DECODER_WRITE_ABORT;
        }
        return FLAC__STREAM_DECODER_WRITE_CONTINUE;
 }
@@ -315,20 +450,26 @@ void metadata_callback(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaD
 {
        stream_info_struct *stream_info = (stream_info_struct *)client_data;
        (void)decoder;
-       if(metadata->type == FLAC__METADATA_TYPE_ENCODING) {
-               stream_info->total_samples = metadata->data.encoding.total_samples - stream_info->skip;
-               stream_info->bps = metadata->data.encoding.bits_per_sample;
-               stream_info->channels = metadata->data.encoding.channels;
-               stream_info->sample_rate = metadata->data.encoding.sample_rate;
-
-               if(stream_info->bps != 8 && stream_info->bps != 16) {
-                       fprintf(stderr, "ERROR: bits per sample is not 8 or 16\n");
+       if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
+               /* remember, metadata->data.stream_info.total_samples can be 0, meaning 'unknown' */
+               if(metadata->data.stream_info.total_samples > 0 && stream_info->skip >= metadata->data.stream_info.total_samples) {
+                       stream_info->total_samples = 0;
+                       stream_info->skip_count_too_high = true;
+               }
+               else
+                       stream_info->total_samples = metadata->data.stream_info.total_samples - stream_info->skip;
+               stream_info->bps = metadata->data.stream_info.bits_per_sample;
+               stream_info->channels = metadata->data.stream_info.channels;
+               stream_info->sample_rate = metadata->data.stream_info.sample_rate;
+
+               if(stream_info->bps != 8 && stream_info->bps != 16 && stream_info->bps != 24) {
+                       fprintf(stderr, "ERROR: bits per sample is not 8/16/24\n");
                        stream_info->abort_flag = true;
                        return;
                }
 
                /* write the WAVE headers if necessary */
-               if(stream_info->is_wave_out) {
+               if(!stream_info->analysis_mode && !stream_info->test_only && stream_info->is_wave_out) {
                        uint64 data_size = stream_info->total_samples * stream_info->channels * ((stream_info->bps+7)/8);
                        if(data_size >= 0xFFFFFFDC) {
                                fprintf(stderr, "ERROR: stream is too big for a wave file\n");
@@ -355,23 +496,31 @@ void error_callback(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorSt
 {
        stream_info_struct *stream_info = (stream_info_struct *)client_data;
        (void)decoder;
-       fprintf(stderr, "*** Got error code %d\n", status);
+       fprintf(stderr, "*** Got error code %d:%s\n", status, FLAC__StreamDecoderErrorStatusString[status]);
        stream_info->abort_flag = true;
 }
 
 void print_stats(const stream_info_struct *stream_info)
 {
        if(stream_info->verbose) {
-               printf("\rwrote %u of %u samples, %6.2f%% complete",
-                       (unsigned)stream_info->samples_processed,
-                       (unsigned)stream_info->total_samples,
+               if(stream_info->total_samples > 0) {
+                       fprintf(stderr, "\r%s %u of %u samples, %6.2f%% complete",
+                               stream_info->test_only? "tested" : stream_info->analysis_mode? "analyzed" : "wrote",
+                               (unsigned)stream_info->samples_processed,
+                               (unsigned)stream_info->total_samples,
 #ifdef _MSC_VER
-                       /* with VC++ you have to spoon feed it the casting */
-                       (double)(int64)stream_info->samples_processed / (double)(int64)stream_info->total_samples * 100.0
+                               /* with VC++ you have to spoon feed it the casting */
+                               (double)(int64)stream_info->samples_processed / (double)(int64)stream_info->total_samples * 100.0
 #else
-                       (double)stream_info->samples_processed / (double)stream_info->total_samples * 100.0
+                               (double)stream_info->samples_processed / (double)stream_info->total_samples * 100.0
 #endif
-               );
-               fflush(stdout);
+                       );
+               }
+               else {
+                       fprintf(stderr, "\r%s %u of ? samples, ?%% complete",
+                               stream_info->test_only? "tested" : stream_info->analysis_mode? "analyzed" : "wrote",
+                               (unsigned)stream_info->samples_processed
+                       );
+               }
        }
 }