diff options
author | Josh Coalson <jcoalson@users.sourceforce.net> | 2008-09-09 07:49:19 +0000 |
---|---|---|
committer | Josh Coalson <jcoalson@users.sourceforce.net> | 2008-09-09 07:49:19 +0000 |
commit | d7f5344a6408f95901cc17329264bea03db514e0 (patch) | |
tree | e4e29a2d5a541ea9695454fb3198aa4431e9bf1c | |
parent | 7617cacb282a00c0188aca3fec5613aa1d038740 (diff) | |
download | flac-d7f5344a6408f95901cc17329264bea03db514e0.tar.gz |
add support for Wave64 (SF#1769582: https://sourceforge.net/tracker/index.php?func=detail&aid=1769582&group_id=13478&atid=113478)
-rw-r--r-- | doc/html/changelog.html | 5 | ||||
-rw-r--r-- | doc/html/documentation_tools_flac.html | 39 | ||||
-rw-r--r-- | doc/html/id.html | 8 | ||||
-rw-r--r-- | man/flac.sgml | 12 | ||||
-rw-r--r-- | src/flac/decode.c | 184 | ||||
-rw-r--r-- | src/flac/encode.c | 172 | ||||
-rw-r--r-- | src/flac/foreign_metadata.c | 163 | ||||
-rw-r--r-- | src/flac/foreign_metadata.h | 6 | ||||
-rw-r--r-- | src/flac/main.c | 60 | ||||
-rw-r--r-- | src/flac/utils.c | 10 | ||||
-rw-r--r-- | src/flac/utils.h | 5 | ||||
-rw-r--r-- | src/test_streams/main.c | 151 | ||||
-rw-r--r-- | test/Makefile.am | 2 | ||||
-rw-r--r-- | test/Makefile.lite | 2 | ||||
-rwxr-xr-x | test/test_flac.sh | 19 | ||||
-rwxr-xr-x | test/write_iff.pl | 73 |
16 files changed, 728 insertions, 183 deletions
diff --git a/doc/html/changelog.html b/doc/html/changelog.html index 2f213c80..91bb60c0 100644 --- a/doc/html/changelog.html +++ b/doc/html/changelog.html @@ -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&aid=1762502&group_id=13478&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&aid=1762502&group_id=13478&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&aid=1769582&group_id=13478&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&aid=1805428&group_id=13478&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&aid=1805946&group_id=13478&atid=363478">SF #1805946</a>)</li> </ul> diff --git a/doc/html/documentation_tools_flac.html b/doc/html/documentation_tools_flac.html index aafc37c8..88ec2d8a 100644 --- a/doc/html/documentation_tools_flac.html +++ b/doc/html/documentation_tools_flac.html @@ -63,9 +63,9 @@ </ul> <a name="usage"><font size="+1"><b><u>General Usage</u></b></font></a><br /> <br /> - <span class="commandname">flac</span> is the command-line file encoder/decoder. The encoder currently supports as input RIFF WAVE, RF64, AIFF, FLAC or Ogg FLAC format, or raw interleaved samples. The decoder currently can output to RIFF WAVE, RF64, or AIFF format, or raw interleaved samples. <span class="commandname">flac</span> only supports linear PCM samples (in other words, no A-LAW, uLAW, etc.), and the input must be between 4 and 24 bits per sample. This is not a limitation of the FLAC format, just the reference encoder/decoder.<br /> + <span class="commandname">flac</span> is the command-line file encoder/decoder. The encoder currently supports as input RIFF WAVE, Wave64, RF64, AIFF, FLAC or Ogg FLAC format, or raw interleaved samples. The decoder currently can output to RIFF WAVE, Wave64, RF64, or AIFF format, or raw interleaved samples. <span class="commandname">flac</span> only supports linear PCM samples (in other words, no A-LAW, uLAW, etc.), and the input must be between 4 and 24 bits per sample. This is not a limitation of the FLAC format, just the reference encoder/decoder.<br /> <br /> - <span class="commandname">flac</span> assumes that files ending in ".wav" or that have the RIFF WAVE header present are WAVE files, files ending in ".rf64" or have the RF64 header present are RF64 files, files ending in ".aif" or ".aiff" or have the AIFF header present are AIFF files, and files ending in ".flac" or have the FLAC header present are FLAC files. This assumption may be overridden with a command-line option. It also assumes that files ending in ".oga" or ".ogg" or have the Ogg FLAC header present are Ogg FLAC files. Other than this, <span class="commandname">flac</span> makes no assumptions about file extensions, though the convention is that FLAC files have the extension ".flac" (or ".fla" on ancient "8.3" file systems like FAT-16).<br /> + <span class="commandname">flac</span> assumes that files ending in ".wav" or that have the RIFF WAVE header present are WAVE files, files ending in ".w64" or have the Wave64 header present are Wave64 files, files ending in ".rf64" or have the RF64 header present are RF64 files, files ending in ".aif" or ".aiff" or have the AIFF header present are AIFF files, and files ending in ".flac" or have the FLAC header present are FLAC files. This assumption may be overridden with a command-line option. It also assumes that files ending in ".oga" or ".ogg" or have the Ogg FLAC header present are Ogg FLAC files. Other than this, <span class="commandname">flac</span> makes no assumptions about file extensions, though the convention is that FLAC files have the extension ".flac" (or ".fla" on ancient "8.3" file systems like FAT-16).<br /> <br /> Before going into the full command-line description, a few other things help to sort it out: 1) <span class="commandname">flac</span> encodes by default, so you must use <b>-d</b> to decode; 2) the options <span class="argument">-0</span> .. <span class="argument">-8</span> (or <span class="argument">--fast</span> and <span class="argument">--best</span>) that control the compression level actually are just synonyms for different groups of specific encoding options (described later) and you can get the same effect by using the same options; 3) <span class="commandname">flac</span> behaves similarly to gzip in the way it handles input and output files.<br /> <br /> @@ -116,7 +116,7 @@ <span class="code">flac -V -- -01-filename.wav</span> </li> </ul> - The encoding options affect the compression ratio and encoding speed. The format options are used to tell <span class="commandname">flac</span> the arrangement of samples if the input file (or output file when decoding) is a raw file. If it is a RIFF WAVE, RF64, or AIFF file the format options are not needed since they are read from the file's header.<br /> + The encoding options affect the compression ratio and encoding speed. The format options are used to tell <span class="commandname">flac</span> the arrangement of samples if the input file (or output file when decoding) is a raw file. If it is a RIFF WAVE, Wave64, RF64, or AIFF file the format options are not needed since they are read from the file's header.<br /> <br /> In test mode, <span class="commandname">flac</span> acts just like in decode mode, except no output file is written. Both decode and test modes detect errors in the stream, but they also detect when the MD5 signature of the decoded audio does not match the stored MD5 signature, even when the bitstream is valid.<br /> <br /> @@ -159,6 +159,9 @@ <tt><b>flac abc.rf64</b></tt><br /> Encode <tt>abc.rf64</tt> to <tt>abc.flac</tt>.<br /> <br /> + <tt><b>flac abc.w64</b></tt><br /> + Encode <tt>abc.w64</tt> to <tt>abc.flac</tt>.<br /> + <br /> <tt><b>flac abc.flac <a href="#flac_options_force">--force</a></b></tt><br /> This one's a little tricky: notice that <span class="commandname">flac</span> is in encode mode by default (you have to specify <span class="argument">-d</span> to decode) so this command actually recompresses <tt>abc.flac</tt> back to <tt>abc.flac</tt>. <span class="argument"><a href="#flac_options_force">--force</a></span> is needed to make sure you really want to overwrite <tt>abc.flac</tt> with a new version. Why would you want to do this? It allows you to recompress an existing FLAC file with (usually) higher compression options or a newer version of FLAC and preserve all the metadata like tags too.<br /> <br /> @@ -176,6 +179,10 @@ <tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_output_name">-o</a> abc.rf64 abc.flac</b></tt><br /> Two different ways of decoding <tt>abc.flac</tt> to <tt>abc.rf64</tt> (RF64 format). <tt>abc.flac</tt> is not deleted.<br /> <br /> + <tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_force_wave64_format">--force-wave64-format</a> abc.flac</b></tt><br /> + <tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_output_name">-o</a> abc.w64 abc.flac</b></tt><br /> + Two different ways of decoding <tt>abc.flac</tt> to <tt>abc.w64</tt> (Wave64 format). <tt>abc.flac</tt> is not deleted.<br /> + <br /> <tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_decode_through_errors">-F</a> abc.flac</b></tt><br /> Decode <tt>abc.flac</tt> to <tt>abc.wav</tt> and don't abort if errors are found (useful for recovering as much as possible from corrupted files).<br /> <br /> @@ -339,13 +346,13 @@ <span class="argument">--keep-foreign-metadata</span> </td> <td> - If encoding, save WAVE, RF64, or AIFF non-audio chunks in FLAC metadata. If decoding, restore any saved non-audio chunks from FLAC metadata when writing the decoded file. Foreign metadata cannot be transcoded, e.g. WAVE chunks saved in a FLAC file cannot be restored when decoding to AIFF. Input and output must be regular files (not stdin or stdout).<br /> + If encoding, save WAVE, Wave64, RF64, or AIFF non-audio chunks in FLAC metadata. If decoding, restore any saved non-audio chunks from FLAC metadata when writing the decoded file. Foreign metadata cannot be transcoded, e.g. WAVE chunks saved in a FLAC file cannot be restored when decoding to AIFF. Input and output must be regular files (not stdin or stdout).<br /> <!-- <br /> - Using this option for both encoding then decoding in most cases will yield the exact same WAVE file as the original, metadata and all. Because there are multiple ways to represent the same data in WAVE, RF64, and AIFF, there are currently a few corner cases where the restoration process may not match exactly (but could with some improvement). The cases are:<br /> + Using this option for both encoding then decoding in most cases will yield the exact same WAVE file as the original, metadata and all. Because there are multiple ways to represent the same data in WAVE, Wave64, RF64, and AIFF, there are currently a few corner cases where the restoration process may not match exactly (but could with some improvement). The cases are:<br /> <ul> - <li>The original WAVE/RF64 had more than 2 channels and needed remapping to FLAC order</li> - <li>The original WAVE/RF64 is not spec compliant, e.g. 20 bps in WAVEFORMATEX; restored file will still be a compliant WAVEFORMATEXTENSIBLE</li> + <li>The original WAVE/Wave64/RF64 had more than 2 channels and needed remapping to FLAC order</li> + <li>The original WAVE/Wave64/RF64 is not spec compliant, e.g. 20 bps in WAVEFORMATEX; restored file will still be a compliant WAVEFORMATEXTENSIBLE</li> <li>Other weird corner cases where the "fmt" chunk is not exactly identical due to there being multiple ways to represent the same thing</li> <li>The original AIFF is in AIFF-C form with compression type "sowt" or "NONE"; currently the restored file will always be in AIFF (uncompressed) form</li> </ul> @@ -949,6 +956,15 @@ </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> @@ -967,11 +983,11 @@ </tr> <tr> <td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC"> - <a name="flac_options_force_raw_format" /> - <span class="argument">--force-raw-format</span> + <a name="flac_options_force_wave64_format" /> + <span class="argument">--force-wave64-format</span> </td> <td> - Treat the input file (or output file if decoding) as a raw file, regardless of the extension. + Force the decoder to output Wave64 format. This option is not needed if the output filename (as set by -o) ends with .w64. Also, this option has no effect when encoding since input Wave64 is auto-detected. </td> </tr> </table> @@ -1061,9 +1077,10 @@ <a href="#flac_options_decode_through_errors" /><span class="argument">-F</span></a><br /> <a href="#flac_options_force" /><span class="argument">-f</span></a><br /> <a href="#flac_options_fast" /><span class="argument">--fast</span></a><br /> + <a href="#flac_options_force_raw_format" /><span class="argument">--force-raw-format</span></a><br /> <a href="#flac_options_force_aiff_format" /><span class="argument">--force-aiff-format</span></a><br /> <a href="#flac_options_force_rf64_format" /><span class="argument">--force-rf64-format</span></a><br /> - <a href="#flac_options_force_raw_format" /><span class="argument">--force-raw-format</span></a><br /> + <a href="#flac_options_force_wave64_format" /><span class="argument">--force-wave64-format</span></a><br /> <a href="#flac_options_force" /><span class="argument">--force</span></a><br /> <a href="#flac_options_explain" /><span class="argument">-H</span></a><br /> <a href="#flac_options_help" /><span class="argument">-h</span></a><br /> diff --git a/doc/html/id.html b/doc/html/id.html index 9d456f4e..e46881ea 100644 --- a/doc/html/id.html +++ b/doc/html/id.html @@ -234,6 +234,14 @@ </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> diff --git a/man/flac.sgml b/man/flac.sgml index 317afd13..3b96644a 100644 --- a/man/flac.sgml +++ b/man/flac.sgml @@ -653,6 +653,14 @@ </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> @@ -669,10 +677,10 @@ </varlistentry> <varlistentry> - <term><option>--force-raw-format</option></term> + <term><option>--force-wave64-format</option></term> <listitem> - <para>Force input (when encoding) or output (when decoding) to be treated as raw samples (even if filename ends in <filename>.wav</filename>).</para> + <para>Force the decoder to output Wave64 format. This option is not needed if the output filename (as set by -o) ends with <filename>.w64</filename>. Also, this option has no effect when encoding since input Wave64 is auto-detected.</para> </listitem> </varlistentry> diff --git a/src/flac/decode.c b/src/flac/decode.c index d98cf6a3..27fe9e4c 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -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; diff --git a/src/flac/encode.c b/src/flac/encode.c index ac3982b2..63e02b47 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -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; diff --git a/src/flac/foreign_metadata.c b/src/flac/foreign_metadata.c index 424ad3d7..1717b231 100644 --- a/src/flac/foreign_metadata.c +++ b/src/flac/foreign_metadata.c @@ -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; diff --git a/src/flac/foreign_metadata.h b/src/flac/foreign_metadata.h index 11c1099a..74dcccb8 100644 --- a/src/flac/foreign_metadata.h +++ b/src/flac/foreign_metadata.h @@ -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); diff --git a/src/flac/main.c b/src/flac/main.c index 6f29d010..881e6a1a 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -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"; } diff --git a/src/flac/utils.c b/src/flac/utils.c index 4dcd0383..28b1ebb6 100644 --- a/src/flac/utils.c +++ b/src/flac/utils.c @@ -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; diff --git a/src/flac/utils.h b/src/flac/utils.h index 368d1e43..96ad5175 100644 --- a/src/flac/utils.h +++ b/src/flac/utils.h @@ -27,10 +27,7 @@ #include "FLAC/format.h" /* for FLAC__StreamMetadata_CueSheet */ #include <stdio.h> /* for FILE */ -typedef enum { FORMAT_RAW, FORMAT_WAVE, FORMAT_RF64, FORMAT_AIFF, FORMAT_AIFF_C, FORMAT_FLAC, FORMAT_OGGFLAC } FileFormat; - -/* returns true iff format is one of FORMAT_WAVE, FORMAT_RF64, FORMAT_AIFF, FORMAT_AIFF_C */ -FLAC__bool flac__utils_format_is_iff(FileFormat format); +typedef enum { FORMAT_RAW, FORMAT_WAVE, FORMAT_WAVE64, FORMAT_RF64, FORMAT_AIFF, FORMAT_AIFF_C, FORMAT_FLAC, FORMAT_OGGFLAC } FileFormat; typedef struct { FLAC__bool is_relative; /* i.e. specification string started with + or - */ diff --git a/src/test_streams/main.c b/src/test_streams/main.c index f91c9e5e..c6e77a10 100644 --- a/src/test_streams/main.c +++ b/src/test_streams/main.c @@ -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) { diff --git a/test/Makefile.am b/test/Makefile.am index 370d10ac..1b94ac66 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 diff --git a/test/Makefile.lite b/test/Makefile.lite index 08bb189e..dce6cffc 100644 --- a/test/Makefile.lite +++ b/test/Makefile.lite @@ -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.* diff --git a/test/test_flac.sh b/test/test_flac.sh index c208d967..0a69a432 100755 --- a/test/test_flac.sh +++ b/test/test_flac.sh @@ -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' diff --git a/test/write_iff.pl b/test/write_iff.pl index 83132fba..21ac7332 100755 --- a/test/write_iff.pl +++ b/test/write_iff.pl @@ -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); } |