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