encode.c 85.2 KB
Newer Older
Josh Coalson's avatar
Josh Coalson committed
1
/* flac - Command-line FLAC encoder/decoder
Josh Coalson's avatar
Josh Coalson committed
2
 * Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson
Josh Coalson's avatar
Josh Coalson committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#if defined _WIN32 && !defined __CYGWIN__
/* where MSVC puts unlink() */
# include <io.h>
#else
# include <unistd.h>
#endif
25
#include <limits.h> /* for LONG_MAX */
26
#include <math.h> /* for floor() */
Josh Coalson's avatar
Josh Coalson committed
27
#include <stdio.h> /* for FILE etc. */
Josh Coalson's avatar
Josh Coalson committed
28
#include <stdlib.h> /* for malloc */
Josh Coalson's avatar
Josh Coalson committed
29
30
#include <string.h> /* for strcmp() */
#include "FLAC/all.h"
31
#include "share/grabbag.h"
Josh Coalson's avatar
Josh Coalson committed
32
#include "encode.h"
33
34
35
36
37

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

38
#ifdef FLAC__HAS_OGG
39
#include "OggFLAC/stream_encoder.h"
40
#include "OggFLAC/file_encoder.h"
41
#endif
Josh Coalson's avatar
Josh Coalson committed
42

Josh Coalson's avatar
Josh Coalson committed
43
44
45
46
#ifdef min
#undef min
#endif
#define min(x,y) ((x)<(y)?(x):(y))
47
48
49
50
#ifdef max
#undef max
#endif
#define max(x,y) ((x)>(y)?(x):(y))
Josh Coalson's avatar
Josh Coalson committed
51

Josh Coalson's avatar
Josh Coalson committed
52
/* this MUST be >= 588 so that sector aligning can take place with one read */
Josh Coalson's avatar
Josh Coalson committed
53
54
55
#define CHUNK_OF_SAMPLES 2048

typedef struct {
Josh Coalson's avatar
Josh Coalson committed
56
57
58
#ifdef FLAC__HAS_OGG
	FLAC__bool use_ogg;
#endif
Josh Coalson's avatar
Josh Coalson committed
59
	FLAC__bool verify;
60
	FLAC__bool is_stdout;
Josh Coalson's avatar
Josh Coalson committed
61
62
63
	const char *inbasefilename;
	const char *outfilename;

64
	FLAC__uint64 skip;
65
	FLAC__uint64 until; /* a value of 0 mean end-of-stream (i.e. --until=-0) */
66
67
68
69
	FLAC__bool replay_gain;
	unsigned channels;
	unsigned bits_per_sample;
	unsigned sample_rate;
Josh Coalson's avatar
Josh Coalson committed
70
71
72
73
	FLAC__uint64 unencoded_size;
	FLAC__uint64 total_samples_to_encode;
	FLAC__uint64 bytes_written;
	FLAC__uint64 samples_written;
Josh Coalson's avatar
Josh Coalson committed
74
75
	unsigned blocksize;
	unsigned stats_mask;
Josh Coalson's avatar
Josh Coalson committed
76

77
	/*
78
79
	 * We use *.stream for encoding to stdout
	 * We use *.file for encoding to a regular file
80
	 */
Josh Coalson's avatar
Josh Coalson committed
81
	union {
82
83
84
85
		union {
			FLAC__StreamEncoder *stream;
			FLAC__FileEncoder *file;
		} flac;
86
#ifdef FLAC__HAS_OGG
87
88
		union {
			OggFLAC__StreamEncoder *stream;
89
			OggFLAC__FileEncoder *file;
90
		} ogg;
91
#endif
Josh Coalson's avatar
Josh Coalson committed
92
93
94
95
96
97
	} encoder;

	FILE *fin;
	FILE *fout;
	FLAC__StreamMetadata *seek_table_template;
} EncoderSession;
Josh Coalson's avatar
Josh Coalson committed
98
99


100
static FLAC__bool is_big_endian_host_;
Josh Coalson's avatar
Josh Coalson committed
101

102
103
104
105
static unsigned char ucbuffer_[CHUNK_OF_SAMPLES*FLAC__MAX_CHANNELS*((FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE+7)/8)];
static signed char *scbuffer_ = (signed char *)ucbuffer_;
static FLAC__uint16 *usbuffer_ = (FLAC__uint16 *)ucbuffer_;
static FLAC__int16 *ssbuffer_ = (FLAC__int16 *)ucbuffer_;
Josh Coalson's avatar
Josh Coalson committed
106

107
108
109
110
static FLAC__int32 in_[FLAC__MAX_CHANNELS][CHUNK_OF_SAMPLES];
static FLAC__int32 *input_[FLAC__MAX_CHANNELS];


111
112
113
114
115
116
117
118
119
/*
 * unpublished debug routines from the FLAC libs
 */
extern FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__file_encoder_disable_constant_subframes(FLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__file_encoder_disable_fixed_subframes(FLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool FLAC__file_encoder_disable_verbatim_subframes(FLAC__FileEncoder *encoder, FLAC__bool value);
120
#ifdef FLAC__HAS_OGG
121
122
123
extern FLAC__bool OggFLAC__stream_encoder_disable_constant_subframes(OggFLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__stream_encoder_disable_fixed_subframes(OggFLAC__StreamEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__stream_encoder_disable_verbatim_subframes(OggFLAC__StreamEncoder *encoder, FLAC__bool value);
124
125
126
extern FLAC__bool OggFLAC__file_encoder_disable_constant_subframes(OggFLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__file_encoder_disable_fixed_subframes(OggFLAC__FileEncoder *encoder, FLAC__bool value);
extern FLAC__bool OggFLAC__file_encoder_disable_verbatim_subframes(OggFLAC__FileEncoder *encoder, FLAC__bool value);
127
#endif
128

129
130
131
/*
 * local routines
 */
132
static FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg, FLAC__bool verify, FILE *infile, const char *infilename, const char *outfilename);
133
134
135
136
137
static void EncoderSession_destroy(EncoderSession *e);
static int EncoderSession_finish_ok(EncoderSession *e, int info_align_carry, int info_align_zero);
static int EncoderSession_finish_error(EncoderSession *e);
static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate);
static FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const buffer[], unsigned samples);
138
static FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e);
139
static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, unsigned sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input);
140
static void format_input(FLAC__int32 *dest[], unsigned wide_samples, FLAC__bool is_big_endian, FLAC__bool is_unsigned_samples, unsigned channels, unsigned bps);
141
142
#ifdef FLAC__HAS_OGG
static FLAC__StreamEncoderWriteStatus ogg_stream_encoder_write_callback(const OggFLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
143
static void ogg_stream_encoder_metadata_callback(const OggFLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data);
144
static void ogg_file_encoder_progress_callback(const OggFLAC__FileEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data);
145
146
#endif
static FLAC__StreamEncoderWriteStatus flac_stream_encoder_write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data);
Josh Coalson's avatar
Josh Coalson committed
147
148
static void flac_stream_encoder_metadata_callback(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data);
static void flac_file_encoder_progress_callback(const FLAC__FileEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data);
149
static FLAC__bool parse_cuesheet_(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset);
Josh Coalson's avatar
Josh Coalson committed
150
static void print_stats(const EncoderSession *encoder_session);
151
152
static void print_error_with_state(const EncoderSession *e, const char *message);
static void print_verify_error(EncoderSession *e);
153
154
155
156
157
static FLAC__bool read_little_endian_uint16(FILE *f, FLAC__uint16 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_little_endian_uint32(FILE *f, FLAC__uint32 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_big_endian_uint16(FILE *f, FLAC__uint16 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_big_endian_uint32(FILE *f, FLAC__uint32 *val, FLAC__bool eof_ok, const char *fn);
static FLAC__bool read_sane_extended(FILE *f, FLAC__uint32 *val, FLAC__bool eof_ok, const char *fn);
158
static FLAC__bool fskip_ahead(FILE *f, FLAC__uint64 offset);
Josh Coalson's avatar
Josh Coalson committed
159

160
161
162
/*
 * public routines
 */
163
164
165
166
int
flac__encode_aif(FILE *infile, long infilesize, const char *infilename, const char *outfilename,
	const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options)
{
Josh Coalson's avatar
Josh Coalson committed
167
	EncoderSession encoder_session;
168
169
170
171
172
173
174
175
176
177
	FLAC__uint16 x;
	FLAC__uint32 xx;
	unsigned int channels= 0U, bps= 0U, sample_rate= 0U, sample_frames= 0U;
	FLAC__bool got_comm_chunk= false, got_ssnd_chunk= false;
	int info_align_carry= -1, info_align_zero= -1;

	(void)infilesize; /* silence compiler warning about unused parameter */
	(void)lookahead; /* silence compiler warning about unused parameter */
	(void)lookahead_length; /* silence compiler warning about unused parameter */

178
179
180
181
182
183
184
185
186
187
188
189
190
191
	if(!
		EncoderSession_construct(
			&encoder_session,
#ifdef FLAC__HAS_OGG
			options.common.use_ogg,
#else
			/*use_ogg=*/false,
#endif
			options.common.verify,
			infile,
			infilename,
			outfilename
		)
	)
Josh Coalson's avatar
Josh Coalson committed
192
		return 1;
193
194
195

	/* lookahead[] already has "FORMxxxxAIFF", do sub-chunks */

196
	while(1) {
197
198
199
200
201
		size_t c= 0U;
		char chunk_id[4];

		/* chunk identifier; really conservative about behavior of fread() and feof() */
		if(feof(infile) || ((c= fread(chunk_id, 1U, 4U, infile)), c==0U && feof(infile)))
202
			break;
203
		else if(c<4U || feof(infile)) {
204
			flac__utils_printf(stderr, 1, "%s: ERROR: incomplete chunk identifier\n", encoder_session.inbasefilename);
205
			return EncoderSession_finish_error(&encoder_session);
206
207
		}

208
		if(got_comm_chunk==false && !strncmp(chunk_id, "COMM", 4)) { /* common chunk */
209
210
			unsigned long skip;

211
			/* COMM chunk size */
212
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
213
214
				return EncoderSession_finish_error(&encoder_session);
			else if(xx<18U) {
215
				flac__utils_printf(stderr, 1, "%s: ERROR: non-standard 'COMM' chunk has length = %u\n", encoder_session.inbasefilename, (unsigned int)xx);
216
				return EncoderSession_finish_error(&encoder_session);
217
			}
218
			else if(xx!=18U) {
219
				flac__utils_printf(stderr, 1, "%s: WARNING: non-standard 'COMM' chunk has length = %u\n", encoder_session.inbasefilename, (unsigned int)xx);
220
			}
221
			skip= (xx-18U)+(xx & 1U);
222

223
			/* number of channels */
224
			if(!read_big_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
225
226
				return EncoderSession_finish_error(&encoder_session);
			else if(x==0U || x>FLAC__MAX_CHANNELS) {
227
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u\n", encoder_session.inbasefilename, (unsigned int)x);
228
				return EncoderSession_finish_error(&encoder_session);
229
			}
230
			else if(options.common.sector_align && x!=2U) {
231
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)x);
232
				return EncoderSession_finish_error(&encoder_session);
233
			}
234
			channels= x;
235

236
			/* number of sample frames */
237
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
238
239
240
241
				return EncoderSession_finish_error(&encoder_session);
			sample_frames= xx;

			/* bits per sample */
242
			if(!read_big_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
243
244
				return EncoderSession_finish_error(&encoder_session);
			else if(x!=8U && x!=16U && x!=24U) {
245
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits per sample %u\n", encoder_session.inbasefilename, (unsigned int)x);
246
				return EncoderSession_finish_error(&encoder_session);
247
			}
248
			else if(options.common.sector_align && x!=16U) {
249
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u bits per sample, must be 16 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)x);
250
251
252
				return EncoderSession_finish_error(&encoder_session);
			}
			bps= x;
253

254
			/* sample rate */
255
			if(!read_sane_extended(infile, &xx, false, encoder_session.inbasefilename))
256
257
				return EncoderSession_finish_error(&encoder_session);
			else if(!FLAC__format_sample_rate_is_valid(xx)) {
258
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, (unsigned int)xx);
259
260
261
				return EncoderSession_finish_error(&encoder_session);
			}
			else if(options.common.sector_align && xx!=44100U) {
262
				flac__utils_printf(stderr, 1, "%s: ERROR: file's sample rate is %u, must be 44100 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)xx);
263
				return EncoderSession_finish_error(&encoder_session);
264
			}
265
			sample_rate= xx;
266
267

			/* skip any extra data in the COMM chunk */
268
269
270
			if(!fskip_ahead(infile, skip)) {
				flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping extra COMM data\n", encoder_session.inbasefilename);
				return EncoderSession_finish_error(&encoder_session);
271
272
			}

273
274
275
276
277
278
279
280
281
			/*
			 * now that we know the sample rate, canonicalize the
			 * --skip string to a number of samples:
			 */
			flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate);
			FLAC__ASSERT(options.common.skip_specification.value.samples >= 0);
			encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0);

282
283
			got_comm_chunk= true;
		}
284
		else if(got_ssnd_chunk==false && !strncmp(chunk_id, "SSND", 4)) { /* sound data chunk */
285
286
			unsigned int offset= 0U, block_size= 0U, align_remainder= 0U, data_bytes;
			size_t bytes_per_frame= channels*(bps>>3);
287
			FLAC__uint64 total_samples_in_input, trim = 0;
288
289
			FLAC__bool pad= false;

290
			if(got_comm_chunk==false) {
291
				flac__utils_printf(stderr, 1, "%s: ERROR: got 'SSND' chunk before 'COMM' chunk\n", encoder_session.inbasefilename);
292
				return EncoderSession_finish_error(&encoder_session);
293
294
			}

295
			/* SSND chunk size */
296
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
297
298
299
				return EncoderSession_finish_error(&encoder_session);
			data_bytes= xx;
			pad= (data_bytes & 1U) ? true : false;
300
			data_bytes-= 8U; /* discount the offset and block size fields */
301

302
			/* offset */
303
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
304
305
				return EncoderSession_finish_error(&encoder_session);
			offset= xx;
306
			data_bytes-= offset;
307

308
			/* block size */
309
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
310
311
				return EncoderSession_finish_error(&encoder_session);
			else if(xx!=0U) {
312
				flac__utils_printf(stderr, 1, "%s: ERROR: block size is %u; must be 0\n", encoder_session.inbasefilename, (unsigned int)xx);
313
				return EncoderSession_finish_error(&encoder_session);
314
			}
315
			block_size= xx;
316

317
318
319
			/* skip any SSND offset bytes */
			FLAC__ASSERT(offset<=LONG_MAX);
			if(!fskip_ahead(infile, offset)) {
320
321
322
323
324
325
326
327
				flac__utils_printf(stderr, 1, "%s: ERROR: skipping offset in SSND chunk\n", encoder_session.inbasefilename);
				return EncoderSession_finish_error(&encoder_session);
			}
			if(data_bytes!=(sample_frames*bytes_per_frame)) {
				flac__utils_printf(stderr, 1, "%s: ERROR: SSND chunk size inconsistent with sample frame count\n", encoder_session.inbasefilename);
				return EncoderSession_finish_error(&encoder_session);
			}

328
329
330
331
332
333
334
335
			/* *options.common.align_reservoir_samples will be 0 unless --sector-align is used */
			FLAC__ASSERT(options.common.sector_align || *options.common.align_reservoir_samples == 0);
			total_samples_in_input = data_bytes / bytes_per_frame + *options.common.align_reservoir_samples;

			/*
			 * now that we know the input size, canonicalize the
			 * --until string to an absolute sample number:
			 */
336
			if(!canonicalize_until_specification(&options.common.until_specification, encoder_session.inbasefilename, sample_rate, encoder_session.skip, total_samples_in_input))
337
338
339
340
				return EncoderSession_finish_error(&encoder_session);
			encoder_session.until = (FLAC__uint64)options.common.until_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.until == 0);

341
			if(encoder_session.skip>0U) {
342
343
344
				if(!fskip_ahead(infile, encoder_session.skip*bytes_per_frame)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
345
346
347
				}
			}

348
349
350
			data_bytes-= (unsigned int)encoder_session.skip*bytes_per_frame; /*@@@ WATCHOUT: 4GB limit */
			encoder_session.total_samples_to_encode= total_samples_in_input - encoder_session.skip;
			if(encoder_session.until > 0) {
351
				trim = total_samples_in_input - encoder_session.until;
352
353
354
355
356
				FLAC__ASSERT(total_samples_in_input > 0);
				FLAC__ASSERT(!options.common.sector_align);
				data_bytes-= (unsigned int)trim*bytes_per_frame;
				encoder_session.total_samples_to_encode-= trim;
			}
357
358
359
360
361
362
363
			if(options.common.sector_align) {
				align_remainder= (unsigned int)(encoder_session.total_samples_to_encode % 588U);
				if(options.common.is_last_file)
					encoder_session.total_samples_to_encode+= (588U-align_remainder); /* will pad with zeroes */
				else
					encoder_session.total_samples_to_encode-= align_remainder; /* will stop short and carry over to next file */
			}
364

365
366
			/* +54 for the size of the AIFF headers; this is just an estimate for the progress indicator and doesn't need to be exact */
			encoder_session.unencoded_size= encoder_session.total_samples_to_encode*bytes_per_frame+54;
367

368
369
			if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate))
				return EncoderSession_finish_error(&encoder_session);
370
371

			/* first do any samples in the reservoir */
372
			if(options.common.sector_align && *options.common.align_reservoir_samples>0U) {
373

374
375
376
				if(!EncoderSession_process(&encoder_session, (const FLAC__int32 *const *)options.common.align_reservoir, *options.common.align_reservoir_samples)) {
					print_error_with_state(&encoder_session, "ERROR during encoding");
					return EncoderSession_finish_error(&encoder_session);
377
378
379
380
				}
			}

			/* decrement the data_bytes counter if we need to align the file */
381
			if(options.common.sector_align) {
382
383
384
385
386
387
388
389
390
				if(options.common.is_last_file)
					*options.common.align_reservoir_samples= 0U;
				else {
					*options.common.align_reservoir_samples= align_remainder;
					data_bytes-= (*options.common.align_reservoir_samples)*bytes_per_frame;
				}
			}

			/* now do from the file */
391
392
			while(data_bytes>0) {
				size_t bytes_read= fread(ucbuffer_, 1U, min(data_bytes, CHUNK_OF_SAMPLES*bytes_per_frame), infile);
393
394
395

				if(bytes_read==0U) {
					if(ferror(infile)) {
396
						flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
397
						return EncoderSession_finish_error(&encoder_session);
398
399
					}
					else if(feof(infile)) {
400
						flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned int)encoder_session.total_samples_to_encode, (unsigned int)encoder_session.samples_written);
401
402
403
404
405
						data_bytes= 0;
					}
				}
				else {
					if(bytes_read % bytes_per_frame != 0U) {
406
						flac__utils_printf(stderr, 1, "%s: ERROR: got partial sample\n", encoder_session.inbasefilename);
407
						return EncoderSession_finish_error(&encoder_session);
408
409
410
					}
					else {
						unsigned int frames= bytes_read/bytes_per_frame;
411
						format_input(input_, frames, true, false, channels, bps);
412

413
414
415
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 *const *)input_, frames)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
416
417
418
419
420
421
422
						}
						else
							data_bytes-= bytes_read;
					}
				}
			}

423
424
			if(trim>0) {
				FLAC__ASSERT(!options.common.sector_align);
425
426
427
				if(!fskip_ahead(infile, trim*bytes_per_frame)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
428
429
430
				}
			}

431
			/* now read unaligned samples into reservoir or pad with zeroes if necessary */
432
			if(options.common.sector_align) {
433
434
435
436
437
438
439
440
				if(options.common.is_last_file) {
					unsigned int pad_frames= 588U-align_remainder;

					if(pad_frames<588U) {
						unsigned int i;

						info_align_zero= pad_frames;
						for(i= 0U; i<channels; ++i)
441
							memset(input_[i], 0, pad_frames*(bps>>3));
442

443
444
445
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 *const *)input_, pad_frames)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
446
447
448
449
450
						}
					}
				}
				else {
					if(*options.common.align_reservoir_samples > 0) {
451
						size_t bytes_read= fread(ucbuffer_, 1U, (*options.common.align_reservoir_samples)*bytes_per_frame, infile);
452
453
454

						FLAC__ASSERT(CHUNK_OF_SAMPLES>=588U);
						if(bytes_read==0U && ferror(infile)) {
455
							flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
456
							return EncoderSession_finish_error(&encoder_session);
457
						}
458
						else if(bytes_read != (*options.common.align_reservoir_samples) * bytes_per_frame) {
459
							flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; read %u bytes; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned int)bytes_read, (unsigned int)encoder_session.total_samples_to_encode, (unsigned int)encoder_session.samples_written);
460
						}
461
462
						else {
							info_align_carry= *options.common.align_reservoir_samples;
463
							format_input(options.common.align_reservoir, *options.common.align_reservoir_samples, true, false, channels, bps);
464
465
466
467
468
						}
					}
				}
			}

469
			if(pad==true) {
470
471
472
				unsigned char tmp;

				if(fread(&tmp, 1U, 1U, infile)<1U) {
473
					flac__utils_printf(stderr, 1, "%s: ERROR during read of SSND pad byte\n", encoder_session.inbasefilename);
474
					return EncoderSession_finish_error(&encoder_session);
475
476
477
478
479
				}
			}

			got_ssnd_chunk= true;
		}
480
		else { /* other chunk */
481
			if(!strncmp(chunk_id, "COMM", 4)) {
482
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'COMM' chunk\n", encoder_session.inbasefilename);
483
484
			}
			else if(!strncmp(chunk_id, "SSND", 4)) {
485
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'SSND' chunk\n", encoder_session.inbasefilename);
486
487
			}
			else {
488
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown chunk '%s'\n", encoder_session.inbasefilename, chunk_id);
489
			}
490
491

			/* chunk size */
492
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
493
				return EncoderSession_finish_error(&encoder_session);
494
495
496
497
			else {
				unsigned long skip= xx+(xx & 1U);

				FLAC__ASSERT(skip<=LONG_MAX);
498
499
500
				if(!fskip_ahead(infile, skip)) {
					fprintf(stderr, "%s: ERROR during read while skipping unknown chunk\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
501
502
503
504
505
506
				}
			}
		}
	}

	if(got_ssnd_chunk==false && sample_frames!=0U) {
507
		flac__utils_printf(stderr, 1, "%s: ERROR: missing SSND chunk\n", encoder_session.inbasefilename);
508
		return EncoderSession_finish_error(&encoder_session);
509
510
	}

511
	return EncoderSession_finish_ok(&encoder_session, info_align_carry, info_align_zero);
512
513
}

514
int flac__encode_wav(FILE *infile, long infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options)
Josh Coalson's avatar
Josh Coalson committed
515
{
Josh Coalson's avatar
Josh Coalson committed
516
	EncoderSession encoder_session;
Josh Coalson's avatar
Josh Coalson committed
517
	FLAC__bool is_unsigned_samples = false;
518
	unsigned channels = 0, bps = 0, sample_rate = 0, data_bytes;
Josh Coalson's avatar
Josh Coalson committed
519
	size_t bytes_per_wide_sample, bytes_read;
Josh Coalson's avatar
Josh Coalson committed
520
521
522
	FLAC__uint16 x;
	FLAC__uint32 xx;
	FLAC__bool got_fmt_chunk = false, got_data_chunk = false;
Josh Coalson's avatar
Josh Coalson committed
523
524
525
	unsigned align_remainder = 0;
	int info_align_carry = -1, info_align_zero = -1;

526
527
528
	(void)infilesize;
	(void)lookahead;
	(void)lookahead_length;
Josh Coalson's avatar
Josh Coalson committed
529

530
531
532
533
534
535
536
537
538
539
540
541
542
543
	if(!
		EncoderSession_construct(
			&encoder_session,
#ifdef FLAC__HAS_OGG
			options.common.use_ogg,
#else
			/*use_ogg=*/false,
#endif
			options.common.verify,
			infile,
			infilename,
			outfilename
		)
	)
544
		return 1;
Josh Coalson's avatar
Josh Coalson committed
545
546

	/*
547
	 * lookahead[] already has "RIFFxxxxWAVE", do sub-chunks
Josh Coalson's avatar
Josh Coalson committed
548
	 */
549
	while(!feof(infile)) {
550
		if(!read_little_endian_uint32(infile, &xx, true, encoder_session.inbasefilename))
551
			return EncoderSession_finish_error(&encoder_session);
552
553
		if(feof(infile))
			break;
554
		if(xx == 0x20746d66 && !got_fmt_chunk) { /* "fmt " */
555
556
			unsigned block_align;

557
			/* fmt sub-chunk size */
558
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
559
				return EncoderSession_finish_error(&encoder_session);
560
			if(xx < 16) {
561
				flac__utils_printf(stderr, 1, "%s: ERROR: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, (unsigned)xx);
562
				return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
563
			}
564
			else if(xx != 16 && xx != 18) {
565
				flac__utils_printf(stderr, 1, "%s: WARNING: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, (unsigned)xx);
566
567
568
			}
			data_bytes = xx;
			/* compression code */
569
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
570
				return EncoderSession_finish_error(&encoder_session);
571
			if(x != 1) {
572
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported compression type %u\n", encoder_session.inbasefilename, (unsigned)x);
573
				return EncoderSession_finish_error(&encoder_session);
574
575
			}
			/* number of channels */
576
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
577
				return EncoderSession_finish_error(&encoder_session);
578
			if(x == 0 || x > FLAC__MAX_CHANNELS) {
579
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u\n", encoder_session.inbasefilename, (unsigned)x);
580
				return EncoderSession_finish_error(&encoder_session);
581
582
			}
			else if(options.common.sector_align && x != 2) {
583
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned)x);
584
				return EncoderSession_finish_error(&encoder_session);
585
586
587
			}
			channels = x;
			/* sample rate */
588
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
589
				return EncoderSession_finish_error(&encoder_session);
590
			if(!FLAC__format_sample_rate_is_valid(xx)) {
591
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, (unsigned)xx);
592
				return EncoderSession_finish_error(&encoder_session);
593
594
			}
			else if(options.common.sector_align && xx != 44100) {
595
				flac__utils_printf(stderr, 1, "%s: ERROR: file's sample rate is %u, must be 44100 for --sector-align\n", encoder_session.inbasefilename, (unsigned)xx);
596
				return EncoderSession_finish_error(&encoder_session);
597
598
599
			}
			sample_rate = xx;
			/* avg bytes per second (ignored) */
600
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
601
				return EncoderSession_finish_error(&encoder_session);
602
			/* block align */
603
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
604
				return EncoderSession_finish_error(&encoder_session);
605
			block_align = x;
606
			/* bits per sample */
607
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
608
				return EncoderSession_finish_error(&encoder_session);
609
			if(x != 8 && x != 16 && x != 24) {
610
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits-per-sample %u\n", encoder_session.inbasefilename, (unsigned)x);
611
				return EncoderSession_finish_error(&encoder_session);
612
613
			}
			else if(options.common.sector_align && x != 16) {
614
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u bits per sample, must be 16 for --sector-align\n", encoder_session.inbasefilename, (unsigned)x);
615
				return EncoderSession_finish_error(&encoder_session);
616
617
			}
			bps = x;
618
			if(bps * channels != block_align * 8) {
619
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported block alignment (%u), for bits-per-sample=%u, channels=%u\n", encoder_session.inbasefilename, block_align, bps, channels);
620
621
				return EncoderSession_finish_error(&encoder_session);
			}
622
623
624
625
626
627
628
629
			is_unsigned_samples = (x == 8);

			/* skip any extra data in the fmt sub-chunk */
			data_bytes -= 16;
			if(data_bytes > 0) {
				unsigned left, need;
				for(left = data_bytes; left > 0; ) {
					need = min(left, CHUNK_OF_SAMPLES);
630
					if(fread(ucbuffer_, 1U, need, infile) < need) {
631
						flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
632
						return EncoderSession_finish_error(&encoder_session);
633
634
					}
					left -= need;
635
				}
636
637
			}

638
639
640
641
642
643
644
645
646
			/*
			 * now that we know the sample rate, canonicalize the
			 * --skip string to a number of samples:
			 */
			flac__utils_canonicalize_skip_until_specification(&options.common.skip_specification, sample_rate);
			FLAC__ASSERT(options.common.skip_specification.value.samples >= 0);
			encoder_session.skip = (FLAC__uint64)options.common.skip_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.skip == 0);

647
648
649
			got_fmt_chunk = true;
		}
		else if(xx == 0x61746164 && !got_data_chunk && got_fmt_chunk) { /* "data" */
650
			FLAC__uint64 total_samples_in_input, trim = 0;
651
			FLAC__bool pad = false;
652

653
			/* data size */
654
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
655
				return EncoderSession_finish_error(&encoder_session);
656
			data_bytes = xx;
657
			pad = (data_bytes & 1U) ? true : false;
658
659

			bytes_per_wide_sample = channels * (bps >> 3);
Josh Coalson's avatar
Josh Coalson committed
660

661
662
663
664
665
666
667
668
			/* *options.common.align_reservoir_samples will be 0 unless --sector-align is used */
			FLAC__ASSERT(options.common.sector_align || *options.common.align_reservoir_samples == 0);
			total_samples_in_input = data_bytes / bytes_per_wide_sample + *options.common.align_reservoir_samples;

			/*
			 * now that we know the input size, canonicalize the
			 * --until string to an absolute sample number:
			 */
669
			if(!canonicalize_until_specification(&options.common.until_specification, encoder_session.inbasefilename, sample_rate, encoder_session.skip, total_samples_in_input))
670
671
672
673
				return EncoderSession_finish_error(&encoder_session);
			encoder_session.until = (FLAC__uint64)options.common.until_specification.value.samples;
			FLAC__ASSERT(!options.common.sector_align || encoder_session.until == 0);

674
			if(encoder_session.skip > 0) {
675
676
677
				if(!fskip_ahead(infile, encoder_session.skip * bytes_per_wide_sample)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
678
				}
Josh Coalson's avatar
Josh Coalson committed
679
			}
680

681
			data_bytes -= (unsigned)encoder_session.skip * bytes_per_wide_sample; /*@@@ WATCHOUT: 4GB limit */
682
683
			encoder_session.total_samples_to_encode = total_samples_in_input - encoder_session.skip;
			if(encoder_session.until > 0) {
684
				trim = total_samples_in_input - encoder_session.until;
685
686
687
688
689
				FLAC__ASSERT(total_samples_in_input > 0);
				FLAC__ASSERT(!options.common.sector_align);
				data_bytes -= (unsigned int)trim * bytes_per_wide_sample;
				encoder_session.total_samples_to_encode -= trim;
			}
690
			if(options.common.sector_align) {
Josh Coalson's avatar
Josh Coalson committed
691
				align_remainder = (unsigned)(encoder_session.total_samples_to_encode % 588);
692
				if(options.common.is_last_file)
Josh Coalson's avatar
Josh Coalson committed
693
					encoder_session.total_samples_to_encode += (588-align_remainder); /* will pad with zeroes */
694
				else
Josh Coalson's avatar
Josh Coalson committed
695
					encoder_session.total_samples_to_encode -= align_remainder; /* will stop short and carry over to next file */
696
			}
697
698

			/* +44 for the size of the WAV headers; this is just an estimate for the progress indicator and doesn't need to be exact */
Josh Coalson's avatar
Josh Coalson committed
699
			encoder_session.unencoded_size = encoder_session.total_samples_to_encode * bytes_per_wide_sample + 44;
700

701
702
			if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate))
				return EncoderSession_finish_error(&encoder_session);
703

704
705
706
707
			/*
			 * first do any samples in the reservoir
			 */
			if(options.common.sector_align && *options.common.align_reservoir_samples > 0) {
708
709
710
				if(!EncoderSession_process(&encoder_session, (const FLAC__int32 * const *)options.common.align_reservoir, *options.common.align_reservoir_samples)) {
					print_error_with_state(&encoder_session, "ERROR during encoding");
					return EncoderSession_finish_error(&encoder_session);
711
712
				}
			}
713

714
715
716
717
718
719
720
721
722
723
724
725
			/*
			 * decrement the data_bytes counter if we need to align the file
			 */
			if(options.common.sector_align) {
				if(options.common.is_last_file) {
					*options.common.align_reservoir_samples = 0;
				}
				else {
					*options.common.align_reservoir_samples = align_remainder;
					data_bytes -= (*options.common.align_reservoir_samples) * bytes_per_wide_sample;
				}
			}
Josh Coalson's avatar
Josh Coalson committed
726

727
728
729
730
			/*
			 * now do from the file
			 */
			while(data_bytes > 0) {
731
				bytes_read = fread(ucbuffer_, sizeof(unsigned char), min(data_bytes, CHUNK_OF_SAMPLES * bytes_per_wide_sample), infile);
732
733
				if(bytes_read == 0) {
					if(ferror(infile)) {
734
						flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
735
						return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
736
					}
737
					else if(feof(infile)) {
738
						flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned)encoder_session.total_samples_to_encode, (unsigned)encoder_session.samples_written);
739
740
						data_bytes = 0;
					}
Josh Coalson's avatar
Josh Coalson committed
741
				}
742
743
				else {
					if(bytes_read % bytes_per_wide_sample != 0) {
744
						flac__utils_printf(stderr, 1, "%s: ERROR: got partial sample\n", encoder_session.inbasefilename);
745
						return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
746
747
					}
					else {
748
						unsigned wide_samples = bytes_read / bytes_per_wide_sample;
749
						format_input(input_, wide_samples, false, is_unsigned_samples, channels, bps);
Josh Coalson's avatar
Josh Coalson committed
750

751
752
753
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 * const *)input_, wide_samples)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
754
						}
755
						data_bytes -= bytes_read;
756
					}
757
758
759
				}
			}

760
			if(trim > 0) {
761
762
763
764
				FLAC__ASSERT(!options.common.sector_align);
				if(!fskip_ahead(infile, trim * bytes_per_wide_sample)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping samples\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
765
766
767
				}
			}

768
769
770
771
772
773
774
775
776
777
778
779
			/*
			 * now read unaligned samples into reservoir or pad with zeroes if necessary
			 */
			if(options.common.sector_align) {
				if(options.common.is_last_file) {
					unsigned wide_samples = 588 - align_remainder;
					if(wide_samples < 588) {
						unsigned channel;

						info_align_zero = wide_samples;
						data_bytes = wide_samples * (bps >> 3);
						for(channel = 0; channel < channels; channel++)
780
							memset(input_[channel], 0, data_bytes);
781

782
783
784
						if(!EncoderSession_process(&encoder_session, (const FLAC__int32 * const *)input_, wide_samples)) {
							print_error_with_state(&encoder_session, "ERROR during encoding");
							return EncoderSession_finish_error(&encoder_session);
785
786
						}
					}
787
				}
788
789
790
				else {
					if(*options.common.align_reservoir_samples > 0) {
						FLAC__ASSERT(CHUNK_OF_SAMPLES >= 588);
791
						bytes_read = fread(ucbuffer_, sizeof(unsigned char), (*options.common.align_reservoir_samples) * bytes_per_wide_sample, infile);
792
						if(bytes_read == 0 && ferror(infile)) {
793
							flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
794
							return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
795
						}
796
						else if(bytes_read != (*options.common.align_reservoir_samples) * bytes_per_wide_sample) {
797
							flac__utils_printf(stderr, 1, "%s: WARNING: unexpected EOF; read %u bytes; expected %u samples, got %u samples\n", encoder_session.inbasefilename, (unsigned)bytes_read, (unsigned)encoder_session.total_samples_to_encode, (unsigned)encoder_session.samples_written);
798
799
800
801
							data_bytes = 0;
						}
						else {
							info_align_carry = *options.common.align_reservoir_samples;
802
							format_input(options.common.align_reservoir, *options.common.align_reservoir_samples, false, is_unsigned_samples, channels, bps);
Josh Coalson's avatar
Josh Coalson committed
803
804
805
						}
					}
				}
806
			}
807

808
809
810
811
812
813
814
815
816
			if(pad == true) {
				unsigned char tmp;

				if(fread(&tmp, 1U, 1U, infile) < 1U) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read of data pad byte\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
				}
			}

817
			got_data_chunk = true;
818
819
		}
		else {
820
			if(xx == 0x20746d66 && got_fmt_chunk) { /* "fmt " */
821
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'fmt ' sub-chunk\n", encoder_session.inbasefilename);
822
823
824
			}
			else if(xx == 0x61746164) { /* "data" */
				if(got_data_chunk) {
825
					flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'data' sub-chunk\n", encoder_session.inbasefilename);
826
827
				}
				else if(!got_fmt_chunk) {
828
					flac__utils_printf(stderr, 1, "%s: ERROR: got 'data' sub-chunk before 'fmt' sub-chunk\n", encoder_session.inbasefilename);
829
					return EncoderSession_finish_error(&encoder_session);
830
831
832
833
834
835
				}
				else {
					FLAC__ASSERT(0);
				}
			}
			else {
836
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown sub-chunk '%c%c%c%c'\n", encoder_session.inbasefilename, (char)(xx&255), (char)((xx>>8)&255), (char)((xx>>16)&255), (char)(xx>>24));
837
			}
838
			/* sub-chunk size */
839
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
840
				return EncoderSession_finish_error(&encoder_session);
841
842
843
844
			else {
				unsigned long skip = xx+(xx & 1U);

				FLAC__ASSERT(skip<=LONG_MAX);
845
846
847
				if(!fskip_ahead(infile, skip)) {
					flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping unsupported sub-chunk\n", encoder_session.inbasefilename);
					return EncoderSession_finish_error(&encoder_session);
848
				}
849
			}
Josh Coalson's avatar
Josh Coalson committed
850
851
852
		}
	}

853
	return EncoderSession_finish_ok(&encoder_session, info_align_carry, info_align_zero);
Josh Coalson's avatar
Josh Coalson committed
854
855
}

856
int flac__encode_raw(FILE *infile, long infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, raw_encode_options_t options)
Josh Coalson's avatar
Josh Coalson committed
857
{
Josh Coalson's avatar
Josh Coalson committed
858
	EncoderSession encoder_session;
Josh Coalson's avatar
Josh Coalson committed
859
	size_t bytes_read;
860
	const size_t bytes_per_wide_sample = options.channels * (options.bps >> 3);
861
862
	unsigned align_remainder = 0;
	int info_align_carry = -1, info_align_zero = -1;
863
	FLAC__uint64 total_samples_in_input = 0;;
Josh Coalson's avatar