Josh Coalson
2008-09-09 07:49:19 +00:00
parent 7617cacb28
commit d7f5344a64
16 changed files with 730 additions and 185 deletions

View File

@@ -116,7 +116,7 @@ static int DecoderSession_finish_ok(DecoderSession *d);
static int DecoderSession_finish_error(DecoderSession *d);
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 write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uint64 samples);
static FLAC__bool write_riff_wave_fmt_chunk(FILE *f, FLAC__bool is_waveformatextensible, unsigned bps, unsigned channels, unsigned sample_rate, FLAC__uint32 channel_mask);
static FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, unsigned bps, unsigned channels, unsigned sample_rate, FLAC__uint32 channel_mask);
static FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, unsigned bps, unsigned channels, unsigned sample_rate);
static FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val);
static FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val);
@@ -142,6 +142,7 @@ int flac__decode_file(const char *infilename, const char *outfilename, FLAC__boo
FLAC__ASSERT(
options.format == FORMAT_WAVE ||
options.format == FORMAT_WAVE64 ||
options.format == FORMAT_RF64 ||
options.format == FORMAT_AIFF ||
options.format == FORMAT_AIFF_C ||
@@ -402,13 +403,28 @@ FLAC__bool DecoderSession_process(DecoderSession *d)
return false;
}
if(!d->analysis_mode && !d->test_only && d->format != FORMAT_RAW && ((d->total_samples * d->channels * ((d->bps+7)/8)) & 1)) {
if(flac__utils_fwrite("\000", 1, 1, d->fout) != 1) {
print_error_with_state(d, d->format == FORMAT_WAVE || d->format == FORMAT_RF64?
"ERROR writing pad byte to WAVE data chunk" :
"ERROR writing pad byte to AIFF SSND chunk"
);
return false;
/* write padding bytes for alignment if necessary */
if(!d->analysis_mode && !d->test_only && d->format != FORMAT_RAW) {
const FLAC__uint64 data_size = d->total_samples * d->channels * ((d->bps+7)/8);
unsigned padding;
if(d->format != FORMAT_WAVE64) {
padding = (unsigned)(data_size & 1);
}
else {
/* 8-byte alignment for Wave64 */
padding = (8 - (unsigned)(data_size & 7)) & 7;
}
for( ; padding > 0; --padding) {
if(flac__utils_fwrite("\000", 1, 1, d->fout) != 1) {
print_error_with_state(
d,
d->format == FORMAT_WAVE? "ERROR writing pad byte to WAVE data chunk" :
d->format == FORMAT_WAVE64? "ERROR writing pad bytes to WAVE64 data chunk" :
d->format == FORMAT_RF64? "ERROR writing pad byte to RF64 data chunk" :
"ERROR writing pad byte to AIFF SSND chunk"
);
return false;
}
}
}
@@ -518,18 +534,37 @@ FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec,
FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uint64 samples)
{
const FileFormat format = decoder_session->format;
const char *fmt_desc = format==FORMAT_WAVE? "WAVE" : format==FORMAT_RF64? "RF64" : "AIFF";
const FLAC__bool is_wavish = format == FORMAT_WAVE || format == FORMAT_RF64;
const FLAC__bool is_waveformatextensible = is_wavish && (decoder_session->channel_mask == 2 || decoder_session->channel_mask > 3 || decoder_session->bps%8 || decoder_session->channels > 2);
const char *fmt_desc =
format==FORMAT_WAVE? "WAVE" :
format==FORMAT_WAVE64? "Wave64" :
format==FORMAT_RF64? "RF64" :
"AIFF";
const FLAC__bool is_waveformatextensible =
(format == FORMAT_WAVE || format == FORMAT_WAVE64 || format == FORMAT_RF64) &&
(
decoder_session->channel_mask == 2 ||
decoder_session->channel_mask > 3 ||
decoder_session->bps%8 ||
decoder_session->channels > 2
);
const FLAC__uint64 data_size = samples * decoder_session->channels * ((decoder_session->bps+7)/8);
const FLAC__uint64 aligned_data_size = data_size & 1? (data_size+1) : data_size;
const FLAC__uint64 aligned_data_size =
format == FORMAT_WAVE64?
(data_size+7) & (~(FLAC__uint64)7) :
(data_size+1) & (~(FLAC__uint64)1);
FLAC__uint64 iff_size;
unsigned foreign_metadata_size = 0; /* size of all non-audio non-fmt/COMM foreign metadata chunks */
foreign_metadata_t *fm = decoder_session->foreign_metadata;
size_t i;
FLAC__ASSERT(flac__utils_format_is_iff(format));
FLAC__ASSERT(
format == FORMAT_WAVE ||
format == FORMAT_WAVE64 ||
format == FORMAT_RF64 ||
format == FORMAT_AIFF ||
format == FORMAT_AIFF_C
);
if(samples == 0) {
if(f == stdout) {
@@ -548,7 +583,7 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
FLAC__ASSERT(fm->format_block);
FLAC__ASSERT(fm->audio_block);
FLAC__ASSERT(fm->format_block < fm->audio_block);
/* calc foreign metadata size; for RIFF/AIFF we always skip the first chunk, ds64 chunk, format chunk, and sound chunk since we write our own */
/* calc foreign metadata size; we always skip the first chunk, ds64 chunk, format chunk, and sound chunk since we write our own */
for(i = format==FORMAT_RF64?2:1; i < fm->num_blocks; i++) {
if(i != fm->format_block && i != fm->audio_block)
foreign_metadata_size += fm->blocks[i].size;
@@ -557,36 +592,61 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
if(samples == 0)
iff_size = 0;
else if(is_wavish)
iff_size = (is_waveformatextensible?60:36) + (format==FORMAT_RF64?36:0) + foreign_metadata_size + aligned_data_size;
else
else if(format == FORMAT_WAVE || format == FORMAT_RF64)
/* 4 for WAVE form bytes */
/* +{36,0} for ds64 chunk */
/* +8+{40,16} for fmt chunk header and body */
/* +8 for data chunk header */
iff_size = 4 + (format==FORMAT_RF64?36:0) + 8+(is_waveformatextensible?40:16) + 8 + foreign_metadata_size + aligned_data_size;
else if(format == FORMAT_WAVE64)
/* 16+8 for RIFF GUID and size field */
/* +16 for WAVE GUID */
/* +16+8+{40,16} for fmt chunk header (GUID and size field) and body */
/* +16+8 for data chunk header (GUID and size field) */
iff_size = 16+8 + 16 + 16+8+(is_waveformatextensible?40:16) + 16+8 + foreign_metadata_size + aligned_data_size;
else /* AIFF */
/* @@@@@@ can ssnd_offset_size be odd and hence screw up our alignment logic? */
iff_size = 46 + foreign_metadata_size + aligned_data_size + (fm? fm->ssnd_offset_size : 0);
if(format != FORMAT_RF64 && iff_size >= 0xFFFFFFF4) {
if(format != FORMAT_WAVE64 && format != FORMAT_RF64 && iff_size >= 0xFFFFFFF4) {
flac__utils_printf(stderr, 1, "%s: ERROR: stream is too big to fit in a single %s file\n", decoder_session->inbasefilename, fmt_desc);
return false;
}
if(is_wavish) {
if(format == FORMAT_RF64) {
if(flac__utils_fwrite("RF64", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, 0xffffffff))
return false;
}
else {
if(flac__utils_fwrite("RIFF", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, (FLAC__uint32)iff_size)) /* filesize-8 */
if(format == FORMAT_WAVE || format == FORMAT_WAVE64 || format == FORMAT_RF64) {
/* RIFF header */
switch(format) {
case FORMAT_WAVE:
if(flac__utils_fwrite("RIFF", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, (FLAC__uint32)iff_size)) /* filesize-8 */
return false;
if(flac__utils_fwrite("WAVE", 1, 4, f) != 4)
return false;
break;
case FORMAT_WAVE64:
/* RIFF GUID 66666972-912E-11CF-A5D6-28DB04C10000 */
if(flac__utils_fwrite("\x72\x69\x66\x66\x2E\x91\xCF\x11\xD6\xA5\x28\xDB\x04\xC1\x00\x00", 1, 16, f) != 16)
return false;
if(!write_little_endian_uint64(f, iff_size))
return false;
/* WAVE GUID 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
if(flac__utils_fwrite("\x77\x61\x76\x65\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 1, 16, f) != 16)
return false;
break;
case FORMAT_RF64:
if(flac__utils_fwrite("RF64", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, 0xffffffff))
return false;
if(flac__utils_fwrite("WAVE", 1, 4, f) != 4)
return false;
break;
default:
return false;
}
if(flac__utils_fwrite("WAVE", 1, 4, f) != 4)
return false;
/* ds64 chunk for RF64 */
if(format == FORMAT_RF64) {
if(flac__utils_fwrite("ds64", 1, 4, f) != 4)
return false;
@@ -619,7 +679,22 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
}
}
if(!write_riff_wave_fmt_chunk(f, is_waveformatextensible, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, decoder_session->channel_mask))
if(format != FORMAT_WAVE64) {
if(flac__utils_fwrite("fmt ", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, is_waveformatextensible? 40 : 16)) /* chunk size */
return false;
}
else { /* Wave64 */
/* fmt GUID 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
if(flac__utils_fwrite("\x66\x6D\x74\x20\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 1, 16, f) != 16)
return false;
/* chunk size (+16+8 for GUID and size fields) */
if(!write_little_endian_uint64(f, 16+8+(is_waveformatextensible?40:16)))
return false;
}
if(!write_riff_wave_fmt_chunk_body(f, is_waveformatextensible, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, decoder_session->channel_mask))
return false;
decoder_session->fm_offset2 = ftello(f);
@@ -634,11 +709,20 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
}
}
if(flac__utils_fwrite("data", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, format==FORMAT_RF64? 0xffffffff : (FLAC__uint32)data_size))
return false;
if(format != FORMAT_WAVE64) {
if(flac__utils_fwrite("data", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, format==FORMAT_RF64? 0xffffffff : (FLAC__uint32)data_size))
return false;
}
else { /* Wave64 */
/* data GUID 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
if(flac__utils_fwrite("\x64\x61\x74\x61\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 1, 16, f) != 16)
return false;
/* +16+8 for GUID and size fields */
if(!write_little_endian_uint64(f, 16+8 + data_size))
return false;
}
decoder_session->fm_offset3 = ftello(f) + aligned_data_size;
}
@@ -707,14 +791,8 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
return true;
}
FLAC__bool write_riff_wave_fmt_chunk(FILE *f, FLAC__bool is_waveformatextensible, unsigned bps, unsigned channels, unsigned sample_rate, FLAC__uint32 channel_mask)
FLAC__bool write_riff_wave_fmt_chunk_body(FILE *f, FLAC__bool is_waveformatextensible, unsigned bps, unsigned channels, unsigned sample_rate, FLAC__uint32 channel_mask)
{
if(flac__utils_fwrite("fmt ", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, is_waveformatextensible? 40 : 16)) /* chunk size */
return false;
if(!write_little_endian_uint16(f, (FLAC__uint16)(is_waveformatextensible? 65534 : 1))) /* compression code */
return false;
@@ -864,7 +942,11 @@ FLAC__bool write_sane_extended(FILE *f, unsigned val)
FLAC__bool fixup_iff_headers(DecoderSession *d)
{
const char *fmt_desc = d->format==FORMAT_WAVE? "WAVE" : d->format==FORMAT_RF64? "RF64" : "AIFF";
const char *fmt_desc =
d->format==FORMAT_WAVE? "WAVE" :
d->format==FORMAT_WAVE64? "Wave64" :
d->format==FORMAT_RF64? "RF64" :
"AIFF";
FILE *f = fopen(d->outfilename, "r+b"); /* stream is positioned at beginning of file */
if(0 == f) {
@@ -886,15 +968,15 @@ 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->format != FORMAT_RAW && (bps%8)? 8-(bps%8): 0);
const unsigned shift = (decoder_session->format != FORMAT_RAW && (bps%8))? 8-(bps%8): 0;
FLAC__bool is_big_endian = (
decoder_session->format == FORMAT_AIFF || decoder_session->format == FORMAT_AIFF_C ? true : (
decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_RF64 ? false :
decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || decoder_session->format == FORMAT_RF64 ? false :
decoder_session->is_big_endian
));
FLAC__bool is_unsigned_samples = (
decoder_session->format == FORMAT_AIFF || decoder_session->format == FORMAT_AIFF_C ? false : (
decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_RF64 ? bps<=8 :
decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || decoder_session->format == FORMAT_RF64 ? bps<=8 :
decoder_session->is_unsigned_samples
));
unsigned wide_samples = frame->header.blocksize, wide_sample, sample, channel, byte;

View File

@@ -105,7 +105,6 @@ typedef struct {
union {
struct {
FLAC__uint64 data_bytes;
FLAC__bool pad;
} iff;
struct {
FLAC__StreamDecoder *decoder;
@@ -149,6 +148,7 @@ static int EncoderSession_finish_ok(EncoderSession *e, int info_align_carry, int
static int EncoderSession_finish_error(EncoderSession *e);
static FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t options);
static FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const buffer[], unsigned samples);
static FLAC__bool EncoderSession_format_is_iff(const EncoderSession *e);
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);
@@ -202,19 +202,30 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio
e->info.is_unsigned_samples = false;
e->info.is_big_endian = false;
/*
* lookahead[] already has "RIFFxxxxWAVE" or "RF64xxxxWAVE", do chunks
*/
if(e->format == FORMAT_WAVE64) {
/*
* lookahead[] already has "riff\x2E\x91\xCF\x11\xD6\xA5\x28\xDB", skip over remaining header
*/
if(!fskip_ahead(e->fin, 16+8+16-12)) { /* riff GUID + riff size + WAVE GUID - lookahead */
flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping over remaining \"riff\" header\n", e->inbasefilename);
return false;
}
}
/* else lookahead[] already has "RIFFxxxxWAVE" or "RF64xxxxWAVE" */
while(!feof(e->fin) && !got_data_chunk) {
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 */
if(!read_bytes(e->fin, (FLAC__byte*)chunk_id, 4, /*eof_ok=*/true, e->inbasefilename)) {
/* chunk IDs are 4 bytes for WAVE/RF64, 16 for Wave64 */
/* for WAVE/RF64 we want the 5th char zeroed so we can treat it like a C string */
char chunk_id[16] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' };
if(!read_bytes(e->fin, (FLAC__byte*)chunk_id, e->format==FORMAT_WAVE64?16:4, /*eof_ok=*/true, e->inbasefilename)) {
flac__utils_printf(stderr, 1, "%s: ERROR: incomplete chunk identifier\n", e->inbasefilename);
return false;
}
if(feof(e->fin))
break;
if(options.format == FORMAT_RF64 && !memcmp(chunk_id, "ds64", 4)) { /* RF64 64-bit sizes chunk */
if(e->format == FORMAT_RF64 && !memcmp(chunk_id, "ds64", 4)) { /* RF64 64-bit sizes chunk */
FLAC__uint32 xx, data_bytes;
if(got_ds64_chunk) {
@@ -257,7 +268,10 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio
got_ds64_chunk = true;
}
else if(!memcmp(chunk_id, "fmt ", 4)) { /* format chunk */
else if(
!memcmp(chunk_id, "fmt ", 4) &&
(e->format!=FORMAT_WAVE64 || !memcmp(chunk_id, "fmt \xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 16))
) { /* format chunk */
FLAC__uint16 x;
FLAC__uint32 xx, data_bytes;
FLAC__uint16 wFormatTag; /* wFormatTag word from the 'fmt ' chunk */
@@ -303,12 +317,32 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio
if(!read_uint32(e->fin, /*big_endian=*/false, &xx, e->inbasefilename))
return false;
data_bytes = xx;
if(e->format == FORMAT_WAVE64) {
/* other half of the size field should be 0 */
if(!read_uint32(e->fin, /*big_endian=*/false, &xx, e->inbasefilename))
return false;
if(xx) {
flac__utils_printf(stderr, 1, "%s: ERROR: freakishly large Wave64 'fmt ' chunk has length = 0x%08X%08X\n", e->inbasefilename, (unsigned)xx, (unsigned)data_bytes);
return false;
}
/* subtract size of header */
if (data_bytes < 16+8) {
flac__utils_printf(stderr, 1, "%s: ERROR: freakishly small Wave64 'fmt ' chunk has length = 0x%08X%08X\n", e->inbasefilename, (unsigned)xx, (unsigned)data_bytes);
return false;
}
data_bytes -= (16+8);
}
if(data_bytes < 16) {
flac__utils_printf(stderr, 1, "%s: ERROR: non-standard 'fmt ' chunk has length = %u\n", e->inbasefilename, (unsigned)data_bytes);
return false;
}
if(data_bytes & 1) /* should never happen, but enforce WAVE alignment rules */
data_bytes++;
if(e->format != FORMAT_WAVE64) {
if(data_bytes & 1) /* should never happen, but enforce WAVE alignment rules */
data_bytes++;
}
else { /* Wave64 */
data_bytes = (data_bytes+7) & (~7u); /* should never happen, but enforce Wave64 alignment rules */
}
/* format code */
if(!read_uint16(e->fin, /*big_endian=*/false, &wFormatTag, e->inbasefilename))
@@ -507,7 +541,10 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio
got_fmt_chunk = true;
}
else if(!memcmp(chunk_id, "data", 4)) { /* data chunk */
else if(
!memcmp(chunk_id, "data", 4) &&
(e->format!=FORMAT_WAVE64 || !memcmp(chunk_id, "data\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 16))
) { /* data chunk */
FLAC__uint32 xx;
FLAC__uint64 data_bytes;
@@ -517,10 +554,22 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio
}
/* data size */
if(!read_uint32(e->fin, /*big_endian=*/false, &xx, e->inbasefilename))
return false;
data_bytes = xx;
if(options.format == FORMAT_RF64) {
if(e->format != FORMAT_WAVE64) {
if(!read_uint32(e->fin, /*big_endian=*/false, &xx, e->inbasefilename))
return false;
data_bytes = xx;
}
else { /* Wave64 */
if(!read_uint64(e->fin, /*big_endian=*/false, &data_bytes, e->inbasefilename))
return false;
/* subtract size of header */
if (data_bytes < 16+8) {
flac__utils_printf(stderr, 1, "%s: ERROR: freakishly small Wave64 'data' chunk has length = 0x00000000%08X\n", e->inbasefilename, (unsigned)data_bytes);
return false;
}
data_bytes -= (16+8);
}
if(e->format == FORMAT_RF64) {
if(!got_ds64_chunk) {
flac__utils_printf(stderr, 1, "%s: ERROR: RF64 file has no 'ds64' chunk before 'data' chunk\n", e->inbasefilename);
return false;
@@ -543,26 +592,59 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio
}
e->fmt.iff.data_bytes = data_bytes;
e->fmt.iff.pad = (data_bytes & 1) ? true : false;
got_data_chunk = true;
break;
}
else {
FLAC__uint32 xx;
FLAC__uint64 skip;
if(!options.format_options.iff.foreign_metadata) {
flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown chunk '%s' (use --keep-foreign-metadata to keep)\n", e->inbasefilename, chunk_id);
if(e->format != FORMAT_WAVE64)
flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown chunk '%s' (use --keep-foreign-metadata to keep)\n", e->inbasefilename, chunk_id);
else
flac__utils_printf(stderr, 1, "%s: WARNING: skipping unknown chunk %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X (use --keep-foreign-metadata to keep)\n",
e->inbasefilename,
(unsigned)((const unsigned char *)chunk_id)[3],
(unsigned)((const unsigned char *)chunk_id)[2],
(unsigned)((const unsigned char *)chunk_id)[1],
(unsigned)((const unsigned char *)chunk_id)[0],
(unsigned)((const unsigned char *)chunk_id)[5],
(unsigned)((const unsigned char *)chunk_id)[4],
(unsigned)((const unsigned char *)chunk_id)[7],
(unsigned)((const unsigned char *)chunk_id)[6],
(unsigned)((const unsigned char *)chunk_id)[9],
(unsigned)((const unsigned char *)chunk_id)[8],
(unsigned)((const unsigned char *)chunk_id)[10],
(unsigned)((const unsigned char *)chunk_id)[11],
(unsigned)((const unsigned char *)chunk_id)[12],
(unsigned)((const unsigned char *)chunk_id)[13],
(unsigned)((const unsigned char *)chunk_id)[14],
(unsigned)((const unsigned char *)chunk_id)[15]
);
if(e->treat_warnings_as_errors)
return false;
}
/* chunk size */
if(!read_uint32(e->fin, /*big_endian=*/false, &xx, e->inbasefilename))
return false;
else {
unsigned long skip = xx + (xx & 1);
FLAC__ASSERT(skip <= LONG_MAX);
if(e->format != FORMAT_WAVE64) {
if(!read_uint32(e->fin, /*big_endian=*/false, &xx, e->inbasefilename))
return false;
skip = xx;
skip += skip & 1;
}
else { /* Wave64 */
if(!read_uint64(e->fin, /*big_endian=*/false, &skip, e->inbasefilename))
return false;
skip = (skip+7) & (~(FLAC__uint64)7);
/* subtract size of header */
if (skip < 16+8) {
flac__utils_printf(stderr, 1, "%s: ERROR: freakishly small Wave64 chunk has length = 0x00000000%08X\n", e->inbasefilename, (unsigned)skip);
return false;
}
skip -= (16+8);
}
if(skip) {
if(!fskip_ahead(e->fin, skip)) {
flac__utils_printf(stderr, 1, "%s: ERROR during read while skipping over chunk\n", e->inbasefilename);
return false;
@@ -615,7 +697,7 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio
FLAC__uint16 x;
FLAC__uint32 xx;
unsigned long skip;
const FLAC__bool is_aifc = options.format == FORMAT_AIFF_C;
const FLAC__bool is_aifc = e->format == FORMAT_AIFF_C;
const FLAC__uint32 minimum_comm_size = (is_aifc? 22 : 18);
if(got_comm_chunk) {
@@ -746,7 +828,6 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio
else {
data_bytes -= 8; /* discount the offset and block size fields */
}
e->fmt.iff.pad = (data_bytes & 1) ? true : false;
/* offset */
if(!read_uint32(e->fin, /*big_endian=*/true, &xx, e->inbasefilename))
@@ -818,7 +899,7 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio
return true;
}
static FLAC__bool get_sample_info_flac(EncoderSession *e, encode_options_t options)
static FLAC__bool get_sample_info_flac(EncoderSession *e)
{
if (!(
FLAC__stream_decoder_set_md5_checking(e->fmt.flac.decoder, false) &&
@@ -828,7 +909,7 @@ static FLAC__bool get_sample_info_flac(EncoderSession *e, encode_options_t optio
return false;
}
if (options.format == FORMAT_OGGFLAC) {
if (e->format == FORMAT_OGGFLAC) {
if (FLAC__stream_decoder_init_ogg_stream(e->fmt.flac.decoder, flac_decoder_read_callback, flac_decoder_seek_callback, flac_decoder_tell_callback, flac_decoder_length_callback, flac_decoder_eof_callback, flac_decoder_write_callback, flac_decoder_metadata_callback, flac_decoder_error_callback, /*client_data=*/e) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
flac__utils_printf(stderr, 1, "%s: ERROR: initializing decoder for Ogg FLAC input, state = %s\n", e->inbasefilename, FLAC__stream_decoder_get_resolved_state_string(e->fmt.flac.decoder));
return false;
@@ -892,11 +973,13 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
}
/* read foreign metadata if requested */
if(flac__utils_format_is_iff(options.format) && options.format_options.iff.foreign_metadata) {
if(EncoderSession_format_is_iff(&encoder_session) && options.format_options.iff.foreign_metadata) {
const char *error;
if(!(
options.format == FORMAT_WAVE || options.format == FORMAT_RF64?
flac__foreign_metadata_read_from_wave(options.format_options.iff.foreign_metadata, infilename, &error) :
options.format == FORMAT_WAVE64?
flac__foreign_metadata_read_from_wave64(options.format_options.iff.foreign_metadata, infilename, &error) :
flac__foreign_metadata_read_from_aiff(options.format_options.iff.foreign_metadata, infilename, &error)
)) {
flac__utils_printf(stderr, 1, "%s: ERROR reading foreign metadata: %s\n", encoder_session.inbasefilename, error);
@@ -911,6 +994,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
return EncoderSession_finish_error(&encoder_session);
break;
case FORMAT_WAVE:
case FORMAT_WAVE64:
case FORMAT_RF64:
if(!get_sample_info_wave(&encoder_session, options))
return EncoderSession_finish_error(&encoder_session);
@@ -929,7 +1013,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
flac__utils_printf(stderr, 1, "%s: ERROR: creating decoder for FLAC input\n", encoder_session.inbasefilename);
return EncoderSession_finish_error(&encoder_session);
}
if(!get_sample_info_flac(&encoder_session, options))
if(!get_sample_info_flac(&encoder_session))
return EncoderSession_finish_error(&encoder_session);
break;
default:
@@ -980,6 +1064,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
total_samples_in_input = (FLAC__uint64)infilesize / encoder_session.info.bytes_per_wide_sample + *options.align_reservoir_samples;
break;
case FORMAT_WAVE:
case FORMAT_WAVE64:
case FORMAT_RF64:
case FORMAT_AIFF:
case FORMAT_AIFF_C:
@@ -1022,6 +1107,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
encoder_session.total_samples_to_encode = total_samples_in_input - skip;
break;
case FORMAT_WAVE:
case FORMAT_WAVE64:
case FORMAT_RF64:
case FORMAT_AIFF:
case FORMAT_AIFF_C:
@@ -1050,7 +1136,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
FLAC__ASSERT(!options.sector_align);
if(options.format == FORMAT_RAW)
infilesize -= (off_t)trim * encoder_session.info.bytes_per_wide_sample;
else if(flac__utils_format_is_iff(options.format))
else if(EncoderSession_format_is_iff(&encoder_session))
encoder_session.fmt.iff.data_bytes -= trim * encoder_session.info.bytes_per_wide_sample;
encoder_session.total_samples_to_encode -= trim;
}
@@ -1070,6 +1156,10 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
/* +44 for the size of the WAVE 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 * encoder_session.info.bytes_per_wide_sample + 44;
break;
case FORMAT_WAVE64:
/* +44 for the size of the WAVE 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 * encoder_session.info.bytes_per_wide_sample + 104;
break;
case FORMAT_RF64:
/* +72 for the size of the RF64 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 * encoder_session.info.bytes_per_wide_sample + 80;
@@ -1128,6 +1218,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
}
break;
case FORMAT_WAVE:
case FORMAT_WAVE64:
case FORMAT_RF64:
case FORMAT_AIFF:
case FORMAT_AIFF_C:
@@ -1182,7 +1273,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
infilesize -= (off_t)((*options.align_reservoir_samples) * encoder_session.info.bytes_per_wide_sample);
FLAC__ASSERT(infilesize >= 0);
}
else if(flac__utils_format_is_iff(options.format))
else if(EncoderSession_format_is_iff(&encoder_session))
encoder_session.fmt.iff.data_bytes -= (*options.align_reservoir_samples) * encoder_session.info.bytes_per_wide_sample;
}
}
@@ -1290,6 +1381,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
}
break;
case FORMAT_WAVE:
case FORMAT_WAVE64:
case FORMAT_RF64:
case FORMAT_AIFF:
case FORMAT_AIFF_C:
@@ -1407,7 +1499,7 @@ int flac__encode_file(FILE *infile, off_t infilesize, const char *infilename, co
&encoder_session,
info_align_carry,
info_align_zero,
flac__utils_format_is_iff(options.format)? options.format_options.iff.foreign_metadata : 0
EncoderSession_format_is_iff(&encoder_session)? options.format_options.iff.foreign_metadata : 0
);
}
@@ -1458,11 +1550,11 @@ FLAC__bool EncoderSession_construct(EncoderSession *e, encode_options_t options,
case FORMAT_RAW:
break;
case FORMAT_WAVE:
case FORMAT_WAVE64:
case FORMAT_RF64:
case FORMAT_AIFF:
case FORMAT_AIFF_C:
e->fmt.iff.data_bytes = 0;
e->fmt.iff.pad = 0;
break;
case FORMAT_FLAC:
case FORMAT_OGGFLAC:
@@ -1643,7 +1735,7 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio
const unsigned channels = e->info.channels;
const unsigned bps = e->info.bits_per_sample - e->info.shift;
const unsigned sample_rate = e->info.sample_rate;
FLACDecoderData *flac_decoder_data = (options.format == FORMAT_FLAC || options.format == FORMAT_OGGFLAC)? &e->fmt.flac.client_data : 0;
FLACDecoderData *flac_decoder_data = (e->format == FORMAT_FLAC || e->format == FORMAT_OGGFLAC)? &e->fmt.flac.client_data : 0;
FLAC__StreamMetadata padding;
FLAC__StreamMetadata **metadata = 0;
static_metadata_t static_metadata;
@@ -1913,7 +2005,7 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio
* we're not encoding from FLAC so we will build the metadata
* from scratch
*/
const foreign_metadata_t *foreign_metadata = flac__utils_format_is_iff(options.format)? options.format_options.iff.foreign_metadata : 0;
const foreign_metadata_t *foreign_metadata = EncoderSession_format_is_iff(e)? options.format_options.iff.foreign_metadata : 0;
if(e->seek_table_template->data.seek_table.num_points > 0) {
e->seek_table_template->is_last = false; /* the encoder will set this for us */
@@ -2082,6 +2174,16 @@ FLAC__bool EncoderSession_process(EncoderSession *e, const FLAC__int32 * const b
return FLAC__stream_encoder_process(e->encoder, buffer, samples);
}
FLAC__bool EncoderSession_format_is_iff(const EncoderSession *e)
{
return
e->format == FORMAT_WAVE ||
e->format == FORMAT_WAVE64 ||
e->format == FORMAT_RF64 ||
e->format == FORMAT_AIFF ||
e->format == FORMAT_AIFF_C;
}
FLAC__bool convert_to_seek_table_template(const char *requested_seek_points, int num_requested_seek_points, FLAC__StreamMetadata *cuesheet, EncoderSession *e)
{
const FLAC__bool only_placeholders = e->is_stdout;

View File

@@ -41,7 +41,7 @@
#define min(x,y) ((x)<(y)?(x):(y))
static const char *FLAC__FOREIGN_METADATA_APPLICATION_ID[2] = { "aiff" , "riff" };
static const char *FLAC__FOREIGN_METADATA_APPLICATION_ID[3] = { "aiff" , "riff", "w64 " };
static FLAC__uint32 unpack32be_(const FLAC__byte *b)
{
@@ -323,6 +323,94 @@ static FLAC__bool read_from_wave_(foreign_metadata_t *fm, FILE *f, const char **
return true;
}
static FLAC__bool read_from_wave64_(foreign_metadata_t *fm, FILE *f, const char **error)
{
FLAC__byte buffer[40];
off_t offset, eof_offset = -1;
if((offset = ftello(f)) < 0) {
if(error) *error = "ftello() error (001)";
return false;
}
if(
fread(buffer, 1, 40, f) < 40 ||
/* RIFF GUID 66666972-912E-11CF-A5D6-28DB04C10000 */
memcmp(buffer, "\x72\x69\x66\x66\x2E\x91\xCF\x11\xD6\xA5\x28\xDB\x04\xC1\x00\x00", 16) ||
/* WAVE GUID 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
memcmp(buffer+24, "\x77\x61\x76\x65\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 16)
) {
if(error) *error = "unsupported Wave64 layout (002)";
return false;
}
if(sizeof(off_t) < 8) {
if(error) *error = "Wave64 is not supported on this compile (r00)";
return false;
}
if(!append_block_(fm, offset, 40, error))
return false;
eof_offset = (off_t)unpack64le_(buffer+16); /*@@@ [2^63 limit] */
while(!feof(f)) {
FLAC__uint64 size;
if((offset = ftello(f)) < 0) {
if(error) *error = "ftello() error (003)";
return false;
}
if((size = fread(buffer, 1, 24, f)) < 24) {
if(size == 0 && feof(f))
break;
if(error) *error = "invalid Wave64 file (004)";
return false;
}
size = unpack64le_(buffer+16);
/* check if pad bytes needed */
if(size & 7)
size = (size+7) & (~((FLAC__uint64)7));
/* fmt GUID 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
if(!memcmp(buffer, "\x66\x6D\x74\x20\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 16)) {
if(fm->format_block) {
if(error) *error = "invalid Wave64 file: multiple \"fmt \" chunks (005)";
return false;
}
if(fm->audio_block) {
if(error) *error = "invalid Wave64 file: \"data\" chunk before \"fmt \" chunk (006)";
return false;
}
fm->format_block = fm->num_blocks;
}
/* data GUID 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
else if(!memcmp(buffer, "\x64\x61\x74\x61\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 16)) {
if(fm->audio_block) {
if(error) *error = "invalid Wave64 file: multiple \"data\" chunks (007)";
return false;
}
if(!fm->format_block) {
if(error) *error = "invalid Wave64 file: \"data\" chunk before \"fmt \" chunk (008)";
return false;
}
fm->audio_block = fm->num_blocks;
}
if(!append_block_(fm, offset, memcmp(buffer, "\x64\x61\x74\x61\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 16)? size : 16+8, error))
return false;
/* skip to next chunk */
if(fseeko(f, size-24, SEEK_CUR) < 0) {
if(error) *error = "invalid Wave64 file: seek error (009)";
return false;
}
}
if(eof_offset != ftello(f)) {
if(error) *error = "invalid Wave64 file: unexpected EOF (010)";
return false;
}
if(!fm->format_block) {
if(error) *error = "invalid Wave64 file: missing \"fmt \" chunk (011)";
return false;
}
if(!fm->audio_block) {
if(error) *error = "invalid Wave64 file: missing \"data\" chunk (012)";
return false;
}
return true;
}
static FLAC__bool write_to_flac_(foreign_metadata_t *fm, FILE *fin, FILE *fout, FLAC__Metadata_SimpleIterator *it, const char **error)
{
FLAC__byte buffer[4];
@@ -391,7 +479,7 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
if(FLAC__metadata_simple_iterator_get_block_type(it) != FLAC__METADATA_TYPE_APPLICATION)
continue;
if(!FLAC__metadata_simple_iterator_get_application_id(it, id)) {
if(error) *error = "FLAC__metadata_simple_iterator_get_application_id() error (003)";
if(error) *error = "FLAC__metadata_simple_iterator_get_application_id() error (002)";
return false;
}
if(memcmp(id, FLAC__FOREIGN_METADATA_APPLICATION_ID[fm->type], sizeof(id)))
@@ -402,17 +490,19 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
offset += sizeof(id);
/* look for format or audio blocks */
if(fseek(f, offset, SEEK_SET) < 0) {
if(error) *error = "seek error (004)";
if(error) *error = "seek error (003)";
return false;
}
if(fread(buffer, 1, 4, f) != 4) {
if(error) *error = "read error (005)";
if(error) *error = "read error (004)";
return false;
}
if(fm->num_blocks == 0) { /* first block? */
fm->is_rf64 = 0 == memcmp(buffer, "RF64", 4);
if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && (0 == memcmp(buffer, "RIFF", 4) || fm->is_rf64))
type_found = true;
else if(fm->type == FOREIGN_BLOCK_TYPE__WAVE64 && 0 == memcmp(buffer, "riff", 4)) /* use first 4 bytes instead of whole GUID */
type_found = true;
else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF && 0 == memcmp(buffer, "FORM", 4))
type_found = true;
else {
@@ -457,31 +547,55 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
ds64_found = true;
}
}
else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF) {
if(!memcmp(buffer, "COMM", 4)) {
else if(fm->type == FOREIGN_BLOCK_TYPE__WAVE64) {
if(!memcmp(buffer, "fmt ", 4)) { /* use first 4 bytes instead of whole GUID */
if(fm->format_block) {
if(error) *error = "invalid AIFF metadata: multiple \"COMM\" chunks (012)";
if(error) *error = "invalid Wave64 metadata: multiple \"fmt \" chunks (012)";
return false;
}
if(fm->audio_block) {
if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (013)";
if(error) *error = "invalid Wave64 metadata: \"data\" chunk before \"fmt \" chunk (013)";
return false;
}
fm->format_block = fm->num_blocks;
}
else if(!memcmp(buffer, "data", 4)) { /* use first 4 bytes instead of whole GUID */
if(fm->audio_block) {
if(error) *error = "invalid Wave64 metadata: multiple \"data\" chunks (014)";
return false;
}
if(!fm->format_block) {
if(error) *error = "invalid Wave64 metadata: \"data\" chunk before \"fmt \" chunk (015)";
return false;
}
fm->audio_block = fm->num_blocks;
}
}
else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF) {
if(!memcmp(buffer, "COMM", 4)) {
if(fm->format_block) {
if(error) *error = "invalid AIFF metadata: multiple \"COMM\" chunks (016)";
return false;
}
if(fm->audio_block) {
if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (017)";
return false;
}
fm->format_block = fm->num_blocks;
}
else if(!memcmp(buffer, "SSND", 4)) {
if(fm->audio_block) {
if(error) *error = "invalid AIFF metadata: multiple \"SSND\" chunks (014)";
if(error) *error = "invalid AIFF metadata: multiple \"SSND\" chunks (018)";
return false;
}
if(!fm->format_block) {
if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (015)";
if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (019)";
return false;
}
fm->audio_block = fm->num_blocks;
/* read SSND offset size */
if(fread(buffer+4, 1, 8, f) != 8) {
if(error) *error = "read error (016)";
if(error) *error = "read error (020)";
return false;
}
fm->ssnd_offset_size = unpack32be_(buffer+8);
@@ -490,26 +604,34 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
else {
FLAC__ASSERT(0);
/* double protection: */
if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (017)";
if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (021)";
return false;
}
if(!append_block_(fm, offset, FLAC__metadata_simple_iterator_get_block_length(it)-sizeof(id), error))
return false;
}
if(!type_found) {
if(error) *error = "no foreign metadata found (018)";
if(error) *error = "no foreign metadata found (022)";
return false;
}
if(fm->is_rf64 && !ds64_found) {
if(error) *error = "invalid RF64 file: second chunk is not \"ds64\" (019)";
if(error) *error = "invalid RF64 file: second chunk is not \"ds64\" (023)";
return false;
}
if(!fm->format_block) {
if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"fmt \" chunk (020)" : "invalid AIFF file: missing \"COMM\" chunk (021)";
if(error)
*error =
fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"fmt \" chunk (024)" :
fm->type==FOREIGN_BLOCK_TYPE__WAVE64? "invalid Wave64 file: missing \"fmt \" chunk (025)" :
"invalid AIFF file: missing \"COMM\" chunk (026)";
return false;
}
if(!fm->audio_block) {
if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"data\" chunk (020)" : "invalid AIFF file: missing \"SSND\" chunk (023)";
if(error)
*error =
fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"data\" chunk (027)" :
fm->type==FOREIGN_BLOCK_TYPE__WAVE64? "invalid Wave64 file: missing \"data\" chunk (028)" :
"invalid AIFF file: missing \"SSND\" chunk (029)";
return false;
}
return true;
@@ -605,6 +727,19 @@ FLAC__bool flac__foreign_metadata_read_from_wave(foreign_metadata_t *fm, const c
return ok;
}
FLAC__bool flac__foreign_metadata_read_from_wave64(foreign_metadata_t *fm, const char *filename, const char **error)
{
FLAC__bool ok;
FILE *f = fopen(filename, "rb");
if(!f) {
if(error) *error = "can't open Wave64 file for reading (000)";
return false;
}
ok = read_from_wave64_(fm, f, error);
fclose(f);
return ok;
}
FLAC__bool flac__foreign_metadata_write_to_flac(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error)
{
FLAC__bool ok;

View File

@@ -29,7 +29,8 @@
/* WATCHOUT: these enums are used to index internal arrays */
typedef enum {
FOREIGN_BLOCK_TYPE__AIFF = 0, /* for AIFF and AIFF-C */
FOREIGN_BLOCK_TYPE__RIFF = 1 /* for WAVE and RF64 */
FOREIGN_BLOCK_TYPE__RIFF = 1, /* for WAVE and RF64 */
FOREIGN_BLOCK_TYPE__WAVE64 = 2 /* only for Sony's flavor */
} foreign_block_type_t;
typedef struct {
@@ -41,6 +42,8 @@ typedef struct {
/* For 'data'/'SSND' chunks, the size does not include the actual sound or padding bytes */
/* because these are not stored, they are recreated from the compressed FLAC stream. */
/* So for RIFF 'data', size is 8, and for AIFF 'SSND', size is 8 + 8 + ssnd_offset_size */
/* 32 bit size is OK because we only care about the non-sound data and FLAC metadata */
/* only supports a few megs anyway. */
FLAC__uint32 size;
} foreign_block_t;
@@ -60,6 +63,7 @@ void flac__foreign_metadata_delete(foreign_metadata_t *fm);
FLAC__bool flac__foreign_metadata_read_from_aiff(foreign_metadata_t *fm, const char *filename, const char **error);
FLAC__bool flac__foreign_metadata_read_from_wave(foreign_metadata_t *fm, const char *filename, const char **error);
FLAC__bool flac__foreign_metadata_read_from_wave64(foreign_metadata_t *fm, const char *filename, const char **error);
FLAC__bool flac__foreign_metadata_write_to_flac(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error);
FLAC__bool flac__foreign_metadata_read_from_flac(foreign_metadata_t *fm, const char *filename, const char **error);

View File

@@ -146,9 +146,10 @@ static struct share__option long_options_[] = {
{ "best" , share__no_argument, 0, '8' },
{ "fast" , share__no_argument, 0, '0' },
{ "verify" , share__no_argument, 0, 'V' },
{ "force-raw-format" , share__no_argument, 0, 0 },
{ "force-aiff-format" , share__no_argument, 0, 0 },
{ "force-rf64-format" , share__no_argument, 0, 0 },
{ "force-raw-format" , share__no_argument, 0, 0 },
{ "force-wave64-format" , share__no_argument, 0, 0 },
{ "lax" , share__no_argument, 0, 0 },
{ "replay-gain" , share__no_argument, 0, 0 },
{ "ignore-chunk-sizes" , share__no_argument, 0, 0 },
@@ -241,9 +242,10 @@ static struct {
FLAC__bool has_serial_number; /* true iff --serial-number was used */
long serial_number; /* this is the Ogg serial number and is unused for native FLAC */
FLAC__bool force_to_stdout;
FLAC__bool force_raw_format;
FLAC__bool force_aiff_format;
FLAC__bool force_rf64_format;
FLAC__bool force_raw_format;
FLAC__bool force_wave64_format;
FLAC__bool delete_input;
FLAC__bool preserve_modtime;
FLAC__bool keep_foreign_metadata;
@@ -380,8 +382,8 @@ int do_it(void)
if(!FLAC__format_sample_rate_is_valid(option_values.format_sample_rate))
return usage_error("ERROR: invalid sample rate '%u', must be > 0 and <= %u\n", option_values.format_sample_rate, FLAC__MAX_SAMPLE_RATE);
}
if((option_values.force_raw_format?1:0) + (option_values.force_aiff_format?1:0) + (option_values.force_rf64_format?1:0) > 1)
return usage_error("ERROR: only one of --force-raw-format/--force-aiff-format/--force-rf64-format allowed\n");
if((option_values.force_raw_format?1:0) + (option_values.force_aiff_format?1:0) + (option_values.force_rf64_format?1:0) + (option_values.force_wave64_format?1:0) > 1)
return usage_error("ERROR: only one of --force-raw-format/--force-aiff-format/--force-rf64-format/--force-wave64-format allowed\n");
if(option_values.mode_decode) {
if(!option_values.force_raw_format) {
if(option_values.format_is_big_endian >= 0)
@@ -564,9 +566,10 @@ FLAC__bool init_options(void)
option_values.has_serial_number = false;
option_values.serial_number = 0;
option_values.force_to_stdout = false;
option_values.force_raw_format = false;
option_values.force_aiff_format = false;
option_values.force_rf64_format = false;
option_values.force_raw_format = false;
option_values.force_wave64_format = false;
option_values.delete_input = false;
option_values.preserve_modtime = true;
option_values.keep_foreign_metadata = false;
@@ -760,14 +763,17 @@ int parse_option(int short_option, const char *long_option, const char *option_a
else if(0 == strcmp(long_option, "no-cued-seekpoints")) {
option_values.cued_seekpoints = false;
}
else if(0 == strcmp(long_option, "force-raw-format")) {
option_values.force_raw_format = true;
}
else if(0 == strcmp(long_option, "force-aiff-format")) {
option_values.force_aiff_format = true;
}
else if(0 == strcmp(long_option, "force-rf64-format")) {
option_values.force_rf64_format = true;
}
else if(0 == strcmp(long_option, "force-raw-format")) {
option_values.force_raw_format = true;
else if(0 == strcmp(long_option, "force-wave64-format")) {
option_values.force_wave64_format = true;
}
else if(0 == strcmp(long_option, "lax")) {
option_values.lax = true;
@@ -1277,9 +1283,10 @@ void show_help(void)
printf(" --sample-rate=# Sample rate in Hz\n");
printf(" --sign={signed|unsigned} Sign of samples\n");
printf(" --input-size=# Size of the raw input in bytes\n");
printf(" --force-raw-format Treat input or output as raw samples\n");
printf(" --force-aiff-format Force decoding to AIFF format\n");
printf(" --force-rf64-format Force decoding to RF64 format\n");
printf(" --force-raw-format Treat input or output as raw samples\n");
printf(" --force-wave64-format Force decoding to Wave64 format\n");
printf("negative options:\n");
printf(" --no-adaptive-mid-side\n");
printf(" --no-decode-through-errors\n");
@@ -1609,6 +1616,8 @@ void show_explain(void)
printf(" the input stream, the encoder will complain\n");
printf(" about an unexpected end-of-file. If the size\n");
printf(" given is less, samples will be truncated.\n");
printf(" --force-raw-format Force input (when encoding) or output (when\n");
printf(" decoding) to be treated as raw samples\n");
printf(" --force-aiff-format Force the decoder to output AIFF format. This\n");
printf(" option is not needed if the output filename (as\n");
printf(" set by -o) ends with .aif or .aiff; this option\n");
@@ -1619,8 +1628,11 @@ void show_explain(void)
printf(" set by -o) ends with .rf64; this option\n");
printf(" has no effect when encoding since input RF64 is\n");
printf(" auto-detected.\n");
printf(" --force-raw-format Force input (when encoding) or output (when\n");
printf(" decoding) to be treated as raw samples\n");
printf(" --force-wave64-format Force the decoder to output Wave64 format. This\n");
printf(" option is not needed if the output filename (as\n");
printf(" set by -o) ends with .w64; this option\n");
printf(" has no effect when encoding since input Wave64 is\n");
printf(" auto-detected.\n");
printf("negative options:\n");
printf(" --no-adaptive-mid-side\n");
printf(" --no-decode-through-errors\n");
@@ -1689,8 +1701,10 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
/* first set format based on name */
if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".wav"))
input_format = FORMAT_WAVE;
if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".rf64"))
else if(strlen(infilename) >= 5 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-5), ".rf64"))
input_format = FORMAT_RF64;
else if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".w64"))
input_format = FORMAT_WAVE64;
else if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".aif"))
input_format = FORMAT_AIFF;
else if(strlen(infilename) >= 5 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-5), ".aiff"))
@@ -1724,6 +1738,8 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
input_format = FORMAT_WAVE;
else if(!memcmp(lookahead, "RF64", 4) && !memcmp(lookahead+8, "WAVE", 4))
input_format = FORMAT_RF64;
else if(!memcmp(lookahead, "riff\x2E\x91\xCF\x11\xD6\xA5\x28\xDB", 12)) /* just check 1st 12 bytes of GUID */
input_format = FORMAT_WAVE64;
else if(!memcmp(lookahead, "FORM", 4) && !memcmp(lookahead+8, "AIFF", 4))
input_format = FORMAT_AIFF;
else if(!memcmp(lookahead, "FORM", 4) && !memcmp(lookahead+8, "AIFC", 4))
@@ -1753,9 +1769,9 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
conditional_fclose(encode_infile);
return usage_error("ERROR: --keep-foreign-metadata cannot be used when encoding from stdin or to stdout\n");
}
if(input_format != FORMAT_WAVE && input_format != FORMAT_RF64 && input_format != FORMAT_AIFF && input_format != FORMAT_AIFF_C) {
if(input_format != FORMAT_WAVE && input_format != FORMAT_WAVE64 && input_format != FORMAT_RF64 && input_format != FORMAT_AIFF && input_format != FORMAT_AIFF_C) {
conditional_fclose(encode_infile);
return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE, RF64, or AIFF input\n");
return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE, Wave64, RF64, or AIFF input\n");
}
}
@@ -1909,7 +1925,7 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
else if(input_format == FORMAT_FLAC || input_format == FORMAT_OGGFLAC) {
retval = flac__encode_file(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, encode_options);
}
else if(input_format == FORMAT_WAVE || input_format == FORMAT_RF64 || input_format == FORMAT_AIFF || input_format == FORMAT_AIFF_C) {
else if(input_format == FORMAT_WAVE || input_format == FORMAT_WAVE64 || input_format == FORMAT_RF64 || input_format == FORMAT_AIFF || input_format == FORMAT_AIFF_C) {
encode_options.format_options.iff.foreign_metadata = 0;
/* initialize foreign metadata if requested */
@@ -1918,6 +1934,8 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
flac__foreign_metadata_new(
input_format==FORMAT_WAVE || input_format==FORMAT_RF64?
FOREIGN_BLOCK_TYPE__RIFF :
input_format==FORMAT_WAVE64?
FOREIGN_BLOCK_TYPE__WAVE64 :
FOREIGN_BLOCK_TYPE__AIFF
);
if(0 == encode_options.format_options.iff.foreign_metadata) {
@@ -2021,6 +2039,11 @@ int decode_file(const char *infilename)
(strlen(outfilename) >= 5 && 0 == FLAC__STRCASECMP(outfilename+(strlen(outfilename)-5), ".rf64"))
)
output_format = FORMAT_RF64;
else if(
option_values.force_wave64_format ||
(strlen(outfilename) >= 4 && 0 == FLAC__STRCASECMP(outfilename+(strlen(outfilename)-4), ".w64"))
)
output_format = FORMAT_WAVE64;
else
output_format = FORMAT_WAVE;
@@ -2032,8 +2055,8 @@ int decode_file(const char *infilename)
if(option_values.keep_foreign_metadata) {
if(0 == strcmp(infilename, "-") || 0 == strcmp(outfilename, "-"))
return usage_error("ERROR: --keep-foreign-metadata cannot be used when decoding from stdin or to stdout\n");
if(output_format != FORMAT_WAVE && output_format != FORMAT_RF64 && output_format != FORMAT_AIFF && output_format != FORMAT_AIFF_C)
return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE, RF64, or AIFF output\n");
if(output_format != FORMAT_WAVE && output_format != FORMAT_WAVE64 && output_format != FORMAT_RF64 && output_format != FORMAT_AIFF && output_format != FORMAT_AIFF_C)
return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE, Wave64, RF64, or AIFF output\n");
}
if(option_values.use_ogg)
@@ -2095,6 +2118,8 @@ int decode_file(const char *infilename)
flac__foreign_metadata_new(
output_format==FORMAT_WAVE || output_format==FORMAT_RF64?
FOREIGN_BLOCK_TYPE__RIFF :
output_format==FORMAT_WAVE64?
FOREIGN_BLOCK_TYPE__WAVE64 :
FOREIGN_BLOCK_TYPE__AIFF
);
if(0 == decode_options.format_options.iff.foreign_metadata) {
@@ -2140,6 +2165,9 @@ const char *get_decoded_outfilename(const char *infilename)
else if(option_values.force_rf64_format) {
suffix = ".rf64";
}
else if(option_values.force_wave64_format) {
suffix = ".w64";
}
else {
suffix = ".wav";
}

View File

@@ -33,16 +33,6 @@ const char *CHANNEL_MASK_TAG = "WAVEFORMATEXTENSIBLE_CHANNEL_MASK";
int flac__utils_verbosity_ = 2;
FLAC__bool flac__utils_format_is_iff(FileFormat format)
{
return
format == FORMAT_WAVE ||
format == FORMAT_RF64 ||
format == FORMAT_AIFF ||
format == FORMAT_AIFF_C
;
}
static FLAC__bool local__parse_uint64_(const char *s, FLAC__uint64 *value)
{
FLAC__uint64 ret = 0;

View File

@@ -27,10 +27,7 @@
#include "FLAC/format.h" /* for FLAC__StreamMetadata_CueSheet */
#include <stdio.h> /* for FILE */
typedef enum { FORMAT_RAW, FORMAT_WAVE, FORMAT_RF64, FORMAT_AIFF, FORMAT_AIFF_C, FORMAT_FLAC, FORMAT_OGGFLAC } FileFormat;
/* returns true iff format is one of FORMAT_WAVE, FORMAT_RF64, FORMAT_AIFF, FORMAT_AIFF_C */
FLAC__bool flac__utils_format_is_iff(FileFormat format);
typedef enum { FORMAT_RAW, FORMAT_WAVE, FORMAT_WAVE64, FORMAT_RF64, FORMAT_AIFF, FORMAT_AIFF_C, FORMAT_FLAC, FORMAT_OGGFLAC } FileFormat;
typedef struct {
FLAC__bool is_relative; /* i.e. specification string started with + or - */

View File

@@ -682,17 +682,20 @@ foo:
return false;
}
static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples, FLAC__bool strict, FLAC__bool rf64)
/* flavor is: 0:WAVE, 1:RF64, 2:WAVE64 */
static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples, FLAC__bool strict, int flavor)
{
const FLAC__bool waveformatextensible = strict && (channels > 2 || (bps%8));
/* ^^^^^^^
* (bps%8) allows 24 bps which is technically supposed to be WAVEFORMATEXTENSIBLE but we
* write 24bps as WAVEFORMATEX since it's unambiguous and matches how flac writes it
*/
const unsigned bytes_per_sample = (bps+7)/8;
const unsigned true_size = channels * bytes_per_sample * samples;
const unsigned padded_size = (true_size + 1) & (~1u);
const unsigned shift = (bps%8)? 8 - (bps%8) : 0;
/* this rig is not going over 4G so we're ok with 32-bit sizes here */
const FLAC__uint32 true_size = channels * bytes_per_sample * samples;
const FLAC__uint32 padded_size = flavor<2? (true_size + 1) & (~1u) : (true_size + 7) & (~7u);
const FLAC__int32 full_scale = (1 << (bps-1)) - 1;
const double f1 = 441.0, a1 = 0.61, f2 = 661.5, a2 = 0.37;
const double delta1 = 2.0 * M_PI / ( sample_rate / f1);
@@ -703,13 +706,45 @@ static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsig
if(0 == (f = fopen(filename, "wb")))
return false;
if(fwrite(rf64?"RF64":"RIFF", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, rf64? 0xffffffff : padded_size + (waveformatextensible?60:36)))
goto foo;
if(fwrite("WAVE", 1, 4, f) < 4)
goto foo;
if(rf64) {
/* RIFFxxxxWAVE or equivalent: */
switch(flavor) {
case 0:
if(fwrite("RIFF", 1, 4, f) < 4)
goto foo;
/* +4 for WAVE */
/* +8+{40,16} for fmt chunk */
/* +8 for data chunk header */
if(!write_little_endian_uint32(f, 4 + 8+(waveformatextensible?40:16) + 8 + padded_size))
goto foo;
if(fwrite("WAVE", 1, 4, f) < 4)
goto foo;
break;
case 1:
if(fwrite("RF64", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, 0xffffffff))
goto foo;
if(fwrite("WAVE", 1, 4, f) < 4)
goto foo;
break;
case 2:
/* RIFF GUID 66666972-912E-11CF-A5D6-28DB04C10000 */
if(fwrite("\x72\x69\x66\x66\x2E\x91\xCF\x11\xD6\xA5\x28\xDB\x04\xC1\x00\x00", 1, 16, f) < 16)
goto foo;
/* +(16+8) for RIFF GUID + size */
/* +16 for WAVE GUID */
/* +16+8+{40,16} for fmt chunk */
/* +16+8 for data chunk header */
if(!write_little_endian_uint64(f, (16+8) + 16 + 16+8+(waveformatextensible?40:16) + (16+8) + padded_size))
goto foo;
/* WAVE GUID 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
if(fwrite("\x77\x61\x76\x65\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 1, 16, f) < 16)
goto foo;
break;
default:
goto foo;
}
if(flavor == 1) { /* rf64 */
if(fwrite("ds64", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, 28)) /* ds64 chunk size */
@@ -723,10 +758,22 @@ static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsig
if(!write_little_endian_uint32(f, 0)) /* table size */
goto foo;
}
if(fwrite("fmt ", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, waveformatextensible?40:16))
goto foo;
/* fmt chunk */
if(flavor < 2) {
if(fwrite("fmt ", 1, 4, f) < 4)
goto foo;
/* chunk size */
if(!write_little_endian_uint32(f, waveformatextensible?40:16))
goto foo;
}
else { /* wave64 */
/* fmt GUID 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
if(fwrite("\x66\x6D\x74\x20\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 1, 16, f) < 16)
goto foo;
/* chunk size (+16+8 for GUID and size fields) */
if(!write_little_endian_uint64(f, 16+8+(waveformatextensible?40:16)))
goto foo;
}
if(!write_little_endian_uint16(f, (FLAC__uint16)(waveformatextensible?65534:1)))
goto foo;
if(!write_little_endian_uint16(f, (FLAC__uint16)channels))
@@ -750,10 +797,21 @@ static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsig
if(fwrite("\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, f) != 16)
goto foo;
}
if(fwrite("data", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, rf64? 0xffffffff : true_size))
goto foo;
/* data chunk */
if(flavor < 2) {
if(fwrite("data", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, flavor==1? 0xffffffff : true_size))
goto foo;
}
else { /* wave64 */
/* data GUID 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
if(fwrite("\x64\x61\x74\x61\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A", 1, 16, f) != 16)
goto foo;
/* +16+8 for GUID and size fields */
if(!write_little_endian_uint64(f, 16+8 + true_size))
goto foo;
}
for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) {
for(j = 0; j < channels; j++) {
@@ -811,6 +869,54 @@ foo:
return false;
}
static FLAC__bool generate_wackywav64s(void)
{
FILE *f;
FLAC__byte wav[] = {
0x72,0x69,0x66,0x66,0x2E,0x91,0xCF,0x11, /* RIFF GUID */
0xD6,0xA5,0x28,0xDB,0x04,0xC1,0x00,0x00,
152, 0, 0, 0, 0, 0, 0, 0,
0x77,0x61,0x76,0x65,0xF3,0xAC,0xD3,0x11, /* WAVE GUID */
0xD1,0x8C,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
0x6A,0x75,0x6E,0x6B,0xF3,0xAC,0xD3,0x11, /* junk GUID */
0xD1,0x8C,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
32, 0, 0, 0 , 0, 0, 0, 0,
'b', 'l', 'a', 'h', 'b', 'l', 'a', 'h',
0x66,0x6D,0x74,0x20,0xF3,0xAC,0xD3,0x11, /* fmt GUID */
0xD1,0x8C,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
40, 0, 0, 0 , 0, 0, 0, 0,
1, 0, 1, 0,0x44,0xAC, 0, 0,
0x88,0x58,0x01, 0, 2, 0, 16, 0,
0x64,0x61,0x74,0x61,0xF3,0xAC,0xD3,0x11, /* data GUID */
0xD1,0x8C,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
40, 0, 0, 0 , 0, 0, 0, 0,
0, 0, 1, 0, 4, 0, 9, 0,
16, 0, 25, 0, 36, 0, 49, 0,
0x6A,0x75,0x6E,0x6B,0xF3,0xAC,0xD3,0x11, /* junk GUID */
0xD1,0x8C,0x00,0xC0,0x4F,0x8E,0xDB,0x8A,
32, 0, 0, 0 , 0, 0, 0, 0,
'b', 'l', 'a', 'h', 'b', 'l', 'a', 'h'
};
if(0 == (f = fopen("wacky1.w64", "wb")))
return false;
if(fwrite(wav, 1, wav[16], f) < wav[16])
goto foo;
fclose(f);
wav[16] += 32;
if(0 == (f = fopen("wacky2.w64", "wb")))
return false;
if(fwrite(wav, 1, wav[16], f) < wav[16])
goto foo;
fclose(f);
return true;
foo:
fclose(f);
return false;
}
static FLAC__bool generate_wackyrf64s(void)
{
FILE *f;
@@ -970,6 +1076,7 @@ int main(int argc, char *argv[])
if(!generate_noise("noise.raw", 65536 * 8 * 3)) return 1;
if(!generate_noise("noise8m32.raw", 32)) return 1;
if(!generate_wackywavs()) return 1;
if(!generate_wackywav64s()) return 1;
if(!generate_wackyrf64s()) return 1;
for(channels = 1; channels <= 8; channels++) {
unsigned bits_per_sample;
@@ -984,11 +1091,15 @@ int main(int argc, char *argv[])
return 1;
sprintf(fn, "rt-%u-%u-%u.wav", channels, bits_per_sample, nsamples[samples]);
if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true, /*rf64=*/false))
if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true, /*flavor=*/0))
return 1;
sprintf(fn, "rt-%u-%u-%u.rf64", channels, bits_per_sample, nsamples[samples]);
if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true, /*rf64=*/true))
if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true, /*flavor=*/1))
return 1;
sprintf(fn, "rt-%u-%u-%u.w64", channels, bits_per_sample, nsamples[samples]);
if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true, /*flavor=*/2))
return 1;
if(bits_per_sample % 8 == 0) {