1 /* flacdiff - Displays where two FLAC streams differ
2 * Copyright (C) 2007,2008,2009 Josh Coalson
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.
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.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "FLAC++/decoder.h"
26 #include "share/compat.h"
29 // warning C4800: 'int' : forcing to bool 'true' or 'false' (performance warning)
30 #pragma warning ( disable : 4800 )
37 inline AutoFILE(const char *path
, const char *mode
): f_(::fopen(path
, mode
)) { }
38 inline virtual ~AutoFILE() { if (f_
) (void)::fclose(f_
); }
40 inline operator bool() const { return 0 != f_
; }
41 inline operator const ::FILE *() const { return f_
; }
42 inline operator ::FILE *() { return f_
; }
45 AutoFILE(const AutoFILE
&);
46 void operator=(const AutoFILE
&);
49 class Decoder
: public FLAC::Decoder::Stream
{
51 Decoder(AutoFILE
&f
, FLAC__off_t tgt
): tgtpos_((FLAC__uint64
)tgt
), curpos_(0), go_(true), err_(false), frame_(), f_(f
) { memset(&frame_
, 0, sizeof(::FLAC__Frame
)); }
52 FLAC__uint64 tgtpos_
, curpos_
;
57 // from FLAC::Decoder::Stream
58 virtual ::FLAC__StreamDecoderReadStatus
read_callback(FLAC__byte buffer
[], size_t *bytes
)
60 *bytes
= fread(buffer
, 1, *bytes
, f_
);
62 return ::FLAC__STREAM_DECODER_READ_STATUS_ABORT
;
63 else if(*bytes
== 0 && feof((FILE*)f_
))
64 return ::FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM
;
66 return ::FLAC__STREAM_DECODER_READ_STATUS_CONTINUE
;
69 virtual ::FLAC__StreamDecoderTellStatus
tell_callback(FLAC__uint64
*absolute_byte_offset
)
71 FLAC__off_t off
= ftello(f_
);
73 return ::FLAC__STREAM_DECODER_TELL_STATUS_ERROR
;
74 *absolute_byte_offset
= off
;
75 return ::FLAC__STREAM_DECODER_TELL_STATUS_OK
;
78 virtual bool eof_callback()
80 return (bool)feof((FILE*)f_
);
83 virtual ::FLAC__StreamDecoderWriteStatus
write_callback(const ::FLAC__Frame
*frame
, const FLAC__int32
* const /*buffer*/[])
86 if(!get_decode_position(&pos
)) {
89 return ::FLAC__STREAM_DECODER_WRITE_STATUS_ABORT
;
97 return ::FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE
;
100 virtual void error_callback(::FLAC__StreamDecoderErrorStatus status
)
102 fprintf(stderr
, "got error %d:%s\n", status
, ::FLAC__StreamDecoderErrorStatusString
[status
]);
108 static bool show_diff(AutoFILE
&f1
, AutoFILE
&f2
, FLAC__off_t off
)
110 Decoder
d1(f1
, off
), d2(f2
, off
);
112 fprintf(stderr
, "ERROR: setting up decoder1, state=%s\n", d1
.get_state().resolved_as_cstring(d1
));
116 fprintf(stderr
, "ERROR: setting up decoder2, state=%s\n", d2
.get_state().resolved_as_cstring(d2
));
119 ::FLAC__StreamDecoderInitStatus is
;
120 if((is
= d1
.init()) != FLAC__STREAM_DECODER_INIT_STATUS_OK
) {
121 fprintf(stderr
, "ERROR: initializing decoder1, status=%s state=%s\n", FLAC__StreamDecoderInitStatusString
[is
], d1
.get_state().resolved_as_cstring(d1
));
124 if((is
= d2
.init()) != FLAC__STREAM_DECODER_INIT_STATUS_OK
) {
125 fprintf(stderr
, "ERROR: initializing decoder2, status=%s state=%s\n", FLAC__StreamDecoderInitStatusString
[is
], d2
.get_state().resolved_as_cstring(d2
));
128 if(!d1
.process_until_end_of_metadata()) {
129 fprintf(stderr
, "ERROR: skipping metadata in decoder1, state=%s\n", d1
.get_state().resolved_as_cstring(d1
));
132 if(!d2
.process_until_end_of_metadata()) {
133 fprintf(stderr
, "ERROR: skipping metadata in decoder2, state=%s\n", d2
.get_state().resolved_as_cstring(d2
));
136 while(d1
.go_
&& d2
.go_
) {
137 if(!d1
.process_single()) {
138 fprintf(stderr
, "ERROR: decoding frame in decoder1, state=%s\n", d1
.get_state().resolved_as_cstring(d1
));
141 if(!d2
.process_single()) {
142 fprintf(stderr
, "ERROR: decoding frame in decoder2, state=%s\n", d2
.get_state().resolved_as_cstring(d2
));
147 fprintf(stderr
, "ERROR: got err_ in decoder1, state=%s\n", d1
.get_state().resolved_as_cstring(d1
));
151 fprintf(stderr
, "ERROR: got err_ in decoder2, state=%s\n", d2
.get_state().resolved_as_cstring(d2
));
154 if(d1
.go_
!= d2
.go_
) {
155 fprintf(stderr
, "ERROR: d1.go_(%s) != d2.go_(%s)\n", d1
.go_
?"true":"false", d2
.go_
?"true":"false");
158 fprintf(stdout
, "pos1 = %" PRIu64
" blocksize=%u sample#%" PRIu64
" frame#%" PRIu64
"\n", d1
.curpos_
, d1
.frame_
.header
.blocksize
, d1
.frame_
.header
.number
.sample_number
, d1
.frame_
.header
.number
.sample_number
/ d1
.frame_
.header
.blocksize
);
159 fprintf(stdout
, "pos2 = %" PRIu64
" blocksize=%u sample#%" PRIu64
" frame#%" PRIu64
"\n", d2
.curpos_
, d2
.frame_
.header
.blocksize
, d2
.frame_
.header
.number
.sample_number
, d2
.frame_
.header
.number
.sample_number
/ d2
.frame_
.header
.blocksize
);
164 static FLAC__off_t
get_diff_offset(AutoFILE
&f1
, AutoFILE
&f2
)
168 if(feof((FILE*)f1
) && feof((FILE*)f1
)) {
169 fprintf(stderr
, "ERROR: files are identical\n");
172 if(feof((FILE*)f1
)) {
173 fprintf(stderr
, "ERROR: file1 EOF\n");
176 if(feof((FILE*)f2
)) {
177 fprintf(stderr
, "ERROR: file2 EOF\n");
180 if(fgetc(f1
) != fgetc(f2
))
186 static bool run(const char *fn1
, const char *fn2
)
189 AutoFILE
f1(fn1
, "rb"), f2(fn2
, "rb");
192 fprintf(stderr
, "ERROR: opening %s for reading\n", fn1
);
196 fprintf(stderr
, "ERROR: opening %s for reading\n", fn2
);
200 if((off
= get_diff_offset(f1
, f2
)) < 0)
203 fprintf(stdout
, "got diff offset = %" PRId64
"\n", off
);
205 return show_diff(f1
, f2
, off
);
208 int main(int argc
, char *argv
[])
210 const char *usage
= "usage: flacdiff flacfile1 flacfile2\n";
212 if(argc
> 1 && 0 == strcmp(argv
[1], "-h")) {
217 fprintf(stderr
, usage
);
221 return run(argv
[1], argv
[2])? 0 : 1;