another big glob of changes/fixes
[platform/upstream/flac.git] / src / flac / decode.c
1 /* flac - Command-line FLAC encoder/decoder
2  * Copyright (C) 2000,2001,2002  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 #ifdef FLAC__HAS_OGG
32 #include "ogg/ogg.h"
33 #endif
34
35 #ifdef FLAC__HAS_OGG
36 typedef struct {
37         ogg_sync_state oy;
38         ogg_stream_state os;
39 } ogg_info_struct;
40 #endif
41
42 typedef struct {
43         const char *inbasefilename;
44 #ifdef FLAC__HAS_OGG
45         FILE *fin;
46 #endif
47         FILE *fout;
48         FLAC__bool abort_flag;
49         FLAC__bool analysis_mode;
50         analysis_options aopts;
51         FLAC__bool test_only;
52         FLAC__bool is_wave_out;
53         FLAC__bool is_big_endian;
54         FLAC__bool is_unsigned_samples;
55         FLAC__uint64 total_samples;
56         unsigned bps;
57         unsigned channels;
58         unsigned sample_rate;
59         FLAC__bool verbose;
60         FLAC__bool continue_through_decode_errors;
61         struct {
62                 FLAC__bool needs_fixup;
63                 unsigned riff_offset;
64                 unsigned data_offset;
65         } wave_chunk_size_fixup;
66         FLAC__uint64 skip;
67         FLAC__uint64 samples_processed;
68         unsigned frame_counter;
69 #ifdef FLAC__HAS_OGG
70         FLAC__bool is_ogg;
71 #endif
72         union {
73                 FLAC__FileDecoder *file;
74                 FLAC__StreamDecoder *stream;
75         } decoder;
76 #ifdef FLAC__HAS_OGG
77         ogg_info_struct ogg;
78 #endif
79 } stream_info_struct;
80
81 static FLAC__bool is_big_endian_host;
82
83 /* local routines */
84 static FLAC__bool init(const char *infilename, stream_info_struct *stream_info);
85 static FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val);
86 static FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val);
87 static FLAC__bool fixup_wave_chunk_size(const char *outfilename, unsigned riff_offset, unsigned data_offset, FLAC__uint32 data_size);
88 #ifdef FLAC__HAS_OGG
89 static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
90 #endif
91 /*
92  * We use 'void *' so that we can use the same callbacks for the
93  * FLAC__StreamDecoder and FLAC__FileDecoder.  The 'decoder' argument is
94  * actually never used in the callbacks.
95  */
96 static FLAC__StreamDecoderWriteStatus write_callback(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data);
97 static void metadata_callback(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
98 static void error_callback(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
99 static void print_stats(const stream_info_struct *stream_info);
100
101
102 int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, wav_decode_options_t options)
103 {
104         FLAC__bool md5_failure = false;
105         stream_info_struct stream_info;
106
107         stream_info.abort_flag = false;
108         stream_info.analysis_mode = analysis_mode;
109         stream_info.aopts = aopts;
110         stream_info.test_only = (outfilename == 0);
111         stream_info.is_wave_out = true;
112         stream_info.verbose = options.common.verbose;
113         stream_info.continue_through_decode_errors = options.common.continue_through_decode_errors;
114         stream_info.wave_chunk_size_fixup.needs_fixup = false;
115         stream_info.skip = options.common.skip;
116         stream_info.samples_processed = 0;
117         stream_info.frame_counter = 0;
118 #ifdef FLAC__HAS_OGG
119         stream_info.is_ogg = options.common.is_ogg;
120 #endif
121         stream_info.decoder.file = 0; /* this zeroes stream_info.decoder.stream also */
122         stream_info.inbasefilename = flac__file_get_basename(infilename);
123         stream_info.fout = 0; /* initialized with an open file later if necessary */
124
125         FLAC__ASSERT(!(stream_info.test_only && stream_info.analysis_mode));
126
127         if(!stream_info.test_only) {
128                 if(0 == strcmp(outfilename, "-")) {
129                         stream_info.fout = file__get_binary_stdout();
130                 }
131                 else {
132                         if(0 == (stream_info.fout = fopen(outfilename, "wb"))) {
133                                 fprintf(stderr, "%s: ERROR: can't open output file %s\n", stream_info.inbasefilename, outfilename);
134                                 return 1;
135                         }
136                 }
137         }
138
139 #ifdef FLAC__HAS_OGG
140         if(stream_info.is_ogg) {
141                 if (0 == strcmp(infilename, "-")) {
142                         stream_info.fin = file__get_binary_stdin();
143                 } else {
144                         if (0 == (stream_info.fin = fopen(infilename, "rb"))) {
145                                 fprintf(stderr, "%s: ERROR: can't open input file %s\n", stream_info.inbasefilename, infilename);
146                                 if(0 != stream_info.fout && stream_info.fout != stdout)
147                                         fclose(stream_info.fout);
148                                 return 1;
149                         }
150                 }
151         }
152 #endif
153
154         if(analysis_mode)
155                 flac__analyze_init(aopts);
156
157         if(!init(infilename, &stream_info))
158                 goto wav_abort_;
159
160         if(stream_info.skip > 0) {
161 #ifdef FLAC__HAS_OGG
162                 if(stream_info.is_ogg) { /*@@@ (move this check into main.c) */
163                         fprintf(stderr, "%s: ERROR, can't skip when decoding Ogg-FLAC yet; convert to native-FLAC first\n", stream_info.inbasefilename);
164                         goto wav_abort_;
165                 }
166 #endif
167                 if(!FLAC__file_decoder_process_metadata(stream_info.decoder.file)) {
168                         fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
169                         goto wav_abort_;
170                 }
171                 if(stream_info.abort_flag)
172                         goto wav_abort_;
173                 if(!FLAC__file_decoder_seek_absolute(stream_info.decoder.file, stream_info.skip)) {
174                         fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
175                         goto wav_abort_;
176                 }
177                 if(!FLAC__file_decoder_process_remaining_frames(stream_info.decoder.file)) {
178                         if(stream_info.verbose) fprintf(stderr, "\n");
179                         fprintf(stderr, "%s: ERROR while decoding frames, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
180                         goto wav_abort_;
181                 }
182                 if(FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_END_OF_FILE) {
183                         if(stream_info.verbose) fprintf(stderr, "\n");
184                         fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
185                         goto wav_abort_;
186                 }
187         }
188         else {
189 #ifdef FLAC__HAS_OGG
190                 if(stream_info.is_ogg) {
191                         if(!FLAC__stream_decoder_process_whole_stream(stream_info.decoder.stream)) {
192                                 if(stream_info.verbose) fprintf(stderr, "\n");
193                                 fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", stream_info.inbasefilename, FLAC__stream_decoder_get_state(stream_info.decoder.stream), FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(stream_info.decoder.stream)]);
194                                 goto wav_abort_;
195                         }
196                         if(FLAC__stream_decoder_get_state(stream_info.decoder.stream) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA && FLAC__stream_decoder_get_state(stream_info.decoder.stream) != FLAC__STREAM_DECODER_END_OF_STREAM) {
197                                 if(stream_info.verbose) fprintf(stderr, "\n");
198                                 fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", stream_info.inbasefilename, FLAC__stream_decoder_get_state(stream_info.decoder.stream), FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(stream_info.decoder.stream)]);
199                                 goto wav_abort_;
200                         }
201                 }
202                 else
203 #endif
204                 {
205                         if(!FLAC__file_decoder_process_whole_file(stream_info.decoder.file)) {
206                                 if(stream_info.verbose) fprintf(stderr, "\n");
207                                 fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
208                                 goto wav_abort_;
209                         }
210                         if(FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_END_OF_FILE) {
211                                 if(stream_info.verbose) fprintf(stderr, "\n");
212                                 fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
213                                 goto wav_abort_;
214                         }
215                 }
216         }
217
218 #ifdef FLAC__HAS_OGG
219         if(stream_info.is_ogg) {
220                 if(stream_info.decoder.stream) {
221                         FLAC__stream_decoder_finish(stream_info.decoder.stream);
222                         md5_failure = false;
223                         print_stats(&stream_info);
224                         FLAC__stream_decoder_delete(stream_info.decoder.stream);
225                 }
226         }
227         else
228 #endif
229         {
230                 if(stream_info.decoder.file) {
231                         md5_failure = !FLAC__file_decoder_finish(stream_info.decoder.file);
232                         print_stats(&stream_info);
233                         FLAC__file_decoder_delete(stream_info.decoder.file);
234                 }
235         }
236         if(0 != stream_info.fout && stream_info.fout != stdout)
237                 fclose(stream_info.fout);
238 #ifdef FLAC__HAS_OGG
239         if(stream_info.is_ogg) {
240                 if(0 != stream_info.fin && stream_info.fin != stdin)
241                         fclose(stream_info.fin);
242         }
243 #endif
244         if(analysis_mode)
245                 flac__analyze_finish(aopts);
246         if(stream_info.wave_chunk_size_fixup.needs_fixup)
247                 if(!fixup_wave_chunk_size(outfilename, stream_info.wave_chunk_size_fixup.riff_offset, stream_info.wave_chunk_size_fixup.data_offset, (FLAC__uint32)stream_info.samples_processed))
248                         return 1;
249         if(md5_failure) {
250                 fprintf(stderr, "\r%s: WARNING, MD5 signature mismatch\n", stream_info.inbasefilename);
251         }
252         else {
253                 if(stream_info.verbose)
254                         fprintf(stderr, "\r%s: %s         \n", stream_info.inbasefilename, stream_info.test_only? "ok           ":analysis_mode?"done           ":"done");
255         }
256         return 0;
257 wav_abort_:
258 #ifdef FLAC__HAS_OGG
259         if(stream_info.is_ogg) {
260                 if(stream_info.decoder.stream) {
261                         FLAC__stream_decoder_finish(stream_info.decoder.stream);
262                         FLAC__stream_decoder_delete(stream_info.decoder.stream);
263                 }
264         }
265         else
266 #endif
267         {
268                 if(stream_info.decoder.file) {
269                         FLAC__file_decoder_finish(stream_info.decoder.file);
270                         FLAC__file_decoder_delete(stream_info.decoder.file);
271                 }
272         }
273         if(0 != stream_info.fout && stream_info.fout != stdout) {
274                 fclose(stream_info.fout);
275                 unlink(outfilename);
276         }
277 #ifdef FLAC__HAS_OGG
278         if(stream_info.is_ogg) {
279                 if(0 != stream_info.fin && stream_info.fin != stdin)
280                         fclose(stream_info.fin);
281         }
282 #endif
283         if(analysis_mode)
284                 flac__analyze_finish(aopts);
285         return 1;
286 }
287
288 int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, raw_decode_options_t options)
289 {
290         FLAC__bool md5_failure = false;
291         stream_info_struct stream_info;
292
293         stream_info.abort_flag = false;
294         stream_info.analysis_mode = analysis_mode;
295         stream_info.aopts = aopts;
296         stream_info.test_only = (outfilename == 0);
297         stream_info.is_wave_out = false;
298         stream_info.is_big_endian = options.is_big_endian;
299         stream_info.is_unsigned_samples = options.is_unsigned_samples;
300         stream_info.verbose = options.common.verbose;
301         stream_info.continue_through_decode_errors = options.common.continue_through_decode_errors;
302         stream_info.wave_chunk_size_fixup.needs_fixup = false;
303         stream_info.skip = options.common.skip;
304         stream_info.samples_processed = 0;
305         stream_info.frame_counter = 0;
306 #ifdef FLAC__HAS_OGG
307         stream_info.is_ogg = options.common.is_ogg;
308 #endif
309         stream_info.decoder.file = 0; /* this zeroes stream_info.decoder.stream also */
310         stream_info.inbasefilename = flac__file_get_basename(infilename);
311         stream_info.fout = 0; /* initialized with an open file later if necessary */
312
313         FLAC__ASSERT(!(stream_info.test_only && stream_info.analysis_mode));
314
315         if(!stream_info.test_only) {
316                 if(0 == strcmp(outfilename, "-")) {
317                         stream_info.fout = file__get_binary_stdout();
318                 }
319                 else {
320                         if(0 == (stream_info.fout = fopen(outfilename, "wb"))) {
321                                 fprintf(stderr, "%s: ERROR: can't open output file %s\n", stream_info.inbasefilename, outfilename);
322                                 return 1;
323                         }
324                 }
325         }
326
327 #ifdef FLAC__HAS_OGG
328         if(stream_info.is_ogg) {
329                 if (0 == strcmp(infilename, "-")) {
330                         stream_info.fin = file__get_binary_stdin();
331                 } else {
332                         if (0 == (stream_info.fin = fopen(infilename, "rb"))) {
333                                 fprintf(stderr, "%s: ERROR: can't open input file %s\n", stream_info.inbasefilename, infilename);
334                                 if(0 != stream_info.fout && stream_info.fout != stdout)
335                                         fclose(stream_info.fout);
336                                 return 1;
337                         }
338                 }
339         }
340 #endif
341
342         if(analysis_mode)
343                 flac__analyze_init(aopts);
344
345         if(!init(infilename, &stream_info))
346                 goto raw_abort_;
347
348         if(stream_info.skip > 0) {
349 #ifdef FLAC__HAS_OGG
350                 if(stream_info.is_ogg) { /*@@@ (move this check into main.c) */
351                         fprintf(stderr, "%s: ERROR, can't skip when decoding Ogg-FLAC yet; convert to native-FLAC first\n", stream_info.inbasefilename);
352                         goto raw_abort_;
353                 }
354 #endif
355                 if(!FLAC__file_decoder_process_metadata(stream_info.decoder.file)) {
356                         fprintf(stderr, "%s: ERROR while decoding metadata, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
357                         goto raw_abort_;
358                 }
359                 if(stream_info.abort_flag)
360                         goto raw_abort_;
361                 if(!FLAC__file_decoder_seek_absolute(stream_info.decoder.file, stream_info.skip)) {
362                         fprintf(stderr, "%s: ERROR seeking while skipping bytes, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
363                         goto raw_abort_;
364                 }
365                 if(!FLAC__file_decoder_process_remaining_frames(stream_info.decoder.file)) {
366                         if(stream_info.verbose) fprintf(stderr, "\n");
367                         fprintf(stderr, "%s: ERROR while decoding frames, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
368                         goto raw_abort_;
369                 }
370                 if(FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_END_OF_FILE) {
371                         if(stream_info.verbose) fprintf(stderr, "\n");
372                         fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
373                         goto raw_abort_;
374                 }
375         }
376         else {
377 #ifdef FLAC__HAS_OGG
378                 if(stream_info.is_ogg) {
379                         if(!FLAC__stream_decoder_process_whole_stream(stream_info.decoder.stream)) {
380                                 if(stream_info.verbose) fprintf(stderr, "\n");
381                                 fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", stream_info.inbasefilename, FLAC__stream_decoder_get_state(stream_info.decoder.stream), FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(stream_info.decoder.stream)]);
382                                 goto raw_abort_;
383                         }
384                         if(FLAC__stream_decoder_get_state(stream_info.decoder.stream) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA && FLAC__stream_decoder_get_state(stream_info.decoder.stream) != FLAC__STREAM_DECODER_END_OF_STREAM) {
385                                 if(stream_info.verbose) fprintf(stderr, "\n");
386                                 fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", stream_info.inbasefilename, FLAC__stream_decoder_get_state(stream_info.decoder.stream), FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(stream_info.decoder.stream)]);
387                                 goto raw_abort_;
388                         }
389                 }
390                 else
391 #endif
392                 {
393                         if(!FLAC__file_decoder_process_whole_file(stream_info.decoder.file)) {
394                                 if(stream_info.verbose) fprintf(stderr, "\n");
395                                 fprintf(stderr, "%s: ERROR while decoding data, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
396                                 goto raw_abort_;
397                         }
398                         if(FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_OK && FLAC__file_decoder_get_state(stream_info.decoder.file) != FLAC__FILE_DECODER_END_OF_FILE) {
399                                 if(stream_info.verbose) fprintf(stderr, "\n");
400                                 fprintf(stderr, "%s: ERROR during decoding, state=%d:%s\n", stream_info.inbasefilename, FLAC__file_decoder_get_state(stream_info.decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info.decoder.file)]);
401                                 goto raw_abort_;
402                         }
403                 }
404         }
405
406 #ifdef FLAC__HAS_OGG
407         if(stream_info.is_ogg) {
408                 if(stream_info.decoder.stream) {
409                         FLAC__stream_decoder_finish(stream_info.decoder.stream);
410                         md5_failure = false;
411                         print_stats(&stream_info);
412                         FLAC__stream_decoder_delete(stream_info.decoder.stream);
413                 }
414         }
415         else
416 #endif
417         {
418                 if(stream_info.decoder.file) {
419                         md5_failure = !FLAC__file_decoder_finish(stream_info.decoder.file);
420                         print_stats(&stream_info);
421                         FLAC__file_decoder_delete(stream_info.decoder.file);
422                 }
423         }
424         if(0 != stream_info.fout && stream_info.fout != stdout)
425                 fclose(stream_info.fout);
426 #ifdef FLAC__HAS_OGG
427         if(stream_info.is_ogg) {
428                 if(0 != stream_info.fin && stream_info.fin != stdin)
429                         fclose(stream_info.fin);
430         }
431 #endif
432         if(analysis_mode)
433                 flac__analyze_finish(aopts);
434         if(md5_failure) {
435                 fprintf(stderr, "\r%s: WARNING, MD5 signature mismatch\n", stream_info.inbasefilename);
436         }
437         else {
438                 if(stream_info.verbose)
439                         fprintf(stderr, "\r%s: %s         \n", stream_info.inbasefilename, stream_info.test_only? "ok           ":analysis_mode?"done           ":"done");
440         }
441         return 0;
442 raw_abort_:
443 #ifdef FLAC__HAS_OGG
444         if(stream_info.is_ogg) {
445                 if(stream_info.decoder.stream) {
446                         FLAC__stream_decoder_finish(stream_info.decoder.stream);
447                         FLAC__stream_decoder_delete(stream_info.decoder.stream);
448                 }
449         }
450         else
451 #endif
452         {
453                 if(stream_info.decoder.file) {
454                         FLAC__file_decoder_finish(stream_info.decoder.file);
455                         FLAC__file_decoder_delete(stream_info.decoder.file);
456                 }
457         }
458         if(0 != stream_info.fout && stream_info.fout != stdout) {
459                 fclose(stream_info.fout);
460                 unlink(outfilename);
461         }
462 #ifdef FLAC__HAS_OGG
463         if(stream_info.is_ogg) {
464                 if(0 != stream_info.fin && stream_info.fin != stdin)
465                         fclose(stream_info.fin);
466         }
467 #endif
468         if(analysis_mode)
469                 flac__analyze_finish(aopts);
470         return 1;
471 }
472
473 FLAC__bool init(const char *infilename, stream_info_struct *stream_info)
474 {
475         FLAC__uint32 test = 1;
476
477         is_big_endian_host = (*((FLAC__byte*)(&test)))? false : true;
478
479 #ifdef FLAC__HAS_OGG
480         if(stream_info->is_ogg) {
481                 stream_info->decoder.stream = FLAC__stream_decoder_new();
482
483                 if(0 == stream_info->decoder.stream) {
484                         fprintf(stderr, "%s: ERROR creating the decoder instance\n", stream_info->inbasefilename);
485                         return false;
486                 }
487
488                 FLAC__stream_decoder_set_read_callback(stream_info->decoder.stream, read_callback);
489                 /*
490                  * The three ugly casts here are to 'downcast' the 'void *' argument of
491                  * the callback down to 'FLAC__StreamDecoder *'.  In C++ this would be
492                  * unnecessary but here the cast makes the C compiler happy.
493                  */
494                 FLAC__stream_decoder_set_write_callback(stream_info->decoder.stream, (FLAC__StreamDecoderWriteStatus (*)(const FLAC__StreamDecoder *, const FLAC__Frame *, const FLAC__int32 *[], void *))write_callback);
495                 FLAC__stream_decoder_set_metadata_callback(stream_info->decoder.stream, (void (*)(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *, void *))metadata_callback);
496                 FLAC__stream_decoder_set_error_callback(stream_info->decoder.stream, (void (*)(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus, void *))error_callback);
497                 FLAC__stream_decoder_set_client_data(stream_info->decoder.stream, stream_info);
498
499                 if(FLAC__stream_decoder_init(stream_info->decoder.stream) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) {
500                         fprintf(stderr, "%s: ERROR initializing decoder, state=%d:%s\n", stream_info->inbasefilename, FLAC__stream_decoder_get_state(stream_info->decoder.stream), FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(stream_info->decoder.stream)]);
501                         return false;
502                 }
503
504                 ogg_stream_init(&stream_info->ogg.os, 0);
505                 ogg_sync_init(&stream_info->ogg.oy);
506         }
507         else
508 #endif
509         {
510                 stream_info->decoder.file = FLAC__file_decoder_new();
511
512                 if(0 == stream_info->decoder.file) {
513                         fprintf(stderr, "%s: ERROR creating the decoder instance\n", stream_info->inbasefilename);
514                         return false;
515                 }
516
517                 FLAC__file_decoder_set_md5_checking(stream_info->decoder.file, true);
518                 FLAC__file_decoder_set_filename(stream_info->decoder.file, infilename);
519                 /*
520                  * The three ugly casts here are to 'downcast' the 'void *' argument of
521                  * the callback down to 'FLAC__FileDecoder *'.
522                  */
523                 FLAC__file_decoder_set_write_callback(stream_info->decoder.file, (FLAC__StreamDecoderWriteStatus (*)(const FLAC__FileDecoder *, const FLAC__Frame *, const FLAC__int32 * const [], void *))write_callback);
524                 FLAC__file_decoder_set_metadata_callback(stream_info->decoder.file, (void (*)(const FLAC__FileDecoder *, const FLAC__StreamMetadata *, void *))metadata_callback);
525                 FLAC__file_decoder_set_error_callback(stream_info->decoder.file, (void (*)(const FLAC__FileDecoder *, FLAC__StreamDecoderErrorStatus, void *))error_callback);
526                 FLAC__file_decoder_set_client_data(stream_info->decoder.file, stream_info);
527
528                 if(FLAC__file_decoder_init(stream_info->decoder.file) != FLAC__FILE_DECODER_OK) {
529                         fprintf(stderr, "%s: ERROR initializing decoder, state=%d:%s\n", stream_info->inbasefilename, FLAC__file_decoder_get_state(stream_info->decoder.file), FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(stream_info->decoder.file)]);
530                         return false;
531                 }
532         }
533
534         return true;
535 }
536
537 FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val)
538 {
539         FLAC__byte *b = (FLAC__byte*)(&val);
540         if(is_big_endian_host) {
541                 FLAC__byte tmp;
542                 tmp = b[1]; b[1] = b[0]; b[0] = tmp;
543         }
544         return fwrite(b, 1, 2, f) == 2;
545 }
546
547 FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val)
548 {
549         FLAC__byte *b = (FLAC__byte*)(&val);
550         if(is_big_endian_host) {
551                 FLAC__byte tmp;
552                 tmp = b[3]; b[3] = b[0]; b[0] = tmp;
553                 tmp = b[2]; b[2] = b[1]; b[1] = tmp;
554         }
555         return fwrite(b, 1, 4, f) == 4;
556 }
557
558 FLAC__bool fixup_wave_chunk_size(const char *outfilename, unsigned riff_offset, unsigned data_offset, FLAC__uint32 data_size)
559 {
560         FILE *f = fopen(outfilename, "r+b");
561         if(0 == f) {
562                 fprintf(stderr, "ERROR, couldn't open file %s while fixing up WAVE chunk size\n", outfilename);
563                 return false;
564         }
565         if(fseek(f, riff_offset, SEEK_SET) < 0) {
566                 fprintf(stderr, "ERROR, couldn't seek in file %s while fixing up WAVE chunk size\n", outfilename);
567                 fclose(f);
568                 return false;
569         }
570         if(!write_little_endian_uint32(f, data_size + 36)) {
571                 fprintf(stderr, "ERROR, couldn't write size in file %s while fixing up WAVE chunk size\n", outfilename);
572                 fclose(f);
573                 return false;
574         }
575         if(fseek(f, data_offset, SEEK_SET) < 0) {
576                 fprintf(stderr, "ERROR, couldn't seek in file %s while fixing up WAVE chunk size\n", outfilename);
577                 fclose(f);
578                 return false;
579         }
580         if(!write_little_endian_uint32(f, data_size)) {
581                 fprintf(stderr, "ERROR, couldn't write size in file %s while fixing up WAVE chunk size\n", outfilename);
582                 fclose(f);
583                 return false;
584         }
585         fclose(f);
586         return true;
587 }
588
589 #ifdef FLAC__HAS_OGG
590 #define OGG_READ_BUFFER_SIZE 4096
591 FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
592 {
593         stream_info_struct *stream_info = (stream_info_struct *)client_data;
594         FILE *fin = stream_info->fin;
595         size_t bytes_read;
596         ogg_page og;
597         char *oggbuf;
598         unsigned int offset = 0;
599
600         *bytes = 0;
601
602         if (stream_info->abort_flag)
603                 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
604
605         oggbuf = ogg_sync_buffer(&stream_info->ogg.oy, OGG_READ_BUFFER_SIZE);
606
607         (void)decoder; /* avoid compiler warning */
608
609         if(feof(fin))
610                 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
611
612         bytes_read = fread(oggbuf, 1, OGG_READ_BUFFER_SIZE, fin);
613
614         if(ferror(fin))
615                 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
616
617         if(ogg_sync_wrote(&stream_info->ogg.oy, bytes_read) < 0)
618                 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
619
620         while(ogg_sync_pageout(&stream_info->ogg.oy, &og) == 1) {
621                 if(ogg_stream_pagein(&stream_info->ogg.os, &og) == 0) {
622                         ogg_packet op;
623
624                         while(ogg_stream_packetout(&stream_info->ogg.os, &op) == 1) {
625                                 memcpy(buffer + offset, op.packet, op.bytes);
626                                 *bytes += op.bytes;
627                                 offset += op.bytes;
628                         }
629                 } else {
630                         return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
631                 }
632         }
633
634         return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
635 }
636 #endif
637
638 FLAC__StreamDecoderWriteStatus write_callback(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
639 {
640         stream_info_struct *stream_info = (stream_info_struct *)client_data;
641         FILE *fout = stream_info->fout;
642         unsigned bps = stream_info->bps, channels = stream_info->channels;
643         FLAC__bool is_big_endian = (stream_info->is_wave_out? false : stream_info->is_big_endian);
644         FLAC__bool is_unsigned_samples = (stream_info->is_wave_out? bps<=8 : stream_info->is_unsigned_samples);
645         unsigned wide_samples = frame->header.blocksize, wide_sample, sample, channel, byte;
646         static FLAC__int8 s8buffer[FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS * sizeof(FLAC__int32)]; /* WATCHOUT: can be up to 2 megs */
647         FLAC__uint8  *u8buffer  = (FLAC__uint8  *)s8buffer;
648         FLAC__int16  *s16buffer = (FLAC__int16  *)s8buffer;
649         FLAC__uint16 *u16buffer = (FLAC__uint16 *)s8buffer;
650         FLAC__int32  *s32buffer = (FLAC__int32  *)s8buffer;
651         FLAC__uint32 *u32buffer = (FLAC__uint32 *)s8buffer;
652
653         (void)decoder;
654
655         if(stream_info->abort_flag)
656                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
657
658         stream_info->samples_processed += wide_samples;
659         stream_info->frame_counter++;
660
661         if(stream_info->verbose && !(stream_info->frame_counter & 0x7f))
662                 print_stats(stream_info);
663
664         if(stream_info->analysis_mode) {
665                 flac__analyze_frame(frame, stream_info->frame_counter-1, stream_info->aopts, fout);
666         }
667         else if(!stream_info->test_only) {
668                 if(bps == 8) {
669                         if(is_unsigned_samples) {
670                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
671                                         for(channel = 0; channel < channels; channel++, sample++)
672                                                 u8buffer[sample] = (FLAC__uint8)(buffer[channel][wide_sample] + 0x80);
673                         }
674                         else {
675                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
676                                         for(channel = 0; channel < channels; channel++, sample++)
677                                                 s8buffer[sample] = (FLAC__int8)(buffer[channel][wide_sample]);
678                         }
679                         if(fwrite(u8buffer, 1, sample, fout) != sample)
680                                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
681                 }
682                 else if(bps == 16) {
683                         if(is_unsigned_samples) {
684                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
685                                         for(channel = 0; channel < channels; channel++, sample++)
686                                                 u16buffer[sample] = (FLAC__uint16)(buffer[channel][wide_sample] + 0x8000);
687                         }
688                         else {
689                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
690                                         for(channel = 0; channel < channels; channel++, sample++)
691                                                 s16buffer[sample] = (FLAC__int16)(buffer[channel][wide_sample]);
692                         }
693                         if(is_big_endian != is_big_endian_host) {
694                                 unsigned char tmp;
695                                 const unsigned bytes = sample * 2;
696                                 for(byte = 0; byte < bytes; byte += 2) {
697                                         tmp = u8buffer[byte];
698                                         u8buffer[byte] = u8buffer[byte+1];
699                                         u8buffer[byte+1] = tmp;
700                                 }
701                         }
702                         if(fwrite(u16buffer, 2, sample, fout) != sample)
703                                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
704                 }
705                 else if(bps == 24) {
706                         if(is_unsigned_samples) {
707                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
708                                         for(channel = 0; channel < channels; channel++, sample++)
709                                                 u32buffer[sample] = buffer[channel][wide_sample] + 0x800000;
710                         }
711                         else {
712                                 for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++)
713                                         for(channel = 0; channel < channels; channel++, sample++)
714                                                 s32buffer[sample] = buffer[channel][wide_sample];
715                         }
716                         if(is_big_endian != is_big_endian_host) {
717                                 unsigned char tmp;
718                                 const unsigned bytes = sample * 4;
719                                 for(byte = 0; byte < bytes; byte += 4) {
720                                         tmp = u8buffer[byte];
721                                         u8buffer[byte] = u8buffer[byte+3];
722                                         u8buffer[byte+3] = tmp;
723                                         tmp = u8buffer[byte+1];
724                                         u8buffer[byte+1] = u8buffer[byte+2];
725                                         u8buffer[byte+2] = tmp;
726                                 }
727                         }
728                         if(is_big_endian) {
729                                 unsigned lbyte;
730                                 const unsigned bytes = sample * 4;
731                                 for(lbyte = byte = 0; byte < bytes; ) {
732                                         byte++;
733                                         u8buffer[lbyte++] = u8buffer[byte++];
734                                         u8buffer[lbyte++] = u8buffer[byte++];
735                                         u8buffer[lbyte++] = u8buffer[byte++];
736                                 }
737                         }
738                         else {
739                                 unsigned lbyte;
740                                 const unsigned bytes = sample * 4;
741                                 for(lbyte = byte = 0; byte < bytes; ) {
742                                         u8buffer[lbyte++] = u8buffer[byte++];
743                                         u8buffer[lbyte++] = u8buffer[byte++];
744                                         u8buffer[lbyte++] = u8buffer[byte++];
745                                         byte++;
746                                 }
747                         }
748                         if(fwrite(u8buffer, 3, sample, fout) != sample)
749                                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
750                 }
751                 else {
752                         FLAC__ASSERT(0);
753                 }
754         }
755         return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
756 }
757
758 void metadata_callback(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
759 {
760         stream_info_struct *stream_info = (stream_info_struct *)client_data;
761         (void)decoder;
762         if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
763                 /* remember, metadata->data.stream_info.total_samples can be 0, meaning 'unknown' */
764                 if(metadata->data.stream_info.total_samples > 0 && stream_info->skip >= metadata->data.stream_info.total_samples) {
765                         fprintf(stderr, "%s: ERROR trying to skip more samples than in stream\n", stream_info->inbasefilename);
766                         stream_info->abort_flag = true;
767                         return;
768                 }
769                 else if(metadata->data.stream_info.total_samples == 0 && stream_info->skip > 0) {
770                         fprintf(stderr, "%s: ERROR, can't skip when FLAC metadata has total sample count of 0\n", stream_info->inbasefilename);
771                         stream_info->abort_flag = true;
772                         return;
773                 }
774                 else
775                         stream_info->total_samples = metadata->data.stream_info.total_samples - stream_info->skip;
776                 stream_info->bps = metadata->data.stream_info.bits_per_sample;
777                 stream_info->channels = metadata->data.stream_info.channels;
778                 stream_info->sample_rate = metadata->data.stream_info.sample_rate;
779
780                 if(stream_info->bps != 8 && stream_info->bps != 16 && stream_info->bps != 24) {
781                         fprintf(stderr, "%s: ERROR: bits per sample is not 8/16/24\n", stream_info->inbasefilename);
782                         stream_info->abort_flag = true;
783                         return;
784                 }
785
786                 /* write the WAVE headers if necessary */
787                 if(!stream_info->analysis_mode && !stream_info->test_only && stream_info->is_wave_out) {
788                         FLAC__uint64 data_size = stream_info->total_samples * stream_info->channels * ((stream_info->bps+7)/8);
789                         if(stream_info->total_samples == 0) {
790                                 if(stream_info->fout == stdout) {
791                                         fprintf(stderr, "%s: WARNING, don't have accurate sample count available for WAVE header.\n", stream_info->inbasefilename);
792                                         fprintf(stderr, "             Generated WAVE file will have a data chunk size of 0.  Try\n");
793                                         fprintf(stderr, "             decoding directly to a file instead.\n");
794                                 }
795                                 else {
796                                         stream_info->wave_chunk_size_fixup.needs_fixup = true;
797                                 }
798                         }
799                         if(data_size >= 0xFFFFFFDC) {
800                                 fprintf(stderr, "%s: ERROR: stream is too big to fit in a single WAVE file chunk\n", stream_info->inbasefilename);
801                                 stream_info->abort_flag = true;
802                                 return;
803                         }
804                         if(fwrite("RIFF", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true;
805                         if(stream_info->wave_chunk_size_fixup.needs_fixup)
806                                 stream_info->wave_chunk_size_fixup.riff_offset = ftell(stream_info->fout);
807                         if(!write_little_endian_uint32(stream_info->fout, (FLAC__uint32)(data_size+36))) stream_info->abort_flag = true; /* filesize-8 */
808                         if(fwrite("WAVEfmt ", 1, 8, stream_info->fout) != 8) stream_info->abort_flag = true;
809                         if(fwrite("\020\000\000\000", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true; /* chunk size = 16 */
810                         if(fwrite("\001\000", 1, 2, stream_info->fout) != 2) stream_info->abort_flag = true; /* compression code == 1 */
811                         if(!write_little_endian_uint16(stream_info->fout, (FLAC__uint16)(stream_info->channels))) stream_info->abort_flag = true;
812                         if(!write_little_endian_uint32(stream_info->fout, stream_info->sample_rate)) stream_info->abort_flag = true;
813                         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 ??? */
814                         if(!write_little_endian_uint16(stream_info->fout, (FLAC__uint16)(stream_info->channels * ((stream_info->bps+7) / 8)))) stream_info->abort_flag = true; /* block align */
815                         if(!write_little_endian_uint16(stream_info->fout, (FLAC__uint16)(stream_info->bps))) stream_info->abort_flag = true; /* bits per sample */
816                         if(fwrite("data", 1, 4, stream_info->fout) != 4) stream_info->abort_flag = true;
817                         if(stream_info->wave_chunk_size_fixup.needs_fixup)
818                                 stream_info->wave_chunk_size_fixup.data_offset = ftell(stream_info->fout);
819                         if(!write_little_endian_uint32(stream_info->fout, (FLAC__uint32)data_size)) stream_info->abort_flag = true; /* data size */
820                 }
821         }
822 }
823
824 void error_callback(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
825 {
826         stream_info_struct *stream_info = (stream_info_struct *)client_data;
827         (void)decoder;
828         fprintf(stderr, "%s: *** Got error code %d:%s\n", stream_info->inbasefilename, status, FLAC__StreamDecoderErrorStatusString[status]);
829         if(!stream_info->continue_through_decode_errors)
830                 stream_info->abort_flag = true;
831 }
832
833 void print_stats(const stream_info_struct *stream_info)
834 {
835         if(stream_info->verbose) {
836 #if defined _MSC_VER || defined __MINGW32__
837                 /* with VC++ you have to spoon feed it the casting */
838                 const double progress = (double)(FLAC__int64)stream_info->samples_processed / (double)(FLAC__int64)stream_info->total_samples * 100.0;
839 #else
840                 const double progress = (double)stream_info->samples_processed / (double)stream_info->total_samples * 100.0;
841 #endif
842                 if(stream_info->total_samples > 0) {
843                         fprintf(stderr, "\r%s: %s%u%% complete",
844                                 stream_info->inbasefilename,
845                                 stream_info->test_only? "testing, " : stream_info->analysis_mode? "analyzing, " : "",
846                                 (unsigned)floor(progress + 0.5)
847                         );
848                 }
849                 else {
850                         fprintf(stderr, "\r%s: %s %u samples",
851                                 stream_info->inbasefilename,
852                                 stream_info->test_only? "tested" : stream_info->analysis_mode? "analyzed" : "wrote",
853                                 (unsigned)stream_info->samples_processed
854                         );
855                 }
856         }
857 }
858 #if 0
859 void metadata_callback(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data)
860 {
861         encoder_wrapper_struct *encoder_wrapper = (encoder_wrapper_struct *)client_data;
862         FLAC__byte b;
863         FILE *f = encoder_wrapper->fout;
864         const FLAC__uint64 samples = metadata->data.stream_info.total_samples;
865         const unsigned min_framesize = metadata->data.stream_info.min_framesize;
866         const unsigned max_framesize = metadata->data.stream_info.max_framesize;
867
868         FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO);
869
870         /*
871          * If we are writing to an ogg stream, there is no need to go back
872          * and update the STREAMINFO or SEEKTABLE blocks; the values we would
873          * update are not necessary with Ogg as the transport.  We can't do
874          * it reliably anyway without knowing the Ogg structure.
875          */
876 #ifdef FLAC__HAS_OGG
877         if(encoder_wrapper->use_ogg)
878                 return;
879 #endif
880
881         /*
882          * we get called by the encoder when the encoding process has
883          * finished so that we can update the STREAMINFO and SEEKTABLE
884          * blocks.
885          */
886
887         (void)encoder; /* silence compiler warning about unused parameter */
888
889         if(f != stdout) {
890                 fclose(encoder_wrapper->fout);
891                 if(0 == (f = fopen(encoder_wrapper->outfilename, "r+b")))
892                         return;
893         }
894
895         /* all this is based on intimate knowledge of the stream header
896          * layout, but a change to the header format that would break this
897          * would also break all streams encoded in the previous format.
898          */
899
900         if(-1 == fseek(f, 26, SEEK_SET)) goto end_;
901         fwrite(metadata->data.stream_info.md5sum, 1, 16, f);
902
903 samples_:
904         /* if we get this far we know we can seek so no need to check the
905          * return value from fseek()
906          */
907         fseek(f, 21, SEEK_SET);
908         if(fread(&b, 1, 1, f) != 1) goto framesize_;
909         fseek(f, 21, SEEK_SET);
910         b = (b & 0xf0) | (FLAC__byte)((samples >> 32) & 0x0F);
911         if(fwrite(&b, 1, 1, f) != 1) goto framesize_;
912         b = (FLAC__byte)((samples >> 24) & 0xFF);
913         if(fwrite(&b, 1, 1, f) != 1) goto framesize_;
914         b = (FLAC__byte)((samples >> 16) & 0xFF);
915         if(fwrite(&b, 1, 1, f) != 1) goto framesize_;
916         b = (FLAC__byte)((samples >> 8) & 0xFF);
917         if(fwrite(&b, 1, 1, f) != 1) goto framesize_;
918         b = (FLAC__byte)(samples & 0xFF);
919         if(fwrite(&b, 1, 1, f) != 1) goto framesize_;
920
921 framesize_:
922         fseek(f, 12, SEEK_SET);
923         b = (FLAC__byte)((min_framesize >> 16) & 0xFF);
924         if(fwrite(&b, 1, 1, f) != 1) goto seektable_;
925         b = (FLAC__byte)((min_framesize >> 8) & 0xFF);
926         if(fwrite(&b, 1, 1, f) != 1) goto seektable_;
927         b = (FLAC__byte)(min_framesize & 0xFF);
928         if(fwrite(&b, 1, 1, f) != 1) goto seektable_;
929         b = (FLAC__byte)((max_framesize >> 16) & 0xFF);
930         if(fwrite(&b, 1, 1, f) != 1) goto seektable_;
931         b = (FLAC__byte)((max_framesize >> 8) & 0xFF);
932         if(fwrite(&b, 1, 1, f) != 1) goto seektable_;
933         b = (FLAC__byte)(max_framesize & 0xFF);
934         if(fwrite(&b, 1, 1, f) != 1) goto seektable_;
935
936 seektable_:
937         if(encoder_wrapper->seek_table.num_points > 0) {
938                 long pos;
939                 unsigned i;
940
941                 /* convert any unused seek points to placeholders */
942                 for(i = 0; i < encoder_wrapper->seek_table.num_points; i++) {
943                         if(encoder_wrapper->seek_table.points[i].sample_number == FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER)
944                                 break;
945                         else if(encoder_wrapper->seek_table.points[i].frame_samples == 0)
946                                 encoder_wrapper->seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
947                 }
948
949                 /* the offset of the seek table data 'pos' should be after then stream sync and STREAMINFO block and SEEKTABLE header */
950                 pos = (FLAC__STREAM_SYNC_LEN + FLAC__STREAM_METADATA_IS_LAST_LEN + FLAC__STREAM_METADATA_TYPE_LEN + FLAC__STREAM_METADATA_LENGTH_LEN) / 8;
951                 pos += metadata->length;
952                 pos += (FLAC__STREAM_METADATA_IS_LAST_LEN + FLAC__STREAM_METADATA_TYPE_LEN + FLAC__STREAM_METADATA_LENGTH_LEN) / 8;
953                 fseek(f, pos, SEEK_SET);
954                 for(i = 0; i < encoder_wrapper->seek_table.num_points; i++) {
955                         if(!write_big_endian_uint64(f, encoder_wrapper->seek_table.points[i].sample_number)) goto end_;
956                         if(!write_big_endian_uint64(f, encoder_wrapper->seek_table.points[i].stream_offset)) goto end_;
957                         if(!write_big_endian_uint16(f, (FLAC__uint16)encoder_wrapper->seek_table.points[i].frame_samples)) goto end_;
958                 }
959         }
960
961 end_:
962         fclose(f);
963         return;
964 }
965 #endif