add support for Wave64 (SF#1769582: https://sourceforge.net/tracker/index.php?func...
authorJosh Coalson <jcoalson@users.sourceforce.net>
Tue, 9 Sep 2008 07:49:19 +0000 (07:49 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Tue, 9 Sep 2008 07:49:19 +0000 (07:49 +0000)
16 files changed:
doc/html/changelog.html
doc/html/documentation_tools_flac.html
doc/html/id.html
man/flac.sgml
src/flac/decode.c
src/flac/encode.c
src/flac/foreign_metadata.c
src/flac/foreign_metadata.h
src/flac/main.c
src/flac/utils.c
src/flac/utils.h
src/test_streams/main.c
test/Makefile.am
test/Makefile.lite
test/test_flac.sh
test/write_iff.pl

index 2f213c8..91bb60c 100644 (file)
@@ -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>
index aafc37c..88ec2d8 100644 (file)
@@ -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 />
                                <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 />
                <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 />
                <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 />
                                        <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>
                        </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" />
                                        <span class="argument">--force-aiff-format</span>
                                </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>
+                                       <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>
                <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 />
index 9d456f4..e46881e 100644 (file)
                        </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>
                                </td>
                                <td>
index 317afd1..3b96644 100644 (file)
        </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>
 
          <listitem>
        </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>
 
index d98cf6a..27fe9e4 100644 (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;
index ac3982b..63e02b4 100644 (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;
index 424ad3d..1717b23 100644 (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__WAVE64) {
+                       if(!memcmp(buffer, "fmt ", 4)) { /* use first 4 bytes instead of whole GUID */
+                               if(fm->format_block) {
+                                       if(error) *error = "invalid Wave64 metadata: multiple \"fmt \" chunks (012)";
+                                       return false;
+                               }
+                               if(fm->audio_block) {
+                                       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 (012)";
+                                       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 (013)";
+                                       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;
index 11c1099..74dcccb 100644 (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);
index 6f29d01..881e6a1 100644 (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";
        }
index 4dcd038..28b1ebb 100644 (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;
index 368d1e4..96ad517 100644 (file)
 #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 - */
index f91c9e5..c6e77a1 100644 (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) {
index 370d10a..1b94ac6 100644 (file)
@@ -49,4 +49,4 @@ EXTRA_DIST = \
        write_iff.pl
 
 clean-local:
-       -rm -f *.raw *.flac *.oga *.ogg *.cmp *.aiff *.wav *.rf64 *.diff *.log *.cue core
+       -rm -f *.raw *.flac *.oga *.ogg *.cmp *.aiff *.wav *.w64 *.rf64 *.diff *.log *.cue core
index 08bb189..dce6cff 100644 (file)
@@ -52,4 +52,4 @@ valgrind: all
 release : all
 
 clean:
-       rm -f *.raw *.flac *.oga *.ogg *.cmp *.aiff *.wav *.rf64 *.diff *.log *.cue core flac-to-flac-metadata-test-files/out.* metaflac-test-files/out.*
+       rm -f *.raw *.flac *.oga *.ogg *.cmp *.aiff *.wav *.w64 *.rf64 *.diff *.log *.cue core flac-to-flac-metadata-test-files/out.* metaflac-test-files/out.*
index c208d96..0a69a43 100755 (executable)
@@ -210,6 +210,20 @@ rt_test_wav ()
        rm -f rt.flac rt.wav
 }
 
+rt_test_w64 ()
+{
+       f="$1"
+       extra="$2"
+       echo -n "round-trip test ($f) encode... "
+       run_flac $SILENT --force --verify --channel-map=none --no-padding --lax -o rt.flac $extra $f || die "ERROR"
+       echo -n "decode... "
+       run_flac $SILENT --force --decode --channel-map=none -o rt.w64 $extra rt.flac || die "ERROR"
+       echo -n "compare... "
+       cmp $f rt.w64 || die "ERROR: file mismatch"
+       echo "OK"
+       rm -f rt.flac rt.w64
+}
+
 rt_test_rf64 ()
 {
        f="$1"
@@ -278,6 +292,9 @@ done
 for f in rt-*.wav ; do
        rt_test_wav $f
 done
+for f in rt-*.w64 ; do
+       rt_test_w64 $f
+done
 for f in rt-*.rf64 ; do
        rt_test_rf64 $f
 done
@@ -1146,6 +1163,8 @@ echo "Testing --keep-foreign-metadata..."
 
 rt_test_wav wacky1.wav '--keep-foreign-metadata'
 rt_test_wav wacky2.wav '--keep-foreign-metadata'
+rt_test_w64 wacky1.w64 '--keep-foreign-metadata'
+rt_test_w64 wacky2.w64 '--keep-foreign-metadata'
 rt_test_rf64 wacky1.rf64 '--keep-foreign-metadata'
 rt_test_rf64 wacky2.rf64 '--keep-foreign-metadata'
 
index 83132fb..21ac733 100755 (executable)
@@ -7,7 +7,7 @@ require Math::BigInt;
 my $usage = "
 $0 <format> <bps> <channels> <sample-rate> <#samples> <sample-type>
 
-     <format> is one of aiff,wave,rf64
+     <format> is one of aiff,wave,wave64,rf64
         <bps> is 8,16,24,32
    <channels> is 1-8
 <sample-rate> is any 32-bit value
@@ -18,7 +18,7 @@ $0 <format> <bps> <channels> <sample-rate> <#samples> <sample-type>
 
 die $usage unless @ARGV == 6;
 
-my %formats = ( 'aiff'=>1, 'wave'=>1, 'rf64'=>1 );
+my %formats = ( 'aiff'=>1, 'wave'=>1, 'wave64'=>1, 'rf64'=>1 );
 my %sampletypes = ( 'zero'=>1, 'rand'=>1 );
 my @channelmask = ( 0, 1, 3, 7, 0x33, 0x607, 0x60f, 0, 0 ); #@@@@@@ need proper masks for 7,8
 
@@ -37,8 +37,10 @@ $bps /= 8;
 my $datasize = $samples * $bps * $channels;
 my $bigdatasize = $bigsamples * $bps * $channels;
 
-my $padding = int($bigdatasize & 1? 1 : 0);
-my $wavx = ($format eq 'wave' || $format eq 'rf64') && ($channels > 2);
+my $padding = int($bigdatasize & 1); # for aiff/wave/rf64 chunk alignment
+my $padding8 = 8 - int($bigdatasize & 7); $padding8 = 0 if $padding8 == 8; # for wave64 alignment
+# wave-ish file needs to be WAVEFORMATEXTENSIBLE?
+my $wavx = ($format eq 'wave' || $format eq 'wave64' || $format eq 'rf64') && ($channels > 2);
 
 # write header
 
@@ -61,14 +63,29 @@ if ($format eq 'aiff') {
        print pack('N', 0); # ssnd_offset_size
        print pack('N', 0); # blocksize
 }
-elsif ($format eq 'wave' || $format eq 'rf64') {
+elsif ($format eq 'wave' || $format eq 'wave64' || $format eq 'rf64') {
        die "sample data too big for format\n" if $format eq 'wave' && ($wavx?60:36) + $datasize + $padding > 4294967295;
        # header
        if ($format eq 'wave') {
                print "RIFF";
-               print pack('V', ($wavx?60:36) + $datasize + $padding);
+               # +4 for WAVE
+               # +8+{40,16} for fmt chunk
+               # +8 for data chunk header
+               print pack('V', 4 + 8+($wavx?40:16) + 8 + $datasize + $padding);
                print "WAVE";
        }
+       elsif ($format eq 'wave64') {
+               # RIFF GUID 66666972-912E-11CF-A5D6-28DB04C10000
+               print "\x72\x69\x66\x66\x2E\x91\xCF\x11\xD6\xA5\x28\xDB\x04\xC1\x00\x00";
+               # +(16+8) for RIFF GUID + size
+               # +16 for WAVE GUID
+               # +16+8+{40,16} for fmt chunk
+               # +16+8 for data chunk header
+               my $bigriffsize = $bigdatasize + (16+8) + 16 + 16+8+($wavx?40:16) + (16+8) + $padding8;
+               print pack_64('V', $bigriffsize);
+               # WAVE GUID 65766177-ACF3-11D3-8CD1-00C04F8EDB8A
+               print "\x77\x61\x76\x65\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A";
+       }
        else {
                print "RF64";
                print pack('V', 0xffffffff);
@@ -76,15 +93,27 @@ elsif ($format eq 'wave' || $format eq 'rf64') {
                # ds64 chunk
                print "ds64";
                print pack('V', 28); # chunk size
-               my $bigriffsize = $bigdatasize + ($wavx?60:36) + (8+28) + $padding;
+               # +4 for WAVE
+               # +(8+28) for ds64 chunk
+               # +8+{40,16} for fmt chunk
+               # +8 for data chunk header
+               my $bigriffsize = $bigdatasize + 4 + (8+28) + 8+($wavx?40:16) + 8 + $padding;
                print pack_64('V', $bigriffsize);
                print pack_64('V', $bigdatasize);
                print pack_64('V', $bigsamples);
                print pack('V', 0); # table size
        }
        # fmt chunk
-       print "fmt ";
-       print pack('V', $wavx?40:16); # chunk size
+       if ($format ne 'wave64') {
+               print "fmt ";
+               print pack('V', $wavx?40:16); # chunk size
+       }
+       else { # wave64
+               # fmt GUID 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A
+               print "\x66\x6D\x74\x20\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A";
+               print pack('V', 16+8+($wavx?40:16)); # chunk size (+16+8 for GUID and size fields)
+               print pack('V', 0);                  # ...is 8 bytes for wave64
+       }
        print pack('v', $wavx?65534:1); # compression code
        print pack('v', $channels);
        print pack('V', $samplerate);
@@ -99,8 +128,15 @@ elsif ($format eq 'wave' || $format eq 'rf64') {
                print "\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71";
        }
        # data header
-       print "data";
-       print pack('V', $format eq 'wave'? $datasize : 0xffffffff);
+       if ($format ne 'wave64') {
+               print "data";
+               print pack('V', $format eq 'wave'? $datasize : 0xffffffff);
+       }
+       else { # wave64
+               # data GUID 61746164-ACF3-11D3-8CD1-00C04F8EDB8A
+               print "\x64\x61\x74\x61\xF3\xAC\xD3\x11\xD1\x8C\x00\xC0\x4F\x8E\xDB\x8A";
+               print pack_64('V', $bigdatasize+16+8); # +16+8 for GUID and size fields
+       }
 }
 else {
        die;
@@ -132,7 +168,14 @@ elsif ($sampletype eq 'rand') {
 else {
        die;
 }
-print "\x00" if $padding;
+
+# write padding
+if ($format eq 'wave64') {
+       print pack("x[$padding8]") if $padding8;
+}
+else {
+       print "\x00" if $padding;
+}
 
 exit 0;
 
@@ -150,14 +193,14 @@ sub pack_sane_extended
 
 sub pack_64
 {
-       my $c = shift;
-       my $v1 = shift;
+       my $c = shift; # 'N' for big-endian, 'V' for little-endian, ala pack()
+       my $v1 = shift; # value, must be Math::BigInt
        my $v2 = $v1->copy();
        if ($c eq 'V') {
                $v1->band(0xffffffff);
                $v2->brsft(32);
        }
-       elsif ($c eq 'C') {
+       elsif ($c eq 'N') {
                $v2->band(0xffffffff);
                $v1->brsft(32);
        }