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