Commit cc5c1d88 authored by Josh Coalson's avatar Josh Coalson

complete WAVEFORMATEXTENSIBLE support, multichannel assignments in format and...

complete WAVEFORMATEXTENSIBLE support, multichannel assignments in format and documentation, multichannel support for AIFF and WAVE, channel mapping, new --channel-map=none option to flac, saving and restoring of WAVEFORMATEXTENSIBLE channel mask to/from tag, robust handling of "odd" sample resolutions (i.e. not multiple of 8 bits) for AIFF and WAVE
parent b73fff6e
......@@ -54,6 +54,7 @@ typedef struct {
FLAC__bool is_aiff_out;
FLAC__bool is_wave_out;
FLAC__bool continue_through_decode_errors;
FLAC__bool channel_map_none;
struct {
replaygain_synthesis_spec_t spec;
......@@ -87,6 +88,7 @@ typedef struct {
unsigned bps;
unsigned channels;
unsigned sample_rate;
FLAC__uint32 channel_mask;
union {
FLAC__StreamDecoder *flac;
......@@ -105,7 +107,7 @@ static FLAC__bool is_big_endian_host_;
/*
* local routines
*/
static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool is_aiff_out, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, const char *infilename, const char *outfilename);
static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool is_aiff_out, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, const char *infilename, const char *outfilename);
static void DecoderSession_destroy(DecoderSession *d, FLAC__bool error_occurred);
static FLAC__bool DecoderSession_init_decoder(DecoderSession *d, decode_options_t decode_options, const char *infilename);
static FLAC__bool DecoderSession_process(DecoderSession *d);
......@@ -145,6 +147,7 @@ int flac__decode_aiff(const char *infilename, const char *outfilename, FLAC__boo
/*is_aiff_out=*/true,
/*is_wave_out=*/false,
options.common.continue_through_decode_errors,
options.common.channel_map_none,
options.common.replaygain_synthesis_spec,
analysis_mode,
aopts,
......@@ -181,6 +184,7 @@ int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool
/*is_aiff_out=*/false,
/*is_wave_out=*/true,
options.common.continue_through_decode_errors,
options.common.channel_map_none,
options.common.replaygain_synthesis_spec,
analysis_mode,
aopts,
......@@ -220,6 +224,7 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool
/*is_aiff_out=*/false,
/*is_wave_out=*/false,
options.common.continue_through_decode_errors,
options.common.channel_map_none,
options.common.replaygain_synthesis_spec,
analysis_mode,
aopts,
......@@ -241,7 +246,7 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool
return DecoderSession_finish_ok(&decoder_session);
}
FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool is_aiff_out, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, const char *infilename, const char *outfilename)
FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool is_aiff_out, FLAC__bool is_wave_out, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, const char *infilename, const char *outfilename)
{
#ifdef FLAC__HAS_OGG
d->is_ogg = is_ogg;
......@@ -252,6 +257,7 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__
d->is_aiff_out = is_aiff_out;
d->is_wave_out = is_wave_out;
d->continue_through_decode_errors = continue_through_decode_errors;
d->channel_map_none = channel_map_none;
d->replaygain.spec = replaygain_synthesis_spec;
d->replaygain.apply = false;
d->replaygain.scale = 0.0;
......@@ -279,6 +285,7 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__
d->bps = 0;
d->channels = 0;
d->sample_rate = 0;
d->channel_mask = 0;
d->decoder.flac = 0;
#ifdef FLAC__HAS_OGG
......@@ -409,6 +416,36 @@ FLAC__bool DecoderSession_process(DecoderSession *d)
if(d->abort_flag)
return false;
/* set channel mapping */
if(!d->channel_map_none) {
/* currently FLAC order matches SMPTE/WAVEFORMATEXTENSIBLE order, so no reordering is necessary; see encode.c */
/* only the channel mask must be set if it was not already picked up from the WAVEFORMATEXTENSIBLE_CHANNEL_MASK tag */
if(d->channels == 1) {
if(d->channel_mask == 0)
d->channel_mask = 0x0001;
}
else if(d->channels == 2) {
if(d->channel_mask == 0)
d->channel_mask = 0x0003;
}
else if(d->channels == 3) {
if(d->channel_mask == 0)
d->channel_mask = 0x0007;
}
else if(d->channels == 4) {
if(d->channel_mask == 0)
d->channel_mask = 0x0033;
}
else if(d->channels == 5) {
if(d->channel_mask == 0)
d->channel_mask = 0x0607;
}
else if(d->channels == 6) {
if(d->channel_mask == 0)
d->channel_mask = 0x060f;
}
}
/* write the WAVE/AIFF headers if necessary */
if(!d->analysis_mode && !d->test_only && (d->is_wave_out || d->is_aiff_out)) {
if(!write_iff_headers(d->fout, d, d->total_samples)) {
......@@ -610,6 +647,7 @@ FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec,
FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uint64 samples)
{
const char *fmt_desc = decoder_session->is_wave_out? "WAVE" : "AIFF";
const FLAC__bool is_waveformatextensible = decoder_session->is_wave_out && (decoder_session->channel_mask == 2 || decoder_session->channel_mask > 3 || decoder_session->bps%8 || decoder_session->channels > 2);
FLAC__uint64 data_size = samples * decoder_session->channels * ((decoder_session->bps+7)/8);
const FLAC__uint32 aligned_data_size = (FLAC__uint32)((data_size+1) & (~1U)); /* we'll check for overflow later */
if(samples == 0) {
......@@ -630,16 +668,16 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
if(flac__utils_fwrite("RIFF", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, aligned_data_size+36)) /* filesize-8 */
if(!write_little_endian_uint32(f, aligned_data_size+(is_waveformatextensible?60:36))) /* filesize-8 */
return false;
if(flac__utils_fwrite("WAVEfmt ", 1, 8, f) != 8)
return false;
if(flac__utils_fwrite("\020\000\000\000", 1, 4, f) != 4) /* chunk size = 16 */
if(!write_little_endian_uint32(f, is_waveformatextensible? 40 : 16)) /* chunk size */
return false;
if(flac__utils_fwrite("\001\000", 1, 2, f) != 2) /* compression code == 1 */
if(!write_little_endian_uint16(f, (FLAC__uint16)(is_waveformatextensible? 65534 : 1))) /* compression code */
return false;
if(!write_little_endian_uint16(f, (FLAC__uint16)(decoder_session->channels)))
......@@ -654,9 +692,24 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
if(!write_little_endian_uint16(f, (FLAC__uint16)(decoder_session->channels * ((decoder_session->bps+7) / 8)))) /* block align */
return false;
if(!write_little_endian_uint16(f, (FLAC__uint16)(decoder_session->bps))) /* bits per sample */
if(!write_little_endian_uint16(f, (FLAC__uint16)(((decoder_session->bps+7)/8)*8))) /* bits per sample */
return false;
if(is_waveformatextensible) {
if(!write_little_endian_uint16(f, (FLAC__uint16)22)) /* cbSize */
return false;
if(!write_little_endian_uint16(f, (FLAC__uint16)decoder_session->bps)) /* validBitsPerSample */
return false;
if(!write_little_endian_uint32(f, decoder_session->channel_mask))
return false;
/* GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} */
if(flac__utils_fwrite("\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, f) != 16)
return false;
}
if(flac__utils_fwrite("data", 1, 4, f) != 4)
return false;
......@@ -801,6 +854,7 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
DecoderSession *decoder_session = (DecoderSession*)client_data;
FILE *fout = decoder_session->fout;
const unsigned bps = frame->header.bits_per_sample, channels = frame->header.channels;
const unsigned shift = (decoder_session->is_wave_out && (bps%8)? 8-(bps%8): 0);
FLAC__bool is_big_endian = (decoder_session->is_aiff_out? true : (decoder_session->is_wave_out? false : decoder_session->is_big_endian));
FLAC__bool is_unsigned_samples = (decoder_session->is_aiff_out? false : (decoder_session->is_wave_out? bps<=8 : decoder_session->is_unsigned_samples));
unsigned wide_samples = frame->header.blocksize, wide_sample, sample, channel, byte;
......@@ -916,7 +970,12 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
flac__analyze_frame(frame, decoder_session->frame_counter-1, decoder_session->aopts, fout);
}
else if(!decoder_session->test_only) {
if (decoder_session->replaygain.apply) {
if(shift && !decoder_session->replaygain.apply) {
for(wide_sample = 0; wide_sample < wide_samples; wide_sample++)
for(channel = 0; channel < channels; channel++)
/*@@@@@@bad un-const:fix@@@@@@*/((FLAC__int32**)buffer)[channel][wide_sample] <<= shift;
}
if(decoder_session->replaygain.apply) {
bytes_to_write = FLAC__replaygain_synthesis__apply_gain(
u8buffer,
!is_big_endian,
......@@ -925,7 +984,7 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
wide_samples,
channels,
bps, /* source_bps */
bps, /* target_bps */
bps+shift, /* target_bps */
decoder_session->replaygain.scale,
decoder_session->replaygain.spec.limiter == RGSS_LIMIT__HARD, /* hard_limit */
decoder_session->replaygain.spec.noise_shaping != NOISE_SHAPING_NONE, /* do_dithering */
......@@ -1014,6 +1073,9 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
}
else {
FLAC__ASSERT(0);
/* double protection */
decoder_session->abort_flag = true;
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
}
}
......@@ -1117,6 +1179,7 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet
flac__utils_printf(stderr, 1, "%s: WARNING: applying ReplayGain is not lossless\n", decoder_session->inbasefilename);
}
}
(void)flac__utils_get_channel_mask_tag(metadata, &decoder_session->channel_mask);
}
}
......
......@@ -47,6 +47,7 @@ typedef struct {
utils__SkipUntilSpecification until_specification;
FLAC__bool has_cue_specification;
utils__CueSpecification cue_specification;
FLAC__bool channel_map_none; /* --channel-map=none specified, eventually will expand to take actual channel map */
} decode_options_t;
/* used for AIFF also */
......
......@@ -136,12 +136,12 @@ static FLAC__bool EncoderSession_construct(EncoderSession *e, FLAC__bool use_ogg
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, FLACDecoderData *flac_decoder_data);
static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options, FLAC__uint32 channel_mask, unsigned channels, unsigned bps, unsigned sample_rate, FLACDecoderData *flac_decoder_data);
static FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const buffer[], unsigned samples);
static FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e);
static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, unsigned sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input);
static FLAC__bool verify_metadata(const EncoderSession *e, FLAC__StreamMetadata **metadata, unsigned num_metadata);
static FLAC__bool format_input(FLAC__int32 *dest[], unsigned wide_samples, FLAC__bool is_big_endian, FLAC__bool is_unsigned_samples, unsigned channels, unsigned bps, unsigned shift);
static FLAC__bool format_input(FLAC__int32 *dest[], unsigned wide_samples, FLAC__bool is_big_endian, FLAC__bool is_unsigned_samples, unsigned channels, unsigned bps, unsigned shift, size_t *channel_map);
static void encoder_progress_callback(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data);
static FLAC__StreamDecoderReadStatus flac_decoder_read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
static FLAC__StreamDecoderSeekStatus flac_decoder_seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
......@@ -173,7 +173,8 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
EncoderSession encoder_session;
FLAC__uint16 x;
FLAC__uint32 xx;
unsigned int channels= 0U, bps= 0U, sample_rate= 0U, sample_frames= 0U;
unsigned int channels= 0U, bps= 0U, shift= 0U, sample_rate= 0U, sample_frames= 0U;
size_t channel_map[FLAC__MAX_CHANNELS];
FLAC__bool got_comm_chunk= false, got_ssnd_chunk= false;
int info_align_carry= -1, info_align_zero= -1;
FLAC__bool is_big_endian_pcm = true;
......@@ -198,6 +199,13 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
)
return 1;
/* initialize default channel map that preserves channel order */
{
size_t i;
for(i = 0; i < sizeof(channel_map)/sizeof(channel_map[0]); i++)
channel_map[i] = i;
}
/* lookahead[] already has "FORMxxxxAIFF", do sub-chunks */
while(1) {
......@@ -235,6 +243,10 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u\n", encoder_session.inbasefilename, (unsigned int)x);
return EncoderSession_finish_error(&encoder_session);
}
else if(x>2U && !options.common.channel_map_none) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u for AIFF\n", encoder_session.inbasefilename, (unsigned int)x);
return EncoderSession_finish_error(&encoder_session);
}
else if(options.common.sector_align && x!=2U) {
flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned int)x);
return EncoderSession_finish_error(&encoder_session);
......@@ -249,15 +261,17 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
/* bits per sample */
if(!read_big_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
else if(x!=8U && x!=16U && x!=24U) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits per sample %u\n", encoder_session.inbasefilename, (unsigned int)x);
else if(x<4U || x>24U) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported bits-per-sample %u\n", encoder_session.inbasefilename, (unsigned int)x);
return EncoderSession_finish_error(&encoder_session);
}
else if(options.common.sector_align && x!=16U) {
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);
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);
return EncoderSession_finish_error(&encoder_session);
}
bps= x;
shift= (bps%8)? 8-(bps%8) : 0; /* SSND data is always byte-aligned, left-justified but format_input() will double-check */
bps+= shift;
/* sample rate */
if(!read_sane_extended(infile, &xx, false, encoder_session.inbasefilename))
......@@ -286,6 +300,35 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
}
}
/* set channel mapping */
/* FLAC order follows SMPTE and WAVEFORMATEXTENSIBLE but with fewer channels, which are: */
/* front left, front right, center, LFE, back left, back right, surround left, surround right */
/* specs say the channel ordering is:
* 1 2 3 4 5 6
* ___________________________________________________
* 2 stereo l r
* 3 l r c
* 4 l c r S
* quad (ambiguous with 4ch) Fl Fr Bl Br
* 5 Fl Fr Fc Sl Sr
* 6 l lc c r rc S
* l:left r:right c:center Fl:front-left Fr:front-right Bl:back-left Br:back-right Lc:left-center Rc:right-center S:surround
* so we only have unambiguous mappings for 2, 3, and 5 channels
*/
if(
options.common.channel_map_none ||
channels == 1 || /* 1 channel: (mono) */
channels == 2 || /* 2 channels: left, right */
channels == 3 || /* 3 channels: left, right, center */
channels == 5 /* 5 channels: front left, front right, center, surround left, surround right */
) {
/* keep default channel order */
}
else {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number channels %u for AIFF\n", encoder_session.inbasefilename, channels);
return EncoderSession_finish_error(&encoder_session);
}
/* skip any extra data in the COMM chunk */
if(!fskip_ahead(infile, skip)) {
flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping extra COMM data\n", encoder_session.inbasefilename);
......@@ -387,7 +430,7 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
/* +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;
if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps, sample_rate, /*flac_decoder_data=*/0))
if(!EncoderSession_init_encoder(&encoder_session, options.common, /*channel_mask=*/0, channels, bps-shift, sample_rate, /*flac_decoder_data=*/0))
return EncoderSession_finish_error(&encoder_session);
/* first do any samples in the reservoir */
......@@ -430,7 +473,7 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
}
else {
unsigned int frames= bytes_read/bytes_per_frame;
if(!format_input(input_, frames, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps, /*shift=*/0))
if(!format_input(input_, frames, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps, shift, channel_map))
return EncoderSession_finish_error(&encoder_session);
if(!EncoderSession_process(&encoder_session, (const FLAC__int32 *const *)input_, frames)) {
......@@ -483,7 +526,7 @@ int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, con
}
else {
info_align_carry= *options.common.align_reservoir_samples;
if(!format_input(options.common.align_reservoir, *options.common.align_reservoir_samples, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps, /*shift=*/0))
if(!format_input(options.common.align_reservoir, *options.common.align_reservoir_samples, is_big_endian_pcm, /*is_unsigned_samples=*/false, channels, bps, shift, channel_map))
return EncoderSession_finish_error(&encoder_session);
}
}
......@@ -541,6 +584,7 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
FLAC__bool is_unsigned_samples = false;
unsigned channels = 0, bps = 0, sample_rate = 0, shift = 0;
size_t bytes_per_wide_sample, bytes_read;
size_t channel_map[FLAC__MAX_CHANNELS];
FLAC__uint16 x, format; /* format is the wFormatTag word from the 'fmt ' chunk */
FLAC__uint32 xx, channel_mask = 0;
FLAC__bool got_fmt_chunk = false, got_data_chunk = false;
......@@ -567,6 +611,13 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
)
return 1;
/* initialize default channel map that preserves channel order */
{
size_t i;
for(i = 0; i < sizeof(channel_map)/sizeof(channel_map[0]); i++)
channel_map[i] = i;
}
/*
* lookahead[] already has "RIFFxxxxWAVE", do sub-chunks
*/
......@@ -603,8 +654,8 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
*
* Block align for WAVE_FORMAT_PCM or WAVE_FORMAT_EXTENSIBLE is also supposed to be channels*bps/8
*
* If the channel mask has less set bits that # of channels, the extra MSBs are ignored.
* If the channel mask has more set bits that # of channels, the extra channels are unassigned to any speaker.
* If the channel mask has more set bits than # of channels, the extra MSBs are ignored.
* If the channel mask has less set bits than # of channels, the extra channels are unassigned to any speaker.
*
* Data is supposed to be unsigned for bps <= 8 else signed.
*/
......@@ -612,12 +663,12 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
/* fmt sub-chunk size */
if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
if(xx < 16) {
flac__utils_printf(stderr, 1, "%s: ERROR: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, (unsigned)xx);
data_bytes = xx;
if(data_bytes < 16) {
flac__utils_printf(stderr, 1, "%s: ERROR: found non-standard 'fmt ' sub-chunk which has length = %u\n", encoder_session.inbasefilename, data_bytes);
return EncoderSession_finish_error(&encoder_session);
}
data_bytes = xx;
/* compression code */
/* format code */
if(!read_little_endian_uint16(infile, &format, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
if(format != 1 /*WAVE_FORMAT_PCM*/ && format != 65534 /*WAVE_FORMAT_EXTENSIBLE*/) {
......@@ -627,57 +678,55 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
/* number of channels */
if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
if(x == 0 || x > FLAC__MAX_CHANNELS) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number of channels %u\n", encoder_session.inbasefilename, (unsigned)x);
channels = (unsigned)x;
if(channels == 0 || channels > FLAC__MAX_CHANNELS) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported number of channels %u\n", encoder_session.inbasefilename, channels);
return EncoderSession_finish_error(&encoder_session);
}
else if(options.common.sector_align && x != 2) {
flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, (unsigned)x);
else if(options.common.sector_align && channels != 2) {
flac__utils_printf(stderr, 1, "%s: ERROR: file has %u channels, must be 2 for --sector-align\n", encoder_session.inbasefilename, channels);
return EncoderSession_finish_error(&encoder_session);
}
channels = x;
/* sample rate */
if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
if(!FLAC__format_sample_rate_is_valid(xx)) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, (unsigned)xx);
sample_rate = xx;
if(!FLAC__format_sample_rate_is_valid(sample_rate)) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported sample rate %u\n", encoder_session.inbasefilename, sample_rate);
return EncoderSession_finish_error(&encoder_session);
}
else if(options.common.sector_align && xx != 44100) {
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);
else if(options.common.sector_align && sample_rate != 44100) {
flac__utils_printf(stderr, 1, "%s: ERROR: file's sample rate is %u, must be 44100 for --sector-align\n", encoder_session.inbasefilename, sample_rate);
return EncoderSession_finish_error(&encoder_session);
}
sample_rate = xx;
/* avg bytes per second (ignored) */
if(!read_little_endian_uint32(infile, &xx, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
/* block align */
if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
block_align = x;
block_align = (unsigned)x;
/* bits per sample */
if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
bps = x;
bps = (unsigned)x;
is_unsigned_samples = (bps <= 8);
if(format == 1) {
if(bps != 8 && bps != 16) {
#if 0 /* reinstate if we need to get stricter on the input */
if(bps == 24 || bps == 32) {
#endif
/* let these slide with a warning since they're unambiguous */
flac__utils_printf(stderr, 1, "%s: WARNING: legacy WAVE file has format type %u but bits-per-sample=%u\n", encoder_session.inbasefilename, (unsigned)format, bps);
#if 0 /* reinstate if we need to get stricter on the input */
}
else {
/* @@@ we could add an option to specify left- or right-justified blocks so we knew how to set 'shift' */
flac__utils_printf(stderr, 1, "%s: ERROR: legacy WAVE file has format type %u but bits-per-sample=%u\n", encoder_session.inbasefilename, (unsigned)format, bps);
return EncoderSession_finish_error(&encoder_session);
}
#endif
}
#if 0 /* @@@ reinstate once we can get an answer about whether the samples are left- or right-justified */
if((bps+7)/8 * channels == block_align) {
if(bps % 8) {
/* assume legacy file is block aligned with some LSBs zero; this is checked for in format_input() */
/* assume legacy file is byte aligned with some LSBs zero; this is double-checked in format_input() */
flac__utils_printf(stderr, 1, "%s: WARNING: legacy WAVE file (format type %d) has block alignment=%u, bits-per-sample=%u, channels=%u\n", encoder_session.inbasefilename, (unsigned)format, block_align, bps, channels);
shift = 8 - (bps % 8);
bps += shift;
......@@ -689,26 +738,33 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
flac__utils_printf(stderr, 1, "%s: ERROR: illegal WAVE file (format type %d) has block alignment=%u, bits-per-sample=%u, channels=%u\n", encoder_session.inbasefilename, (unsigned)format, block_align, bps, channels);
return EncoderSession_finish_error(&encoder_session);
}
#else
shift = 0;
#endif
if(channels > 2 && !options.common.channel_map_none) {
flac__utils_printf(stderr, 1, "%s: ERROR: WAVE has >2 channels but is not WAVE_FORMAT_EXTENSIBLE; cannot assign channels\n", encoder_session.inbasefilename);
return EncoderSession_finish_error(&encoder_session);
}
FLAC__ASSERT(data_bytes >= 16);
data_bytes -= 16;
}
else {
if(data_bytes < 40) {
flac__utils_printf(stderr, 1, "%s: ERROR: invalid WAVEFORMATEXTENSIBLE sub-chunk with size %u\n", encoder_session.inbasefilename, data_bytes);
flac__utils_printf(stderr, 1, "%s: ERROR: invalid WAVEFORMATEXTENSIBLE chunk with size %u\n", encoder_session.inbasefilename, data_bytes);
return EncoderSession_finish_error(&encoder_session);
}
/* cbSize */
if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
if(x < 22) {
flac__utils_printf(stderr, 1, "%s: ERROR: invalid WAVEFORMATEXTENSIBLE sub-chunk with cbSize %u\n", encoder_session.inbasefilename, (unsigned)x);
flac__utils_printf(stderr, 1, "%s: ERROR: invalid WAVEFORMATEXTENSIBLE chunk with cbSize %u\n", encoder_session.inbasefilename, (unsigned)x);
return EncoderSession_finish_error(&encoder_session);
}
/* valid bps */
if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
if((unsigned)x > bps) {
flac__utils_printf(stderr, 1, "%s: ERROR: invalid WAVEFORMATEXTENSIBLE sub-chunk with wValidBitsPerSample (%u) > wBitsPerSample (%u)\n", encoder_session.inbasefilename, (unsigned)x, bps);
flac__utils_printf(stderr, 1, "%s: ERROR: invalid WAVEFORMATEXTENSIBLE chunk with wValidBitsPerSample (%u) > wBitsPerSample (%u)\n", encoder_session.inbasefilename, (unsigned)x, bps);
return EncoderSession_finish_error(&encoder_session);
}
shift = bps - (unsigned)x;
......@@ -722,21 +778,85 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
else if(channels == 2)
channel_mask = 0x0003;
}
if(channel_mask != 0x0001 && channel_mask != 0x0003 && channel_mask != 0x003f && channel_mask != 0x060f) {
flac__utils_printf(stderr, 1, "%s: ERROR: WAVEFORMATEXTENSIBLE sub-chunk with unsupported channel mask=0x%04X and #channels=%u\n", encoder_session.inbasefilename, (unsigned)channel_mask, channels);
return EncoderSession_finish_error(&encoder_session);
/* set channel mapping */
/* FLAC order follows SMPTE and WAVEFORMATEXTENSIBLE but with fewer channels, which are: */
/* front left, front right, center, LFE, back left, back right, surround left, surround right */
/* the default mapping is sufficient for 1-6 channels and 7-8 are currently unspecified anyway */
#if 0
/* @@@ example for dolby/vorbis order, for reference later in case it becomes important */
if(
options.common.channel_map_none ||
channel_mask == 0x0001 || /* 1 channel: (mono) */
channel_mask == 0x0003 || /* 2 channels: front left, front right */
channel_mask == 0x0033 || /* 4 channels: front left, front right, back left, back right */
channel_mask == 0x0603 /* 4 channels: front left, front right, side left, side right */
) {
/* keep default channel order */
}
else if(
channel_mask == 0x0007 || /* 3 channels: front left, front right, front center */
channel_mask == 0x0037 || /* 5 channels: front left, front right, front center, back left, back right */
channel_mask == 0x0607 /* 5 channels: front left, front right, front center, side left, side right */
) {
/* to dolby order: front left, center, front right [, surround left, surround right ] */
channel_map[1] = 2;
channel_map[2] = 1;
}
else if(
channel_mask == 0x003f || /* 6 channels: front left, front right, front center, LFE, back left, back right */
channel_mask == 0x060f /* 6 channels: front left, front right, front center, LFE, side left, side right */
) {
/* to dolby order: front left, center, front right, surround left, surround right, LFE */
channel_map[1] = 2;
channel_map[2] = 1;
channel_map[3] = 5;
channel_map[4] = 3;
channel_map[5] = 4;
}
#else
if(
options.common.channel_map_none ||
channel_mask == 0x0001 || /* 1 channel: (mono) */
channel_mask == 0x0003 || /* 2 channels: front left, front right */
channel_mask == 0x0007 || /* 3 channels: front left, front right, front center */
channel_mask == 0x0033 || /* 4 channels: front left, front right, back left, back right */
channel_mask == 0x0603 || /* 4 channels: front left, front right, side left, side right */
channel_mask == 0x0037 || /* 5 channels: front left, front right, front center, back left, back right */
channel_mask == 0x0607 || /* 5 channels: front left, front right, front center, side left, side right */
channel_mask == 0x003f || /* 6 channels: front left, front right, front center, LFE, back left, back right */
channel_mask == 0x060f /* 6 channels: front left, front right, front center, LFE, side left, side right */
) {
/* keep default channel order */
}
if(count_channel_mask_bits(channel_mask) < channels) {
flac__utils_printf(stderr, 1, "%s: ERROR: WAVEFORMATEXTENSIBLE sub-chunk: channel mask 0x%04X has unassigned channels (#channels=%u)\n", encoder_session.inbasefilename, channel_mask, channels);
#endif
else {
flac__utils_printf(stderr, 1, "%s: ERROR: WAVEFORMATEXTENSIBLE chunk with unsupported channel mask=0x%04X\n", encoder_session.inbasefilename, (unsigned)channel_mask);
return EncoderSession_finish_error(&encoder_session);
}
else if(count_channel_mask_bits(channel_mask) > channels)
channel_mask = limit_channel_mask(channel_mask, channels);
if(!options.common.channel_map_none) {
if(count_channel_mask_bits(channel_mask) < channels) {
flac__utils_printf(stderr, 1, "%s: ERROR: WAVEFORMATEXTENSIBLE chunk: channel mask 0x%04X has unassigned channels (#channels=%u)\n", encoder_session.inbasefilename, (unsigned)channel_mask, channels);
return EncoderSession_finish_error(&encoder_session);
}
#if 0
/* supporting this is too difficult with channel mapping; e.g. what if mask is 0x003f but #channels=4?
* there would be holes in the order that would have to be filled in, or the mask would have to be
* limited and the logic above rerun to see if it still fits into the FLAC mapping.
*/
else if(count_channel_mask_bits(channel_mask) > channels)
channel_mask = limit_channel_mask(channel_mask, channels);
#else
else if(count_channel_mask_bits(channel_mask) > channels) {
flac__utils_printf(stderr, 1, "%s: ERROR: WAVEFORMATEXTENSIBLE chunk: channel mask 0x%04X has extra bits for non-existant channels (#channels=%u)\n", encoder_session.inbasefilename, (unsigned)channel_mask, channels);
return EncoderSession_finish_error(&encoder_session);
}
#endif
}
/* first part of GUID */
if(!read_little_endian_uint16(infile, &x, false, encoder_session.inbasefilename))
return EncoderSession_finish_error(&encoder_session);
if(x != 1) {
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported WAVEFORMATEXTENSIBLE sub-chunk with non-PCM format %u\n", encoder_session.inbasefilename, (unsigned)x);
flac__utils_printf(stderr, 1, "%s: ERROR: unsupported WAVEFORMATEXTENSIBLE chunk with non-PCM format %u\n", encoder_session.inbasefilename, (unsigned)x);
return EncoderSession_finish_error(&encoder_session);
}
data_bytes -= 26;
......@@ -747,7 +867,7 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
return EncoderSession_finish_error(&encoder_session);
}
else if(options.common.sector_align && bps-shift != 16) {
flac__utils_printf(stderr, 1, "%s: ERROR: file has %u bits per sample, must be 16 for --sector-align\n", encoder_session.inbasefilename, bps-shift);
flac__utils_printf(stderr, 1, "%s: ERROR: file has %u bits-per-sample, must be 16 for --sector-align\n", encoder_session.inbasefilename, bps-shift);
return EncoderSession_finish_error(&encoder_session);
}
......@@ -821,7 +941,7 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
/* +44 for the size of the WAV 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_wide_sample + 44;
if(!EncoderSession_init_encoder(&encoder_session, options.common, channels, bps-shift, sample_rate, /*flac_decoder_data=*/0))
if(!EncoderSession_init_encoder(&encoder_session, options.common, channel_mask, channels, bps-shift, sample_rate, /*flac_decoder_data=*/0))
return EncoderSession_finish_error(&encoder_session);
/*
......@@ -869,7 +989,7 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
}
else {
unsigned wide_samples = bytes_read / bytes_per_wide_sample;
if(!format_input(input_, wide_samples, /*is_big_endian=*/false, is_unsigned_samples, channels, bps, shift))
if(!format_input(input_, wide_samples, /*is_big_endian=*/false, is_unsigned_samples, channels, bps, shift, channel_map))
return EncoderSession_finish_error(&encoder_session);
if(!EncoderSession_process(&encoder_session, (const FLAC__int32 * const *)input_, wide_samples)) {
......@@ -921,7 +1041,7 @@ int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, con
}
else {