Commit d7f5344a authored by Josh Coalson's avatar Josh Coalson
Browse files

add support for Wave64 (SF#1769582:...

add support for Wave64 (SF#1769582: https://sourceforge.net/tracker/index.php?func=detail&aid=1769582&group_id=13478&atid=113478)
parent 7617cacb
......@@ -63,7 +63,7 @@
General:
<ul>
<li>The <span class="argument"><a href="documentation_tools_flac.html#flac_options_sector_align">--sector-align</a></span> option of <span class="commandname">flac</span> has been deprecated and may not exist in future versions. <a href="http://www.etree.org/shnutils/shntool/">shntool</a> provides similar functionality.</li>
<li>Support for the RF64 format in <span class="commandname">flac</span> (see below).</li>
<li>Support for the RF64 and Wave64 formats in <span class="commandname">flac</span> (see below).</li>
</ul>
</li>
<li>
......@@ -81,7 +81,8 @@
<li>
flac:
<ul>
<li>Added support for encoding from and decoding to the RF64 format, and a new corresponding option <span class="argument"><a href="#flac_options_force_rf64_format" />--force-rf64-format</a></span>. (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1762502&amp;group_id=13478&amp;atid=363478">SF #1762502</a>).</li>
<li>Added support for encoding from and decoding to the RF64 format, and a new corresponding option <span class="argument"><a href="#flac_options_force_rf64_format" />--force-rf64-format</a></span>. (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1762502&amp;group_id=13478&amp;atid=363478">SF #1762502</a>). <span class="argument"><a href="documentation_tools_flac.html#flac_options_keep_foreign_metadata">--keep-foreign-metadata</a></span> is also supported.</li>
<li>Added support for encoding from and decoding to the Sony Wave64 format, and a new corresponding option <span class="argument"><a href="#flac_options_force_wave64_format" />--force-wave64-format</a></span>. (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1769582&amp;group_id=13478&amp;atid=363478">SF #1769582</a>). <span class="argument"><a href="documentation_tools_flac.html#flac_options_keep_foreign_metadata">--keep-foreign-metadata</a></span> is also supported.</li>
<li>Added a new options <span class="argument"><a href="documentation_tools_flac.html#flac_options_preserve_modtime">--preserve-modtime</a></span> and <span class="argument"><a href="documentation_tools_flac.html#flac_options_no_preserve_modtime">--no-preserve-modtime</a></span> to specify whether or not output files should copy the timestamp and permissions from their input files. The default is <span class="argument"><a href="documentation_tools_flac.html#flac_options_preserve_modtime">--preserve-modtime</a></span> as in previous versions. (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1805428&amp;group_id=13478&amp;atid=363478">SF #1805428</a>).</li>
<li>The <span class="argument"><a href="documentation_tools_flac.html#flac_options_sector_align">--sector-align</a></span> option of <span class="commandname">flac</span> has been deprecated and may not exist in future versions. <a href="http://www.etree.org/shnutils/shntool/">shntool</a> provides similar functionality. (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1805946&amp;group_id=13478&amp;atid=363478">SF #1805946</a>)</li>
</ul>
......
......@@ -63,9 +63,9 @@
</ul>
<a name="usage"><font size="+1"><b><u>General Usage</u></b></font></a><br />
<br />
<span class="commandname">flac</span> is the command-line file encoder/decoder. The encoder currently supports as input RIFF WAVE, RF64, AIFF, FLAC or Ogg FLAC format, or raw interleaved samples. The decoder currently can output to RIFF WAVE, RF64, or AIFF format, or raw interleaved samples. <span class="commandname">flac</span> only supports linear PCM samples (in other words, no A-LAW, uLAW, etc.), and the input must be between 4 and 24 bits per sample. This is not a limitation of the FLAC format, just the reference encoder/decoder.<br />
<span class="commandname">flac</span> is the command-line file encoder/decoder. The encoder currently supports as input RIFF WAVE, Wave64, RF64, AIFF, FLAC or Ogg FLAC format, or raw interleaved samples. The decoder currently can output to RIFF WAVE, Wave64, RF64, or AIFF format, or raw interleaved samples. <span class="commandname">flac</span> only supports linear PCM samples (in other words, no A-LAW, uLAW, etc.), and the input must be between 4 and 24 bits per sample. This is not a limitation of the FLAC format, just the reference encoder/decoder.<br />
<br />
<span class="commandname">flac</span> assumes that files ending in ".wav" or that have the RIFF WAVE header present are WAVE files, files ending in ".rf64" or have the RF64 header present are RF64 files, files ending in ".aif" or ".aiff" or have the AIFF header present are AIFF files, and files ending in ".flac" or have the FLAC header present are FLAC files. This assumption may be overridden with a command-line option. It also assumes that files ending in ".oga" or ".ogg" or have the Ogg FLAC header present are Ogg FLAC files. Other than this, <span class="commandname">flac</span> makes no assumptions about file extensions, though the convention is that FLAC files have the extension ".flac" (or ".fla" on ancient "8.3" file systems like FAT-16).<br />
<span class="commandname">flac</span> assumes that files ending in ".wav" or that have the RIFF WAVE header present are WAVE files, files ending in ".w64" or have the Wave64 header present are Wave64 files, files ending in ".rf64" or have the RF64 header present are RF64 files, files ending in ".aif" or ".aiff" or have the AIFF header present are AIFF files, and files ending in ".flac" or have the FLAC header present are FLAC files. This assumption may be overridden with a command-line option. It also assumes that files ending in ".oga" or ".ogg" or have the Ogg FLAC header present are Ogg FLAC files. Other than this, <span class="commandname">flac</span> makes no assumptions about file extensions, though the convention is that FLAC files have the extension ".flac" (or ".fla" on ancient "8.3" file systems like FAT-16).<br />
<br />
Before going into the full command-line description, a few other things help to sort it out: 1) <span class="commandname">flac</span> encodes by default, so you must use <b>-d</b> to decode; 2) the options <span class="argument">-0</span> .. <span class="argument">-8</span> (or <span class="argument">--fast</span> and <span class="argument">--best</span>) that control the compression level actually are just synonyms for different groups of specific encoding options (described later) and you can get the same effect by using the same options; 3) <span class="commandname">flac</span> behaves similarly to gzip in the way it handles input and output files.<br />
<br />
......@@ -116,7 +116,7 @@
<span class="code">flac -V -- -01-filename.wav</span>
</li>
</ul>
The encoding options affect the compression ratio and encoding speed. The format options are used to tell <span class="commandname">flac</span> the arrangement of samples if the input file (or output file when decoding) is a raw file. If it is a RIFF WAVE, RF64, or AIFF file the format options are not needed since they are read from the file's header.<br />
The encoding options affect the compression ratio and encoding speed. The format options are used to tell <span class="commandname">flac</span> the arrangement of samples if the input file (or output file when decoding) is a raw file. If it is a RIFF WAVE, Wave64, RF64, or AIFF file the format options are not needed since they are read from the file's header.<br />
<br />
In test mode, <span class="commandname">flac</span> acts just like in decode mode, except no output file is written. Both decode and test modes detect errors in the stream, but they also detect when the MD5 signature of the decoded audio does not match the stored MD5 signature, even when the bitstream is valid.<br />
<br />
......@@ -159,6 +159,9 @@
<tt><b>flac abc.rf64</b></tt><br />
Encode <tt>abc.rf64</tt> to <tt>abc.flac</tt>.<br />
<br />
<tt><b>flac abc.w64</b></tt><br />
Encode <tt>abc.w64</tt> to <tt>abc.flac</tt>.<br />
<br />
<tt><b>flac abc.flac <a href="#flac_options_force">--force</a></b></tt><br />
This one's a little tricky: notice that <span class="commandname">flac</span> is in encode mode by default (you have to specify <span class="argument">-d</span> to decode) so this command actually recompresses <tt>abc.flac</tt> back to <tt>abc.flac</tt>. <span class="argument"><a href="#flac_options_force">--force</a></span> is needed to make sure you really want to overwrite <tt>abc.flac</tt> with a new version. Why would you want to do this? It allows you to recompress an existing FLAC file with (usually) higher compression options or a newer version of FLAC and preserve all the metadata like tags too.<br />
<br />
......@@ -176,6 +179,10 @@
<tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_output_name">-o</a> abc.rf64 abc.flac</b></tt><br />
Two different ways of decoding <tt>abc.flac</tt> to <tt>abc.rf64</tt> (RF64 format). <tt>abc.flac</tt> is not deleted.<br />
<br />
<tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_force_wave64_format">--force-wave64-format</a> abc.flac</b></tt><br />
<tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_output_name">-o</a> abc.w64 abc.flac</b></tt><br />
Two different ways of decoding <tt>abc.flac</tt> to <tt>abc.w64</tt> (Wave64 format). <tt>abc.flac</tt> is not deleted.<br />
<br />
<tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_decode_through_errors">-F</a> abc.flac</b></tt><br />
Decode <tt>abc.flac</tt> to <tt>abc.wav</tt> and don't abort if errors are found (useful for recovering as much as possible from corrupted files).<br />
<br />
......@@ -339,13 +346,13 @@
<span class="argument">--keep-foreign-metadata</span>
</td>
<td>
If encoding, save WAVE, RF64, or AIFF non-audio chunks in FLAC metadata. If decoding, restore any saved non-audio chunks from FLAC metadata when writing the decoded file. Foreign metadata cannot be transcoded, e.g. WAVE chunks saved in a FLAC file cannot be restored when decoding to AIFF. Input and output must be regular files (not stdin or stdout).<br />
If encoding, save WAVE, Wave64, RF64, or AIFF non-audio chunks in FLAC metadata. If decoding, restore any saved non-audio chunks from FLAC metadata when writing the decoded file. Foreign metadata cannot be transcoded, e.g. WAVE chunks saved in a FLAC file cannot be restored when decoding to AIFF. Input and output must be regular files (not stdin or stdout).<br />
<!--
<br />
Using this option for both encoding then decoding in most cases will yield the exact same WAVE file as the original, metadata and all. Because there are multiple ways to represent the same data in WAVE, RF64, and AIFF, there are currently a few corner cases where the restoration process may not match exactly (but could with some improvement). The cases are:<br />
Using this option for both encoding then decoding in most cases will yield the exact same WAVE file as the original, metadata and all. Because there are multiple ways to represent the same data in WAVE, Wave64, RF64, and AIFF, there are currently a few corner cases where the restoration process may not match exactly (but could with some improvement). The cases are:<br />
<ul>
<li>The original WAVE/RF64 had more than 2 channels and needed remapping to FLAC order</li>
<li>The original WAVE/RF64 is not spec compliant, e.g. 20 bps in WAVEFORMATEX; restored file will still be a compliant WAVEFORMATEXTENSIBLE</li>
<li>The original WAVE/Wave64/RF64 had more than 2 channels and needed remapping to FLAC order</li>
<li>The original WAVE/Wave64/RF64 is not spec compliant, e.g. 20 bps in WAVEFORMATEX; restored file will still be a compliant WAVEFORMATEXTENSIBLE</li>
<li>Other weird corner cases where the "fmt" chunk is not exactly identical due to there being multiple ways to represent the same thing</li>
<li>The original AIFF is in AIFF-C form with compression type "sowt" or "NONE"; currently the restored file will always be in AIFF (uncompressed) form</li>
</ul>
......@@ -947,6 +954,15 @@
Specify the size of the raw input in bytes. If you are encoding raw samples from stdin, you must set this option in order to be able to use --skip, --until, --cue-sheet, or other options that need to know the size of the input beforehand. If the size given is greater than what is found in the input stream, the encoder will complain about an unexpected end-of-file. If the size given is less, samples will be truncated.
</td>
</tr>
<tr>
<td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
<a name="flac_options_force_raw_format" />
<span class="argument">--force-raw-format</span>
</td>
<td>
Treat the input file (or output file if decoding) as a raw file, regardless of the extension.
</td>
</tr>
<tr>
<td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
<a name="flac_options_force_aiff_format" />
......@@ -967,11 +983,11 @@
</tr>
<tr>
<td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
<a name="flac_options_force_raw_format" />
<span class="argument">--force-raw-format</span>
<a name="flac_options_force_wave64_format" />
<span class="argument">--force-wave64-format</span>
</td>
<td>
Treat the input file (or output file if decoding) as a raw file, regardless of the extension.
Force the decoder to output Wave64 format. This option is not needed if the output filename (as set by -o) ends with .w64. Also, this option has no effect when encoding since input Wave64 is auto-detected.
</td>
</tr>
</table>
......@@ -1061,9 +1077,10 @@
<a href="#flac_options_decode_through_errors" /><span class="argument">-F</span></a><br />
<a href="#flac_options_force" /><span class="argument">-f</span></a><br />
<a href="#flac_options_fast" /><span class="argument">--fast</span></a><br />
<a href="#flac_options_force_raw_format" /><span class="argument">--force-raw-format</span></a><br />
<a href="#flac_options_force_aiff_format" /><span class="argument">--force-aiff-format</span></a><br />
<a href="#flac_options_force_rf64_format" /><span class="argument">--force-rf64-format</span></a><br />
<a href="#flac_options_force_raw_format" /><span class="argument">--force-raw-format</span></a><br />
<a href="#flac_options_force_wave64_format" /><span class="argument">--force-wave64-format</span></a><br />
<a href="#flac_options_force" /><span class="argument">--force</span></a><br />
<a href="#flac_options_explain" /><span class="argument">-H</span></a><br />
<a href="#flac_options_help" /><span class="argument">-h</span></a><br />
......
......@@ -232,6 +232,14 @@
<a href="http://www.tagtuner.com/">TagTuner</a>
</td>
</tr>
<tr>
<td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
<tt>77363420 - "w64 "</tt>
</td>
<td>
FLAC Wave64 chunk storage
</td>
</tr>
<tr>
<td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
<tt>78626174 - "xbat"</tt>
......
......@@ -652,6 +652,14 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--force-raw-format</option></term>
<listitem>
<para>Force input (when encoding) or output (when decoding) to be treated as raw samples (even if filename ends in <filename>.wav</filename>).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--force-aiff-format</option></term>
......@@ -669,10 +677,10 @@
</varlistentry>
<varlistentry>
<term><option>--force-raw-format</option></term>
<term><option>--force-wave64-format</option></term>
<listitem>
<para>Force input (when encoding) or output (when decoding) to be treated as raw samples (even if filename ends in <filename>.wav</filename>).</para>
<para>Force the decoder to output Wave64 format. This option is not needed if the output filename (as set by -o) ends with <filename>.w64</filename>. Also, this option has no effect when encoding since input Wave64 is auto-detected.</para>
</listitem>
</varlistentry>
......
......@@ -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;
......
......@@ -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)