rename FLAC__Encoder to FLAC__StreamEncoder, OOPize encoder and decoder interfaces
[flac.git] / src / flac / decode.c
1 /* flac - Command-line FLAC encoder/decoder
2  * Copyright (C) 2000,2001  Josh Coalson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18
19 #if defined _WIN32 && !defined __CYGWIN__
20 /* where MSVC puts unlink() */
21 # include <io.h>
22 #else
23 # include <unistd.h>
24 #endif
25 #include <stdio.h> /* for FILE */
26 #include <string.h> /* for strcmp() */
27 #include "FLAC/all.h"
28 #include "decode.h"
29
30 typedef struct {
31         FILE *fout;
32         bool abort_flag;
33         bool analysis_mode;
34         analysis_options aopts;
35         bool test_only;
36         bool is_wave_out;
37         bool is_big_endian;
38         bool is_unsigned_samples;
39         uint64 total_samples;
40         unsigned bps;
41         unsigned channels;
42         unsigned sample_rate;
43         bool verbose;
44         uint64 skip;
45         bool skip_count_too_high;
46         uint64 samples_processed;
47         unsigned frame_counter;
48 } stream_info_struct;
49
50 static FLAC__FileDecoder *decoder;
51 static bool is_big_endian_host;
52
53 /* local routines */
54 static bool init(const char *infile, stream_info_struct *stream_info);
55 static bool write_little_endian_uint16(FILE *f, uint16 val);
56 static bool write_little_endian_uint32(FILE *f, uint32 val);
57 static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data);
58 static void metadata_callback(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data);
59 static void error_callback(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
60 static void print_stats(const stream_info_struct *stream_info);
61
62 int flac__decode_wav(const char *infile, const char *outfile, bool analysis_mode, analysis_options aopts, bool verbose, uint64 skip)
63 {
64         bool md5_failure = false;
65         stream_info_struct stream_info;
66
67         decoder = 0;
68         stream_info.abort_flag = false;
69         stream_info.analysis_mode = analysis_mode;
70         stream_info.aopts = aopts;
71         stream_info.test_only = (outfile == 0);
72         stream_info.is_wave_out = true;
73         stream_info.verbose = verbose;
74         stream_info.skip = skip;
75         stream_info.skip_count_too_high = false;
76         stream_info.samples_processed = 0;
77         stream_info.frame_counter = 0;
78         stream_info.fout = 0; /* initialized with an open file later if necessary */
79
80         FLAC__ASSERT(!(stream_info.test_only && stream_info.analysis_mode));
81
82         if(!stream_info.test_only) {
83                 if(0 == strcmp(outfile, "-")) {
84                         stream_info.fout = stdout;
85                 }
86                 else {
87                         if(0 == (stream_info.fout = fopen(outfile, "wb"))) {
88                                 fprintf(stderr, "ERROR: can't open output file %s\n", outfile);
89                                 return false;
90                         }
91                 }
92         }
93
94         if(analysis_mode)
95                 flac__analyze_init(aopts);
96
97         if(!init(infile, &stream_info))
98                 goto wav_abort_;
99
100         if(skip > 0) {
101                 if(!FLAC__file_decoder_process_metadata(decoder)) {
102                         fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
103                         goto wav_abort_;
104                 }
105                 if(stream_info.skip_count_too_high) {
106                         fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", infile);
107                         goto wav_abort_;
108                 }
109                 if(!FLAC__file_decoder_seek_absolute(decoder, skip)) {
110                         fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
111                         goto wav_abort_;
112                 }
113                 if(!FLAC__file_decoder_process_remaining_frames(decoder)) {
114                         if(verbose) fprintf(stderr, "\n");
115                         fprintf(stderr, "%s: ERROR while decoding frames, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
116                         goto wav_abort_;
117                 }
118                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_END_OF_FILE) {
119                         if(verbose) fprintf(stderr, "\n");
120                         fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
121                         goto wav_abort_;
122                 }
123         }
124         else {
125                 if(!FLAC__file_decoder_process_whole_file(decoder)) {
126                         if(verbose) fprintf(stderr, "\n");
127                         fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
128                         goto wav_abort_;
129                 }
130                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_END_OF_FILE) {
131                         if(verbose) fprintf(stderr, "\n");
132                         fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
133                         goto wav_abort_;
134                 }
135         }
136
137         if(decoder) {
138                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_UNINITIALIZED)
139                         md5_failure = !FLAC__file_decoder_finish(decoder);
140                 print_stats(&stream_info);
141                 FLAC__file_decoder_delete(decoder);
142         }
143         if(0 != stream_info.fout && stream_info.fout != stdout)
144                 fclose(stream_info.fout);
145         if(verbose)
146                 fprintf(stderr, "\n");
147         if(analysis_mode)
148                 flac__analyze_finish(aopts);
149         if(md5_failure) {
150                 fprintf(stderr, "%s: WARNING, MD5 signature mismatch\n", infile);
151                 return 1;
152         }
153         else {
154                 if(stream_info.test_only)
155                         fprintf(stderr, "%s: ok\n", infile);
156         }
157         return 0;
158 wav_abort_:
159         if(decoder) {
160                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_UNINITIALIZED)
161                         FLAC__file_decoder_finish(decoder);
162                 FLAC__file_decoder_delete(decoder);
163         }
164         if(0 != stream_info.fout && stream_info.fout != stdout) {
165                 fclose(stream_info.fout);
166                 unlink(outfile);
167         }
168         if(analysis_mode)
169                 flac__analyze_finish(aopts);
170         return 1;
171 }
172
173 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)
174 {
175         bool md5_failure = false;
176         stream_info_struct stream_info;
177
178         decoder = 0;
179         stream_info.abort_flag = false;
180         stream_info.analysis_mode = analysis_mode;
181         stream_info.aopts = aopts;
182         stream_info.test_only = (outfile == 0);
183         stream_info.is_wave_out = false;
184         stream_info.is_big_endian = is_big_endian;
185         stream_info.is_unsigned_samples = is_unsigned_samples;
186         stream_info.verbose = verbose;
187         stream_info.skip = skip;
188         stream_info.skip_count_too_high = false;
189         stream_info.samples_processed = 0;
190         stream_info.frame_counter = 0;
191         stream_info.fout = 0; /* initialized with an open file later if necessary */
192
193         FLAC__ASSERT(!(stream_info.test_only && stream_info.analysis_mode));
194
195         if(!stream_info.test_only) {
196                 if(0 == strcmp(outfile, "-")) {
197                         stream_info.fout = stdout;
198                 }
199                 else {
200                         if(0 == (stream_info.fout = fopen(outfile, "wb"))) {
201                                 fprintf(stderr, "ERROR: can't open output file %s\n", outfile);
202                                 return false;
203                         }
204                 }
205         }
206
207         if(analysis_mode)
208                 flac__analyze_init(aopts);
209
210         if(!init(infile, &stream_info))
211                 goto raw_abort_;
212
213         if(skip > 0) {
214                 if(!FLAC__file_decoder_process_metadata(decoder)) {
215                         fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
216                         goto raw_abort_;
217                 }
218                 if(stream_info.skip_count_too_high) {
219                         fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", infile);
220                         goto raw_abort_;
221                 }
222                 if(!FLAC__file_decoder_seek_absolute(decoder, skip)) {
223                         fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
224                         goto raw_abort_;
225                 }
226                 if(!FLAC__file_decoder_process_remaining_frames(decoder)) {
227                         if(verbose) fprintf(stderr, "\n");
228                         fprintf(stderr, "%s: ERROR while decoding frames, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
229                         goto raw_abort_;
230                 }
231                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_END_OF_FILE) {
232                         if(verbose) fprintf(stderr, "\n");
233                         fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
234                         goto raw_abort_;
235                 }
236         }
237         else {
238                 if(!FLAC__file_decoder_process_whole_file(decoder)) {
239                         if(verbose) fprintf(stderr, "\n");
240                         fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
241                         goto raw_abort_;
242                 }
243                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_END_OF_FILE) {
244                         if(verbose) fprintf(stderr, "\n");
245                         fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", infile, FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
246                         goto raw_abort_;
247                 }
248         }
249
250         if(decoder) {
251                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_UNINITIALIZED)
252                         md5_failure = !FLAC__file_decoder_finish(decoder);
253                 print_stats(&stream_info);
254                 FLAC__file_decoder_delete(decoder);
255         }
256         if(0 != stream_info.fout && stream_info.fout != stdout)
257                 fclose(stream_info.fout);
258         if(verbose)
259                 fprintf(stderr, "\n");
260         if(analysis_mode)
261                 flac__analyze_finish(aopts);
262         if(md5_failure) {
263                 fprintf(stderr, "%s: WARNING, MD5 signature mismatch\n", infile);
264                 return 1;
265         }
266         else {
267                 if(stream_info.test_only)
268                         fprintf(stderr, "%s: ok\n", infile);
269         }
270         return 0;
271 raw_abort_:
272         if(decoder) {
273                 if(FLAC__file_decoder_state(decoder) != FLAC__FILE_DECODER_UNINITIALIZED)
274                         FLAC__file_decoder_finish(decoder);
275                 FLAC__file_decoder_delete(decoder);
276         }
277         if(0 != stream_info.fout && stream_info.fout != stdout) {
278                 fclose(stream_info.fout);
279                 unlink(outfile);
280         }
281         if(analysis_mode)
282                 flac__analyze_finish(aopts);
283         return 1;
284 }
285
286 bool init(const char *infile, stream_info_struct *stream_info)
287 {
288         uint32 test = 1;
289
290         is_big_endian_host = (*((byte*)(&test)))? false : true;
291
292         decoder = FLAC__file_decoder_new();
293         if(0 == decoder) {
294                 fprintf(stderr, "ERROR creating the decoder instance\n");
295                 return false;
296         }
297
298         if(FLAC__file_decoder_init(decoder, true /*check_md5*/, infile, write_callback, metadata_callback, error_callback, stream_info) != FLAC__FILE_DECODER_OK) {
299                 fprintf(stderr, "ERROR initializing decoder, state=%d:%s\n", FLAC__file_decoder_state(decoder), FLAC__FileDecoderStateString[FLAC__file_decoder_state(decoder)]);
300                 return false;
301         }
302
303         return true;
304 }
305
306 bool write_little_endian_uint16(FILE *f, uint16 val)
307 {
308         byte *b = (byte*)(&val);
309         if(is_big_endian_host) {
310                 byte tmp;
311                 tmp = b[1]; b[1] = b[0]; b[0] = tmp;
312         }
313         return fwrite(b, 1, 2, f) == 2;
314 }
315
316 bool write_little_endian_uint32(FILE *f, uint32 val)
317 {
318         byte *b = (byte*)(&val);
319         if(is_big_endian_host) {
320                 byte tmp;
321                 tmp = b[3]; b[3] = b[0]; b[0] = tmp;
322                 tmp = b[2]; b[2] = b[1]; b[1] = tmp;
323         }
324         return fwrite(b, 1, 4, f) == 4;
325 }
326
327 FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data)
328 {
329         stream_info_struct *stream_info = (stream_info_struct *)client_data;
330         FILE *fout = stream_info->fout;
331         unsigned bps = stream_info->bps, channels = stream_info->channels;
332         bool is_big_endian = (stream_info->is_wave_out? false : stream_info->is_big_endian);
333         bool is_unsigned_samples = (stream_info->is_wave_out? bps<=8 : stream_info->is_unsigned_samples);
334         unsigned wide_samples = frame->header.blocksize, wide_sample, sample, channel, byte;
335         static int8 s8buffer[FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS * sizeof(int32)]; /* WATCHOUT: can be up to 2 megs */
336         /* 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 */
337         uint8  *u8buffer  = (uint8  *)s8buffer;
338         int16  *s16buffer = (int16  *)s8buffer;
339         uint16 *u16buffer = (uint16 *)s8buffer;
340         int32  *s32buffer = (int32  *)s8buffer;
341         uint32 *u32buffer = (uint32 *)s8buffer;
342
343         (void)decoder;
344
345         if(stream_info->abort_flag)
346                 return FLAC__STREAM_DECODER_WRITE_ABORT;
347
348         stream_info->samples_processed += wide_samples;
349         stream_info->frame_counter++;
350
351         if(stream_info->verbose && !(stream_info->frame_counter & 0x7f))
352                 print_stats(stream_info);
353
354         if(stream_info->analysis_mode) {
355                 flac__analyze_frame(frame, stream_info->frame_counter-1, stream_info->aopts, fout);
356         }
357         else if(!stream_info->test_only) {
358                 if(bps == 8) {
359                         if(is_unsigned_samples) {
360                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
361                                         for(channel = 0; channel < channels; channel++, sample++)
362                                                 u8buffer[sample] = buffer[channel][wide_sample] + 0x80;
363                         }
364                         else {
365                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
366                                         for(channel = 0; channel < channels; channel++, sample++)
367                                                 s8buffer[sample] = buffer[channel][wide_sample];
368                         }
369                         if(fwrite(u8buffer, 1, sample, fout) != sample)
370                                 return FLAC__STREAM_DECODER_WRITE_ABORT;
371                 }
372                 else if(bps == 16) {
373                         if(is_unsigned_samples) {
374                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
375                                         for(channel = 0; channel < channels; channel++, sample++)
376                                                 u16buffer[sample] = buffer[channel][wide_sample] + 0x8000;
377                         }
378                         else {
379                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
380                                         for(channel = 0; channel < channels; channel++, sample++)
381                                                 s16buffer[sample] = buffer[channel][wide_sample];
382                         }
383                         if(is_big_endian != is_big_endian_host) {
384                                 unsigned char tmp;
385                                 const unsigned bytes = sample * 2;
386                                 for(byte = 0; byte < bytes; byte += 2) {
387                                         tmp = u8buffer[byte];
388                                         u8buffer[byte] = u8buffer[byte+1];
389                                         u8buffer[byte+1] = tmp;
390                                 }
391                         }
392                         if(fwrite(u16buffer, 2, sample, fout) != sample)
393                                 return FLAC__STREAM_DECODER_WRITE_ABORT;
394                 }
395                 else if(bps == 24) {
396                         if(is_unsigned_samples) {
397                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
398                                         for(channel = 0; channel < channels; channel++, sample++)
399                                                 u32buffer[sample] = buffer[channel][wide_sample] + 0x800000;
400                         }
401                         else {
402                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
403                                         for(channel = 0; channel < channels; channel++, sample++)
404                                                 s32buffer[sample] = buffer[channel][wide_sample];
405                         }
406                         if(is_big_endian != is_big_endian_host) {
407                                 unsigned char tmp;
408                                 const unsigned bytes = sample * 4;
409                                 for(byte = 0; byte < bytes; byte += 4) {
410                                         tmp = u8buffer[byte];
411                                         u8buffer[byte] = u8buffer[byte+3];
412                                         u8buffer[byte+3] = tmp;
413                                         tmp = u8buffer[byte+1];
414                                         u8buffer[byte+1] = u8buffer[byte+2];
415                                         u8buffer[byte+2] = tmp;
416                                 }
417                         }
418                         if(is_big_endian) {
419                                 unsigned lbyte;
420                                 const unsigned bytes = sample * 4;
421                                 for(lbyte = byte = 0; byte < bytes; ) {
422                                         byte++;
423                                         u8buffer[lbyte++] = u8buffer[byte++];
424                                         u8buffer[lbyte++] = u8buffer[byte++];
425                                         u8buffer[lbyte++] = u8buffer[byte++];
426                                 }
427                         }
428                         else {
429                                 unsigned lbyte;
430                                 const unsigned bytes = sample * 4;
431                                 for(lbyte = byte = 0; byte < bytes; ) {
432                                         u8buffer[lbyte++] = u8buffer[byte++];
433                                         u8buffer[lbyte++] = u8buffer[byte++];
434                                         u8buffer[lbyte++] = u8buffer[byte++];
435                                         byte++;
436                                 }
437                         }
438                         if(fwrite(u8buffer, 3, sample, fout) != sample)
439                                 return FLAC__STREAM_DECODER_WRITE_ABORT;
440                 }
441                 else {
442                         FLAC__ASSERT(0);
443                 }
444         }
445         return FLAC__STREAM_DECODER_WRITE_CONTINUE;
446 }
447
448 void metadata_callback(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
449 {
450         stream_info_struct *stream_info = (stream_info_struct *)client_data;
451         (void)decoder;
452         if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
453                 /* remember, metadata->data.stream_info.total_samples can be 0, meaning 'unknown' */
454                 if(metadata->data.stream_info.total_samples > 0 && stream_info->skip >= metadata->data.stream_info.total_samples) {
455                         stream_info->total_samples = 0;
456                         stream_info->skip_count_too_high = true;
457                 }
458                 else
459                         stream_info->total_samples = metadata->data.stream_info.total_samples - stream_info->skip;
460                 stream_info->bps = metadata->data.stream_info.bits_per_sample;
461                 stream_info->channels = metadata->data.stream_info.channels;
462                 stream_info->sample_rate = metadata->data.stream_info.sample_rate;
463
464                 if(stream_info->bps != 8 && stream_info->bps != 16 && stream_info->bps != 24) {
465                         fprintf(stderr, "ERROR: bits per sample is not 8/16/24\n");
466                         stream_info->abort_flag = true;
467                         return;
468                 }
469
470                 /* write the WAVE headers if necessary */
471                 if(!stream_info->analysis_mode && !stream_info->test_only && stream_info->is_wave_out) {
472                         uint64 data_size = stream_info->total_samples * stream_info->channels * ((stream_info->bps+7)/8);
473                         if(data_size >= 0xFFFFFFDC) {
474                                 fprintf(stderr, "ERROR: stream is too big for a wave file\n");
475                                 stream_info->abort_flag = true;
476                                 return;
477                         }
478                         if(fwrite("RIFF", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true;
479                         if(!write_little_endian_uint32(stream_info->fout, (uint32)(data_size+36))) stream_info->abort_flag = true; /* filesize-8 */
480                         if(fwrite("WAVEfmt ", 1, 8, stream_info->fout) != 8) stream_info->abort_flag = true;
481                         if(fwrite("\020\000\000\000", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true; /* chunk size = 16 */
482                         if(fwrite("\001\000", 1, 2, stream_info->fout) != 2) stream_info->abort_flag = true; /* compression code == 1 */
483                         if(!write_little_endian_uint16(stream_info->fout, (uint16)(stream_info->channels))) stream_info->abort_flag = true;
484                         if(!write_little_endian_uint32(stream_info->fout, stream_info->sample_rate)) stream_info->abort_flag = true;
485                         if(!write_little_endian_uint32(stream_info->fout, stream_info->sample_rate * stream_info->channels * ((stream_info->bps+7) / 8))) stream_info->abort_flag = true; /* @@@ or is it (sample_rate*channels*bps) / 8 ??? */
486                         if(!write_little_endian_uint16(stream_info->fout, (uint16)(stream_info->channels * ((stream_info->bps+7) / 8)))) stream_info->abort_flag = true; /* block align */
487                         if(!write_little_endian_uint16(stream_info->fout, (uint16)(stream_info->bps))) stream_info->abort_flag = true; /* bits per sample */
488                         if(fwrite("data", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true;
489                         if(!write_little_endian_uint32(stream_info->fout, (uint32)data_size)) stream_info->abort_flag = true; /* data size */
490                 }
491         }
492 }
493
494 void error_callback(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
495 {
496         stream_info_struct *stream_info = (stream_info_struct *)client_data;
497         (void)decoder;
498         fprintf(stderr, "*** Got error code %d:%s\n", status, FLAC__StreamDecoderErrorStatusString[status]);
499         stream_info->abort_flag = true;
500 }
501
502 void print_stats(const stream_info_struct *stream_info)
503 {
504         if(stream_info->verbose) {
505                 if(stream_info->total_samples > 0) {
506                         fprintf(stderr, "\r%s %u of %u samples, %6.2f%% complete",
507                                 stream_info->test_only? "tested" : stream_info->analysis_mode? "analyzed" : "wrote",
508                                 (unsigned)stream_info->samples_processed,
509                                 (unsigned)stream_info->total_samples,
510 #ifdef _MSC_VER
511                                 /* with VC++ you have to spoon feed it the casting */
512                                 (double)(int64)stream_info->samples_processed / (double)(int64)stream_info->total_samples * 100.0
513 #else
514                                 (double)stream_info->samples_processed / (double)stream_info->total_samples * 100.0
515 #endif
516                         );
517                 }
518                 else {
519                         fprintf(stderr, "\r%s %u of ? samples, ?%% complete",
520                                 stream_info->test_only? "tested" : stream_info->analysis_mode? "analyzed" : "wrote",
521                                 (unsigned)stream_info->samples_processed
522                         );
523                 }
524         }
525 }