encode.c 110 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,2006  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
 *
 * 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.
 */

Josh Coalson's avatar
Josh Coalson committed
19 20 21 22
#if HAVE_CONFIG_H
#  include <config.h>
#endif

Josh Coalson's avatar
Josh Coalson committed
23 24 25 26 27 28
#if defined _WIN32 && !defined __CYGWIN__
/* where MSVC puts unlink() */
# include <io.h>
#else
# include <unistd.h>
#endif
Josh Coalson's avatar
Josh Coalson committed
29 30 31 32 33 34 35
#if defined _MSC_VER || defined __MINGW32__
#include <sys/types.h> /* for off_t */
//@@@ [2G limit] hacks for MSVC6
#define fseeko fseek
#define ftello ftell
#endif
#include <errno.h>
36
#include <limits.h> /* for LONG_MAX */
37
#include <math.h> /* for floor() */
Josh Coalson's avatar
Josh Coalson committed
38
#include <stdio.h> /* for FILE etc. */
Josh Coalson's avatar
Josh Coalson committed
39
#include <stdlib.h> /* for malloc */
Josh Coalson's avatar
Josh Coalson committed
40
#include <string.h> /* for strcmp(), strerror( */
Josh Coalson's avatar
Josh Coalson committed
41
#include "FLAC/all.h"
42
#include "share/grabbag.h"
Josh Coalson's avatar
Josh Coalson committed
43
#include "encode.h"
44

45
#ifdef FLAC__HAS_OGG
46
#include "OggFLAC/stream_encoder.h"
47
#include "OggFLAC/file_encoder.h"
48
#endif
Josh Coalson's avatar
Josh Coalson committed
49

Josh Coalson's avatar
Josh Coalson committed
50 51 52 53
#ifdef min
#undef min
#endif
#define min(x,y) ((x)<(y)?(x):(y))
54 55 56 57
#ifdef max
#undef max
#endif
#define max(x,y) ((x)>(y)?(x):(y))
Josh Coalson's avatar
Josh Coalson committed
58

Josh Coalson's avatar
Josh Coalson committed
59
/* this MUST be >= 588 so that sector aligning can take place with one read */
Josh Coalson's avatar
Josh Coalson committed
60 61 62
#define CHUNK_OF_SAMPLES 2048

typedef struct {
Josh Coalson's avatar
Josh Coalson committed
63 64 65
#ifdef FLAC__HAS_OGG
	FLAC__bool use_ogg;
#endif
Josh Coalson's avatar
Josh Coalson committed
66
	FLAC__bool verify;
67
	FLAC__bool is_stdout;
Josh Coalson's avatar
Josh Coalson committed
68 69 70
	const char *inbasefilename;
	const char *outfilename;

71
	FLAC__uint64 skip;
72
	FLAC__uint64 until; /* a value of 0 mean end-of-stream (i.e. --until=-0) */
73 74 75 76
	FLAC__bool replay_gain;
	unsigned channels;
	unsigned bits_per_sample;
	unsigned sample_rate;
Josh Coalson's avatar
Josh Coalson committed
77 78 79 80
	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
81 82
	unsigned blocksize;
	unsigned stats_mask;
Josh Coalson's avatar
Josh Coalson committed
83

84
	/*
85 86
	 * We use *.stream for encoding to stdout
	 * We use *.file for encoding to a regular file
87
	 */
Josh Coalson's avatar
Josh Coalson committed
88
	union {
89 90 91 92
		union {
			FLAC__StreamEncoder *stream;
			FLAC__FileEncoder *file;
		} flac;
93
#ifdef FLAC__HAS_OGG
94 95
		union {
			OggFLAC__StreamEncoder *stream;
96
			OggFLAC__FileEncoder *file;
97
		} ogg;
98
#endif
Josh Coalson's avatar
Josh Coalson committed
99 100 101 102 103 104
	} encoder;

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

106 107 108 109 110 111 112 113 114 115 116 117 118
/* this is data attached to the FLAC decoder when encoding from a FLAC file */
typedef struct {
	EncoderSession *encoder_session;
	off_t filesize;
	const FLAC__byte *lookahead;
	unsigned lookahead_length;
	size_t num_metadata_blocks;
	FLAC__StreamMetadata *metadata_blocks[1024]; /*@@@ BAD MAGIC number */
	FLAC__uint64 samples_left_to_process;
	FLAC__bool fatal_error;
} FLACDecoderData;

const int FLAC_ENCODE__DEFAULT_PADDING = 4096;
Josh Coalson's avatar
Josh Coalson committed
119

120
static FLAC__bool is_big_endian_host_;
Josh Coalson's avatar
Josh Coalson committed
121

122 123 124 125
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
126

127 128 129 130
static FLAC__int32 in_[FLAC__MAX_CHANNELS][CHUNK_OF_SAMPLES];
static FLAC__int32 *input_[FLAC__MAX_CHANNELS];


131 132 133 134 135 136 137 138 139
/*
 * 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);
140
#ifdef FLAC__HAS_OGG
141 142 143
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);
144 145 146
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);
147
#endif
148

149 150 151
/*
 * local routines
 */
152
static FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg, FLAC__bool verify, FILE *infile, const char *infilename, const char *outfilename);
153 154 155
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);
156
static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, unsigned channels, unsigned bps, unsigned sample_rate, FLACDecoderData *flac_decoder_data);
157
static FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const buffer[], unsigned samples);
158
static FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e);
159
static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, unsigned sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input);
160
static void format_input(FLAC__int32 *dest[], unsigned wide_samples, FLAC__bool is_big_endian, FLAC__bool is_unsigned_samples, unsigned channels, unsigned bps);
161 162
#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);
163
static void ogg_stream_encoder_metadata_callback(const OggFLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data);
164
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);
165 166
#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
167 168
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);
169 170 171 172 173 174 175 176 177
static FLAC__SeekableStreamDecoderReadStatus flac_decoder_read_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
static FLAC__SeekableStreamDecoderSeekStatus flac_decoder_seek_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
static FLAC__SeekableStreamDecoderTellStatus flac_decoder_tell_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
static FLAC__SeekableStreamDecoderLengthStatus flac_decoder_length_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
static FLAC__bool flac_decoder_eof_callback(const FLAC__SeekableStreamDecoder *decoder, void *client_data);
static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
static void flac_decoder_metadata_callback(const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static void flac_decoder_error_callback(const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
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
178
static void print_stats(const EncoderSession *encoder_session);
179 180
static void print_error_with_state(const EncoderSession *e, const char *message);
static void print_verify_error(EncoderSession *e);
181 182 183 184 185
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);
186
static FLAC__bool fskip_ahead(FILE *f, FLAC__uint64 offset);
Josh Coalson's avatar
Josh Coalson committed
187

188 189 190
/*
 * public routines
 */
Josh Coalson's avatar
Josh Coalson committed
191
int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options, FLAC__bool is_aifc)
192
{
Josh Coalson's avatar
Josh Coalson committed
193
	EncoderSession encoder_session;
194 195 196 197 198
	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;
199
	FLAC__bool is_big_endian_pcm = true;
200 201 202 203 204

	(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 */

205 206 207 208 209 210 211 212 213 214 215 216 217 218
	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
219
		return 1;
220 221 222

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

223
	while(1) {
224
		size_t c= 0U;
225
		char chunk_id[5] = { '\0', '\0', '\0', '\0', '\0' }; /* one extra byte for terminating NUL so we can also treat it like a C string */
226 227 228

		/* chunk identifier; really conservative about behavior of fread() and feof() */
		if(feof(infile) || ((c= fread(chunk_id, 1U, 4U, infile)), c==0U && feof(infile)))
229
			break;
230
		else if(c<4U || feof(infile)) {
231
			flac__utils_printf(stderr, 1, "%s: ERROR: incomplete chunk identifier\n", encoder_session.inbasefilename);
232
			return EncoderSession_finish_error(&encoder_session);
233 234
		}

235
		if(got_comm_chunk==false && !memcmp(chunk_id, "COMM", 4)) { /* common chunk */
236
			unsigned long skip;
237
			const FLAC__uint32 minimum_comm_size = (is_aifc? 22 : 18);
238

239
			/* COMM chunk size */
240
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
241
				return EncoderSession_finish_error(&encoder_session);
242 243
			else if(xx<minimum_comm_size) {
				flac__utils_printf(stderr, 1, "%s: ERROR: non-standard %s 'COMM' chunk has length = %u\n", encoder_session.inbasefilename, is_aifc? "AIFF-C" : "AIFF", (unsigned int)xx);
244
				return EncoderSession_finish_error(&encoder_session);
245
			}
246 247
			else if(!is_aifc && xx!=minimum_comm_size) {
				flac__utils_printf(stderr, 1, "%s: WARNING: non-standard %s 'COMM' chunk has length = %u, expected %u\n", encoder_session.inbasefilename, is_aifc? "AIFF-C" : "AIFF", (unsigned int)xx, minimum_comm_size);
248
			}
249
			skip= (xx-minimum_comm_size)+(xx & 1U);
250

251
			/* number of channels */
252
			if(!read_big_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
253 254
				return EncoderSession_finish_error(&encoder_session);
			else if(x==0U || x>FLAC__MAX_CHANNELS) {
255
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u\n", encoder_session.inbasefilename, (unsigned int)x);
256
				return EncoderSession_finish_error(&encoder_session);
257
			}
258
			else if(options.common.sector_align && x!=2U) {
259
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)x);
260
				return EncoderSession_finish_error(&encoder_session);
261
			}
262
			channels= x;
263

264
			/* number of sample frames */
265
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
266 267 268 269
				return EncoderSession_finish_error(&encoder_session);
			sample_frames= xx;

			/* bits per sample */
270
			if(!read_big_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
271 272
				return EncoderSession_finish_error(&encoder_session);
			else if(x!=8U && x!=16U && x!=24U) {
273
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits per sample %u\n", encoder_session.inbasefilename, (unsigned int)x);
274
				return EncoderSession_finish_error(&encoder_session);
275
			}
276
			else if(options.common.sector_align && x!=16U) {
277
				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);
278 279 280
				return EncoderSession_finish_error(&encoder_session);
			}
			bps= x;
281

282
			/* sample rate */
283
			if(!read_sane_extended(infile, &xx, false, encoder_session.inbasefilename))
284 285
				return EncoderSession_finish_error(&encoder_session);
			else if(!FLAC__format_sample_rate_is_valid(xx)) {
286
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, (unsigned int)xx);
287 288 289
				return EncoderSession_finish_error(&encoder_session);
			}
			else if(options.common.sector_align && xx!=44100U) {
290
				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);
291
				return EncoderSession_finish_error(&encoder_session);
292
			}
293
			sample_rate= xx;
294

295 296 297 298 299 300 301 302 303 304 305 306 307 308
			/* check compression type for AIFF-C */
			if(is_aifc) {
				if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
					return EncoderSession_finish_error(&encoder_session);
				if(xx == 0x736F7774) /* "sowt" */
					is_big_endian_pcm = false;
				else if(xx == 0x4E4F4E45) /* "NONE" */
					; /* nothing to do, we already default to big-endian */
				else {
					flac__utils_printf(stderr, 1, "%s: ERROR: can't handle AIFF-C compression type \"%c%c%c%c\"\n", encoder_session.inbasefilename, (char)(xx>>24), (char)((xx>>16)&8), (char)((xx>>8)&8), (char)(xx&8));
					return EncoderSession_finish_error(&encoder_session);
				}
			}

309
			/* skip any extra data in the COMM chunk */
310 311 312
			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);
313 314
			}

315 316 317 318 319 320 321 322 323
			/*
			 * 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);

324 325
			got_comm_chunk= true;
		}
326
		else if(got_ssnd_chunk==false && !memcmp(chunk_id, "SSND", 4)) { /* sound data chunk */
327 328
			unsigned int offset= 0U, block_size= 0U, align_remainder= 0U, data_bytes;
			size_t bytes_per_frame= channels*(bps>>3);
329
			FLAC__uint64 total_samples_in_input, trim = 0;
330 331
			FLAC__bool pad= false;

332
			if(got_comm_chunk==false) {
333
				flac__utils_printf(stderr, 1, "%s: ERROR: got 'SSND' chunk before 'COMM' chunk\n", encoder_session.inbasefilename);
334
				return EncoderSession_finish_error(&encoder_session);
335 336
			}

337
			/* SSND chunk size */
338
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
339 340 341
				return EncoderSession_finish_error(&encoder_session);
			data_bytes= xx;
			pad= (data_bytes & 1U) ? true : false;
342
			data_bytes-= 8U; /* discount the offset and block size fields */
343

344
			/* offset */
345
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
346 347
				return EncoderSession_finish_error(&encoder_session);
			offset= xx;
348
			data_bytes-= offset;
349

350
			/* block size */
351
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
352 353
				return EncoderSession_finish_error(&encoder_session);
			else if(xx!=0U) {
354
				flac__utils_printf(stderr, 1, "%s: ERROR: block size is %u; must be 0\n", encoder_session.inbasefilename, (unsigned int)xx);
355
				return EncoderSession_finish_error(&encoder_session);
356
			}
357
			block_size= xx;
358

359 360 361
			/* skip any SSND offset bytes */
			FLAC__ASSERT(offset<=LONG_MAX);
			if(!fskip_ahead(infile, offset)) {
362 363 364 365 366 367 368 369
				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);
			}

370 371 372 373 374 375 376 377
			/* *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:
			 */
378
			if(!canonicalize_until_specification(&options.common.until_specification, encoder_session.inbasefilename, sample_rate, encoder_session.skip, total_samples_in_input))
379 380 381 382
				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);

383
			if(encoder_session.skip>0U) {
384 385 386
				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);
387 388 389
				}
			}

390 391 392
			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) {
393
				trim = total_samples_in_input - encoder_session.until;
394 395 396 397 398
				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;
			}
399 400 401 402 403 404 405
			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 */
			}
406

407 408
			/* +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;
409

410
			if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate, /*flac_decoder_data=*/0))
411
				return EncoderSession_finish_error(&encoder_session);
412 413

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

416 417 418
				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);
419 420 421 422
				}
			}

			/* decrement the data_bytes counter if we need to align the file */
423
			if(options.common.sector_align) {
424 425 426 427 428 429 430 431 432
				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 */
433 434
			while(data_bytes>0) {
				size_t bytes_read= fread(ucbuffer_, 1U, min(data_bytes, CHUNK_OF_SAMPLES*bytes_per_frame), infile);
435 436 437

				if(bytes_read==0U) {
					if(ferror(infile)) {
438
						flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
439
						return EncoderSession_finish_error(&encoder_session);
440 441
					}
					else if(feof(infile)) {
442
						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);
443 444 445 446 447
						data_bytes= 0;
					}
				}
				else {
					if(bytes_read % bytes_per_frame != 0U) {
448
						flac__utils_printf(stderr, 1, "%s: ERROR: got partial sample\n", encoder_session.inbasefilename);
449
						return EncoderSession_finish_error(&encoder_session);
450 451 452
					}
					else {
						unsigned int frames= bytes_read/bytes_per_frame;
453
						format_input(input_, frames, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps);
454

455 456 457
						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);
458 459 460 461 462 463 464
						}
						else
							data_bytes-= bytes_read;
					}
				}
			}

465 466
			if(trim>0) {
				FLAC__ASSERT(!options.common.sector_align);
467 468 469
				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);
470 471 472
				}
			}

473
			/* now read unaligned samples into reservoir or pad with zeroes if necessary */
474
			if(options.common.sector_align) {
475 476 477 478 479 480 481 482
				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)
483
							memset(input_[i], 0, sizeof(input_[0][0])*pad_frames);
484

485 486 487
						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);
488 489 490 491 492
						}
					}
				}
				else {
					if(*options.common.align_reservoir_samples > 0) {
493
						size_t bytes_read= fread(ucbuffer_, 1U, (*options.common.align_reservoir_samples)*bytes_per_frame, infile);
494 495 496

						FLAC__ASSERT(CHUNK_OF_SAMPLES>=588U);
						if(bytes_read==0U && ferror(infile)) {
497
							flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
498
							return EncoderSession_finish_error(&encoder_session);
499
						}
500
						else if(bytes_read != (*options.common.align_reservoir_samples) * bytes_per_frame) {
501
							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);
502
						}
503 504
						else {
							info_align_carry= *options.common.align_reservoir_samples;
505
							format_input(options.common.align_reservoir, *options.common.align_reservoir_samples, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps);
506 507 508 509 510
						}
					}
				}
			}

511
			if(pad==true) {
512 513 514
				unsigned char tmp;

				if(fread(&tmp, 1U, 1U, infile)<1U) {
515
					flac__utils_printf(stderr, 1, "%s: ERROR during read of SSND pad byte\n", encoder_session.inbasefilename);
516
					return EncoderSession_finish_error(&encoder_session);
517 518 519 520 521
				}
			}

			got_ssnd_chunk= true;
		}
522
		else { /* other chunk */
523
			if(!memcmp(chunk_id, "COMM", 4)) {
524
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'COMM' chunk\n", encoder_session.inbasefilename);
525
			}
526
			else if(!memcmp(chunk_id, "SSND", 4)) {
527
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping extra 'SSND' chunk\n", encoder_session.inbasefilename);
528 529
			}
			else {
530
				flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown chunk '%s'\n", encoder_session.inbasefilename, chunk_id);
531
			}
532 533

			/* chunk size */
534
			if(!read_big_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
535
				return EncoderSession_finish_error(&encoder_session);
536 537 538 539
			else {
				unsigned long skip= xx+(xx & 1U);

				FLAC__ASSERT(skip<=LONG_MAX);
540 541 542
				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);
543 544 545 546 547 548
				}
			}
		}
	}

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

553
	return EncoderSession_finish_ok(&encoder_session, info_align_carry, info_align_zero);
554 555
}

Josh Coalson's avatar
Josh Coalson committed
556
int flac__encode_wav(FILE *infile, off_t 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
557
{
Josh Coalson's avatar
Josh Coalson committed
558
	EncoderSession encoder_session;
Josh Coalson's avatar
Josh Coalson committed
559
	FLAC__bool is_unsigned_samples = false;
560
	unsigned channels = 0, bps = 0, sample_rate = 0;
Josh Coalson's avatar
Josh Coalson committed
561
	size_t bytes_per_wide_sample, bytes_read;
Josh Coalson's avatar
Josh Coalson committed
562 563 564
	FLAC__uint16 x;
	FLAC__uint32 xx;
	FLAC__bool got_fmt_chunk = false, got_data_chunk = false;
Josh Coalson's avatar
Josh Coalson committed
565 566 567
	unsigned align_remainder = 0;
	int info_align_carry = -1, info_align_zero = -1;

568 569 570
	(void)infilesize;
	(void)lookahead;
	(void)lookahead_length;
Josh Coalson's avatar
Josh Coalson committed
571

572 573 574 575 576 577 578 579 580 581 582 583 584 585
	if(!
		EncoderSession_construct(
			&encoder_session,
#ifdef FLAC__HAS_OGG
			options.common.use_ogg,
#else
			/*use_ogg=*/false,
#endif
			options.common.verify,
			infile,
			infilename,
			outfilename
		)
	)
586
		return 1;
Josh Coalson's avatar
Josh Coalson committed
587 588

	/*
589
	 * lookahead[] already has "RIFFxxxxWAVE", do sub-chunks
Josh Coalson's avatar
Josh Coalson committed
590
	 */
591
	while(!feof(infile)) {
592
		if(!read_little_endian_uint32(infile, &xx, true, encoder_session.inbasefilename))
593
			return EncoderSession_finish_error(&encoder_session);
594 595
		if(feof(infile))
			break;
596
		if(xx == 0x20746d66 && !got_fmt_chunk) { /* "fmt " */
597
			unsigned block_align, data_bytes;
598

599
			/* fmt sub-chunk size */
600
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
601
				return EncoderSession_finish_error(&encoder_session);
602
			if(xx < 16) {
603
				flac__utils_printf(stderr, 1, "%s: ERROR: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, (unsigned)xx);
604
				return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
605
			}
606
			else if(xx != 16 && xx != 18) {
607
				flac__utils_printf(stderr, 1, "%s: WARNING: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, (unsigned)xx);
608 609 610
			}
			data_bytes = xx;
			/* compression code */
611
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
612
				return EncoderSession_finish_error(&encoder_session);
613
			if(x != 1) {
614
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported compression type %u\n", encoder_session.inbasefilename, (unsigned)x);
615
				return EncoderSession_finish_error(&encoder_session);
616 617
			}
			/* number of channels */
618
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
619
				return EncoderSession_finish_error(&encoder_session);
620
			if(x == 0 || x > FLAC__MAX_CHANNELS) {
621
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u\n", encoder_session.inbasefilename, (unsigned)x);
622
				return EncoderSession_finish_error(&encoder_session);
623 624
			}
			else if(options.common.sector_align && x != 2) {
625
				flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned)x);
626
				return EncoderSession_finish_error(&encoder_session);
627 628 629
			}
			channels = x;
			/* sample rate */
630
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
631
				return EncoderSession_finish_error(&encoder_session);
632
			if(!FLAC__format_sample_rate_is_valid(xx)) {
633
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, (unsigned)xx);
634
				return EncoderSession_finish_error(&encoder_session);
635 636
			}
			else if(options.common.sector_align && xx != 44100) {
637
				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);
638
				return EncoderSession_finish_error(&encoder_session);
639 640 641
			}
			sample_rate = xx;
			/* avg bytes per second (ignored) */
642
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
643
				return EncoderSession_finish_error(&encoder_session);
644
			/* block align */
645
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
646
				return EncoderSession_finish_error(&encoder_session);
647
			block_align = x;
648
			/* bits per sample */
649
			if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
650
				return EncoderSession_finish_error(&encoder_session);
651
			if(x != 8 && x != 16 && x != 24) {
652
				flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits-per-sample %u\n", encoder_session.inbasefilename, (unsigned)x);
653
				return EncoderSession_finish_error(&encoder_session);
654 655
			}
			else if(options.common.sector_align && x != 16) {
656
				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);
657
				return EncoderSession_finish_error(&encoder_session);
658 659
			}
			bps = x;
660
			if(bps * channels != block_align * 8) {
661
				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);
662 663
				return EncoderSession_finish_error(&encoder_session);
			}
664 665 666
			is_unsigned_samples = (x == 8);

			/* skip any extra data in the fmt sub-chunk */
667
			FLAC__ASSERT(data_bytes >= 16);
668
			data_bytes -= 16;
669 670 671
			if(!fskip_ahead(infile, data_bytes)) {
				flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping extra 'fmt' data\n", encoder_session.inbasefilename);
				return EncoderSession_finish_error(&encoder_session);
672 673
			}

674 675 676 677 678 679 680 681 682
			/*
			 * 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);

683 684 685
			got_fmt_chunk = true;
		}
		else if(xx == 0x61746164 && !got_data_chunk && got_fmt_chunk) { /* "data" */
686
			FLAC__uint64 total_samples_in_input, trim = 0;
687
			FLAC__bool pad = false;
688
			unsigned data_bytes;
689

690
			/* data size */
691
			if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
692
				return EncoderSession_finish_error(&encoder_session);
693
			data_bytes = xx;
694
			pad = (data_bytes & 1U) ? true : false;
695 696

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

698 699 700 701 702 703 704 705
			/* *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:
			 */
706
			if(!canonicalize_until_specification(&options.common.until_specification, encoder_session.inbasefilename, sample_rate, encoder_session.skip, total_samples_in_input))
707 708 709 710
				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);

711
			if(encoder_session.skip > 0) {
712 713 714
				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);
715
				}
Josh Coalson's avatar
Josh Coalson committed
716
			}
717

718
			data_bytes -= (unsigned)encoder_session.skip * bytes_per_wide_sample; /*@@@ WATCHOUT: 4GB limit */
719 720
			encoder_session.total_samples_to_encode = total_samples_in_input - encoder_session.skip;
			if(encoder_session.until > 0) {
721
				trim = total_samples_in_input - encoder_session.until;
722 723 724 725 726
				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;
			}
727
			if(options.common.sector_align) {
Josh Coalson's avatar
Josh Coalson committed
728
				align_remainder = (unsigned)(encoder_session.total_samples_to_encode % 588);
729
				if(options.common.is_last_file)
Josh Coalson's avatar
Josh Coalson committed
730
					encoder_session.total_samples_to_encode += (588-align_remainder); /* will pad with zeroes */
731
				else
Josh Coalson's avatar
Josh Coalson committed
732
					encoder_session.total_samples_to_encode -= align_remainder; /* will stop short and carry over to next file */
733
			}
734 735

			/* +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
736
			encoder_session.unencoded_size = encoder_session.total_samples_to_encode * bytes_per_wide_sample + 44;
737

738
			if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate, /*flac_decoder_data=*/0))
739
				return EncoderSession_finish_error(&encoder_session);
740

741 742 743 744
			/*
			 * first do any samples in the reservoir
			 */
			if(options.common.sector_align && *options.common.align_reservoir_samples > 0) {
745 746 747
				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);
748 749
				}
			}
750

751 752 753 754 755 756 757 758 759 760 761 762
			/*
			 * 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
763

764 765 766 767
			/*
			 * now do from the file
			 */
			while(data_bytes > 0) {
768
				bytes_read = fread(ucbuffer_, sizeof(unsigned char), min(data_bytes, CHUNK_OF_SAMPLES * bytes_per_wide_sample), infile);
769 770
				if(bytes_read == 0) {
					if(ferror(infile)) {
771
						flac__utils_printf(stderr, 1, "%s: ERROR during read\n", encoder_session.inbasefilename);
772
						return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
773
					}
774
					else if(feof(infile)) {
775
						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);
776 777
						data_bytes = 0;
					}
Josh Coalson's avatar
Josh Coalson committed
778
				}
779 780
				else {
					if(bytes_read % bytes_per_wide_sample != 0) {
781
						flac__utils_printf(stderr, 1, "%s: ERROR: got partial sample\n", encoder_session.inbasefilename);
782
						return EncoderSession_finish_error(&encoder_session);
Josh Coalson's avatar
Josh Coalson committed
783 784
					}
					else {
785
						unsigned wide_samples = bytes_read / bytes_per_wide_sample;
786
						format_input(input_, wide_samples, /*is_big_endian=*/false, is_unsigned_samples, channels, bps);
Josh Coalson's avatar
Josh Coalson committed
787

788 789 790
						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);
791
						}
792
						data_bytes -= bytes_read;
793
					}
794 795 796
				}
			}

797
			if(trim > 0) {
798 799 800 801
				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);
802 803 804
				}
			}

805 806 807 808 809 810 811 812 813 814 815
			/*
			 * 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;
						for(channel = 0; channel < channels; channel++)