Commit 7617cacb authored by Josh Coalson's avatar Josh Coalson
Browse files

add support for RF64

parent 59c9d343
......@@ -63,6 +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>
</ul>
</li>
<li>
......@@ -80,6 +81,7 @@
<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 a new options <span class="argument"><a href="documentation_tools_flac.html#flac_options_preserve_modtime">--preserve-modtime</a></span> and <span class="argument"><a href="documentation_tools_flac.html#flac_options_no_preserve_modtime">--no-preserve-modtime</a></span> to specify whether or not output files should copy the timestamp and permissions from their input files. The default is <span class="argument"><a href="documentation_tools_flac.html#flac_options_preserve_modtime">--preserve-modtime</a></span> as in previous versions. (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1805428&amp;group_id=13478&amp;atid=363478">SF #1805428</a>).</li>
<li>The <span class="argument"><a href="documentation_tools_flac.html#flac_options_sector_align">--sector-align</a></span> option of <span class="commandname">flac</span> has been deprecated and may not exist in future versions. <a href="http://www.etree.org/shnutils/shntool/">shntool</a> provides similar functionality. (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1805946&amp;group_id=13478&amp;atid=363478">SF #1805946</a>)</li>
</ul>
......
......@@ -63,9 +63,9 @@
</ul>
<a name="usage"><font size="+1"><b><u>General Usage</u></b></font></a><br />
<br />
<span class="commandname">flac</span> is the command-line file encoder/decoder. The encoder currently supports as input RIFF WAVE, AIFF, FLAC or Ogg FLAC format, or raw interleaved samples. The decoder currently can output to RIFF WAVE 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, 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 />
<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 ".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 ".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 or AIFF file the format options are not needed since they are read from the AIFF/WAVE 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, 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 />
......@@ -156,6 +156,9 @@
<tt><b>flac abc.aiff</b></tt><br />
Encode <tt>abc.aiff</tt> to <tt>abc.flac</tt>.<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.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 />
......@@ -169,6 +172,10 @@
<tt><b>flac <a href="#flac_options_decode">-d</a> <a href="#flac_options_output_name">-o</a> abc.aiff abc.flac</b></tt><br />
Two different ways of decoding <tt>abc.flac</tt> to <tt>abc.aiff</tt> (AIFF 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_rf64_format">--force-rf64-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.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_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 />
......@@ -332,15 +339,15 @@
<span class="argument">--keep-foreign-metadata</span>
</td>
<td>
If encoding, save WAVE 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, 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 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, 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 had more than 2 channels and needed remapping to FLAC order</li>
<li>The original WAVE is not spec compliant, e.g. 20 bps in WAVEFORMATEX; restored file will still be a compliant WAVEFORMATEXTENSIBLE</li>
<li>Other wierd 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 WAIV is in AIFF-C form with compression type "sowt" or "NONE"; currently the restored file will always be in AIFF (uncompressed) form</li>
<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>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>
-->
</td>
......@@ -946,7 +953,16 @@
<span class="argument">--force-aiff-format</span>
</td>
<td>
Force the decoder to output AIFF format. This option is not needed if the output filename (as set by -o) ends with .aiff. Also, this option has no effect when encoding since input AIFF is auto-detected.
Force the decoder to output AIFF format. This option is not needed if the output filename (as set by -o) ends with .aif or .aiff. Also, this option has no effect when encoding since input AIFF is auto-detected.
</td>
</tr>
<tr>
<td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
<a name="flac_options_force_rf64_format" />
<span class="argument">--force-rf64-format</span>
</td>
<td>
Force the decoder to output RF64 format. This option is not needed if the output filename (as set by -o) ends with .rf64. Also, this option has no effect when encoding since input RF64 is auto-detected.
</td>
</tr>
<tr>
......@@ -1046,6 +1062,7 @@
<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_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" /><span class="argument">--force</span></a><br />
<a href="#flac_options_explain" /><span class="argument">-H</span></a><br />
......
......@@ -48,6 +48,7 @@
<arg choice=opt><replaceable>OPTIONS</replaceable></arg>
<group rep=repeat>
<arg><replaceable>infile.wav</replaceable></arg>
<arg><replaceable>infile.rf64</replaceable></arg>
<arg><replaceable>infile.aiff</replaceable></arg>
<arg><replaceable>infile.raw</replaceable></arg>
<arg><replaceable>infile.flac</replaceable></arg>
......@@ -217,7 +218,7 @@
<term><option>--keep-foreign-metadata</option>
</term>
<listitem>
<para>If encoding, save WAVE 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).</para>
<para>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).</para>
</listitem>
</varlistentry>
......@@ -655,7 +656,15 @@
<term><option>--force-aiff-format</option></term>
<listitem>
<para>Force the decoder to output AIFF format. This option is not needed if the output filename (as set by -o) ends with <filename>.aiff</filename>. Also, this option has no effect when encoding since input AIFF is auto-detected.</para>
<para>Force the decoder to output AIFF format. This option is not needed if the output filename (as set by -o) ends with <filename>.aif</filename> or <filename>.aiff</filename>. Also, this option has no effect when encoding since input AIFF is auto-detected.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--force-rf64-format</option></term>
<listitem>
<para>Force the decoder to output RF64 format. This option is not needed if the output filename (as set by -o) ends with <filename>.rf64</filename>. Also, this option has no effect when encoding since input RF64 is auto-detected.</para>
</listitem>
</varlistentry>
......
......@@ -49,8 +49,7 @@ typedef struct {
long serial_number;
#endif
FLAC__bool is_aiff_out;
FLAC__bool is_wave_out;
FileFormat format;
FLAC__bool treat_warnings_as_errors;
FLAC__bool continue_through_decode_errors;
FLAC__bool channel_map_none;
......@@ -109,7 +108,7 @@ static FLAC__bool is_big_endian_host_;
/*
* local routines
*/
static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool use_first_serial_number, long serial_number, FLAC__bool is_aiff_out, FLAC__bool is_wave_out, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename);
static FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool use_first_serial_number, long serial_number, FileFormat format, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename);
static void DecoderSession_destroy(DecoderSession *d, FLAC__bool error_occurred);
static FLAC__bool DecoderSession_init_decoder(DecoderSession *d, const char *infilename);
static FLAC__bool DecoderSession_process(DecoderSession *d);
......@@ -121,6 +120,7 @@ static FLAC__bool write_riff_wave_fmt_chunk(FILE *f, FLAC__bool is_waveformatext
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);
static FLAC__bool write_little_endian_uint64(FILE *f, FLAC__uint64 val);
static FLAC__bool write_big_endian_uint16(FILE *f, FLAC__uint16 val);
static FLAC__bool write_big_endian_uint32(FILE *f, FLAC__uint32 val);
static FLAC__bool write_sane_extended(FILE *f, unsigned val);
......@@ -136,123 +136,46 @@ static void print_stats(const DecoderSession *decoder_session);
/*
* public routines
*/
int flac__decode_aiff(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, wav_decode_options_t options)
int flac__decode_file(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, decode_options_t options)
{
DecoderSession decoder_session;
if(!
DecoderSession_construct(
&decoder_session,
#if FLAC__HAS_OGG
options.common.is_ogg,
options.common.use_first_serial_number,
options.common.serial_number,
#else
/*is_ogg=*/false,
/*use_first_serial_number=*/false,
/*serial_number=*/0,
#endif
/*is_aiff_out=*/true,
/*is_wave_out=*/false,
options.common.treat_warnings_as_errors,
options.common.continue_through_decode_errors,
options.common.channel_map_none,
options.common.replaygain_synthesis_spec,
analysis_mode,
aopts,
&options.common.skip_specification,
&options.common.until_specification,
options.common.has_cue_specification? &options.common.cue_specification : 0,
options.foreign_metadata,
infilename,
outfilename
)
)
return 1;
if(!DecoderSession_init_decoder(&decoder_session, infilename))
return DecoderSession_finish_error(&decoder_session);
if(!DecoderSession_process(&decoder_session))
return DecoderSession_finish_error(&decoder_session);
return DecoderSession_finish_ok(&decoder_session);
}
int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, wav_decode_options_t options)
{
DecoderSession decoder_session;
if(!
DecoderSession_construct(
&decoder_session,
#if FLAC__HAS_OGG
options.common.is_ogg,
options.common.use_first_serial_number,
options.common.serial_number,
#else
/*is_ogg=*/false,
/*use_first_serial_number=*/false,
/*serial_number=*/0,
#endif
/*is_aiff_out=*/false,
/*is_wave_out=*/true,
options.common.treat_warnings_as_errors,
options.common.continue_through_decode_errors,
options.common.channel_map_none,
options.common.replaygain_synthesis_spec,
analysis_mode,
aopts,
&options.common.skip_specification,
&options.common.until_specification,
options.common.has_cue_specification? &options.common.cue_specification : 0,
options.foreign_metadata,
infilename,
outfilename
)
)
return 1;
if(!DecoderSession_init_decoder(&decoder_session, infilename))
return DecoderSession_finish_error(&decoder_session);
if(!DecoderSession_process(&decoder_session))
return DecoderSession_finish_error(&decoder_session);
return DecoderSession_finish_ok(&decoder_session);
}
int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, raw_decode_options_t options)
{
DecoderSession decoder_session;
decoder_session.is_big_endian = options.is_big_endian;
decoder_session.is_unsigned_samples = options.is_unsigned_samples;
FLAC__ASSERT(
options.format == FORMAT_WAVE ||
options.format == FORMAT_RF64 ||
options.format == FORMAT_AIFF ||
options.format == FORMAT_AIFF_C ||
options.format == FORMAT_RAW
);
if(options.format == FORMAT_RAW) {
decoder_session.is_big_endian = options.format_options.raw.is_big_endian;
decoder_session.is_unsigned_samples = options.format_options.raw.is_unsigned_samples;
}
if(!
DecoderSession_construct(
&decoder_session,
#if FLAC__HAS_OGG
options.common.is_ogg,
options.common.use_first_serial_number,
options.common.serial_number,
options.is_ogg,
options.use_first_serial_number,
options.serial_number,
#else
/*is_ogg=*/false,
/*use_first_serial_number=*/false,
/*serial_number=*/0,
#endif
/*is_aiff_out=*/false,
/*is_wave_out=*/false,
options.common.treat_warnings_as_errors,
options.common.continue_through_decode_errors,
options.common.channel_map_none,
options.common.replaygain_synthesis_spec,
options.format,
options.treat_warnings_as_errors,
options.continue_through_decode_errors,
options.channel_map_none,
options.replaygain_synthesis_spec,
analysis_mode,
aopts,
&options.common.skip_specification,
&options.common.until_specification,
options.common.has_cue_specification? &options.common.cue_specification : 0,
/*foreign_metadata=*/NULL,
&options.skip_specification,
&options.until_specification,
options.has_cue_specification? &options.cue_specification : 0,
options.format == FORMAT_RAW? NULL : options.format_options.iff.foreign_metadata,
infilename,
outfilename
)
......@@ -268,7 +191,7 @@ int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool
return DecoderSession_finish_ok(&decoder_session);
}
FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool use_first_serial_number, long serial_number, FLAC__bool is_aiff_out, FLAC__bool is_wave_out, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename)
FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__bool use_first_serial_number, long serial_number, FileFormat format, FLAC__bool treat_warnings_as_errors, FLAC__bool continue_through_decode_errors, FLAC__bool channel_map_none, replaygain_synthesis_spec_t replaygain_synthesis_spec, FLAC__bool analysis_mode, analysis_options aopts, utils__SkipUntilSpecification *skip_specification, utils__SkipUntilSpecification *until_specification, utils__CueSpecification *cue_specification, foreign_metadata_t *foreign_metadata, const char *infilename, const char *outfilename)
{
#if FLAC__HAS_OGG
d->is_ogg = is_ogg;
......@@ -280,8 +203,7 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__
(void)serial_number;
#endif
d->is_aiff_out = is_aiff_out;
d->is_wave_out = is_wave_out;
d->format = format;
d->treat_warnings_as_errors = treat_warnings_as_errors;
d->continue_through_decode_errors = continue_through_decode_errors;
d->channel_map_none = channel_map_none;
......@@ -361,13 +283,11 @@ FLAC__bool DecoderSession_init_decoder(DecoderSession *decoder_session, const ch
is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true;
if(!decoder_session->analysis_mode && !decoder_session->test_only && (decoder_session->is_wave_out || decoder_session->is_aiff_out)) {
if(decoder_session->foreign_metadata) {
const char *error;
if(!flac__foreign_metadata_read_from_flac(decoder_session->foreign_metadata, infilename, &error)) {
flac__utils_printf(stderr, 1, "%s: ERROR reading foreign metadata: %s\n", decoder_session->inbasefilename, error);
return false;
}
if(!decoder_session->analysis_mode && !decoder_session->test_only && decoder_session->foreign_metadata) {
const char *error;
if(!flac__foreign_metadata_read_from_flac(decoder_session->foreign_metadata, infilename, &error)) {
flac__utils_printf(stderr, 1, "%s: ERROR reading foreign metadata: %s\n", decoder_session->inbasefilename, error);
return false;
}
}
......@@ -452,7 +372,7 @@ FLAC__bool DecoderSession_process(DecoderSession *d)
}
/* write the WAVE/AIFF headers if necessary */
if(!d->analysis_mode && !d->test_only && (d->is_wave_out || d->is_aiff_out)) {
if(!d->analysis_mode && !d->test_only && d->format != FORMAT_RAW) {
if(!write_iff_headers(d->fout, d, d->total_samples)) {
d->abort_flag = true;
return false;
......@@ -482,9 +402,9 @@ FLAC__bool DecoderSession_process(DecoderSession *d)
return false;
}
if(!d->analysis_mode && !d->test_only && (d->is_wave_out || d->is_aiff_out) && ((d->total_samples * d->channels * ((d->bps+7)/8)) & 1)) {
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->is_wave_out?
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"
);
......@@ -522,7 +442,7 @@ int DecoderSession_finish_ok(DecoderSession *d)
flac__utils_printf(stderr, 2, "\r%s: %s \n", d->inbasefilename, d->test_only? "ok ":d->analysis_mode?"done ":"done");
}
DecoderSession_destroy(d, /*error_occurred=*/!ok);
if(!d->analysis_mode && !d->test_only && (d->is_wave_out || d->is_aiff_out)) {
if(!d->analysis_mode && !d->test_only && d->format != FORMAT_RAW) {
if(d->iff_headers_need_fixup || (!d->got_stream_info && strcmp(d->outfilename, "-"))) {
if(!fixup_iff_headers(d))
return 1;
......@@ -597,15 +517,20 @@ FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec,
FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uint64 samples)
{
const char *fmt_desc = decoder_session->is_wave_out? "WAVE" : "AIFF";
const FLAC__bool is_waveformatextensible = decoder_session->is_wave_out && (decoder_session->channel_mask == 2 || decoder_session->channel_mask > 3 || decoder_session->bps%8 || decoder_session->channels > 2);
FLAC__uint64 data_size = samples * decoder_session->channels * ((decoder_session->bps+7)/8);
const FLAC__uint32 aligned_data_size = (FLAC__uint32)((data_size+1) & (~1U)); /* we'll check for overflow later */
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 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;
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));
if(samples == 0) {
if(f == stdout) {
flac__utils_printf(stderr, 1, "%s: WARNING, don't have accurate sample count available for %s header.\n", decoder_session->inbasefilename, fmt_desc);
......@@ -623,33 +548,70 @@ 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, format chunk, and sound chunk since we write our own */
for(i = 1; i < fm->num_blocks; i++) {
/* 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 */
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;
}
}
if(data_size + foreign_metadata_size + 60/*worst-case*/ >= 0xFFFFFFF4) {
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
/* @@@@@@ 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) {
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(decoder_session->is_wave_out) {
if(flac__utils_fwrite("RIFF", 1, 4, f) != 4)
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, foreign_metadata_size + aligned_data_size + (is_waveformatextensible?60:36))) /* filesize-8 */
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 */
return false;
}
if(flac__utils_fwrite("WAVE", 1, 4, f) != 4)
return false;
if(format == FORMAT_RF64) {
if(flac__utils_fwrite("ds64", 1, 4, f) != 4)
return false;
if(!write_little_endian_uint32(f, 28)) /* chunk size */
return false;
if(!write_little_endian_uint64(f, iff_size))
return false;
if(!write_little_endian_uint64(f, data_size))
return false;
if(!write_little_endian_uint64(f, samples)) /*@@@@@@ correct? */
return false;
if(!write_little_endian_uint32(f, 0)) /* table size */
return false;
}
decoder_session->fm_offset1 = ftello(f);
if(fm) {
/* seek forward to {allocate} or {skip over already-written chunks} before "fmt " */
for(i = 1; i < fm->format_block; i++) {
for(i = format==FORMAT_RF64?2:1; i < fm->format_block; i++) {
if(fseeko(f, fm->blocks[i].size, SEEK_CUR) < 0) {
flac__utils_printf(stderr, 1, "%s: ERROR: allocating/skipping foreign metadata before \"fmt \"\n", decoder_session->inbasefilename);
return false;
......@@ -675,7 +637,7 @@ 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, (FLAC__uint32)data_size)) /* data size */
if(!write_little_endian_uint32(f, format==FORMAT_RF64? 0xffffffff : (FLAC__uint32)data_size))
return false;
decoder_session->fm_offset3 = ftello(f) + aligned_data_size;
......@@ -686,7 +648,7 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin
if(flac__utils_fwrite("FORM", 1, 4, f) != 4)
return false;
if(!write_big_endian_uint32(f, foreign_metadata_size + aligned_data_size + 46 + ssnd_offset_size)) /* filesize-8 */
if(!write_big_endian_uint32(f, (FLAC__uint32)iff_size)) /* filesize-8 */
return false;
if(flac__utils_fwrite("AIFF", 1, 4, f) != 4)
......@@ -835,6 +797,19 @@ FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val)
return flac__utils_fwrite(b, 1, 4, f) == 4;
}
FLAC__bool write_little_endian_uint64(FILE *f, FLAC__uint64 val)
{
FLAC__byte *b = (FLAC__byte*)(&val);
if(is_big_endian_host_) {
FLAC__byte tmp;
tmp = b[7]; b[7] = b[0]; b[0] = tmp;
tmp = b[6]; b[6] = b[1]; b[1] = tmp;
tmp = b[5]; b[5] = b[2]; b[2] = tmp;
tmp = b[4]; b[4] = b[3]; b[3] = tmp;
}
return flac__utils_fwrite(b, 1, 8, f) == 8;
}
FLAC__bool write_big_endian_uint16(FILE *f, FLAC__uint16 val)
{
FLAC__byte *b = (FLAC__byte*)(&val);
......@@ -889,7 +864,7 @@ FLAC__bool write_sane_extended(FILE *f, unsigned val)
FLAC__bool fixup_iff_headers(DecoderSession *d)
{
const char *fmt_desc = (d->is_wave_out? "WAVE" : "AIFF");
const char *fmt_desc = d->format==FORMAT_WAVE? "WAVE" : d->format==FORMAT_RF64? "RF64" : "AIFF";
FILE *f = fopen(d->outfilename, "r+b"); /* stream is positioned at beginning of file */
if(0 == f) {
......@@ -911,9 +886,17 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
DecoderSession *decoder_session = (DecoderSession*)client_data;
FILE *fout = decoder_session->fout;
const unsigned bps = frame->header.bits_per_sample, channels = frame->header.channels;
const unsigned shift = ((decoder_session->is_wave_out || decoder_session->is_aiff_out) && (bps%8)? 8-(bps%8): 0);
FLAC__bool is_big_endian = (decoder_session->is_aiff_out? true : (decoder_session->is_wave_out? false : decoder_session->is_big_endian));
FLAC__bool is_unsigned_samples = (decoder_session->is_aiff_out? false : (decoder_session->is_wave_out? bps<=8 : decoder_session->is_unsigned_samples));
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->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->is_unsigned_samples
));
unsigned wide_samples = frame->header.blocksize, wide_sample, sample, channel, byte;
unsigned frame_bytes = 0;
static FLAC__int8 s8buffer[FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS * sizeof(FLAC__int32)]; /* WATCHOUT: can be up to 2 megs */
......
......@@ -51,24 +51,20 @@ typedef struct {
FLAC__bool has_cue_specification;
utils__CueSpecification cue_specification;
FLAC__bool channel_map_none; /* --channel-map=none specified, eventually will expand to take actual channel map */
} decode_options_t;
/* used for AIFF also */
typedef struct {
decode_options_t common;
foreign_metadata_t *foreign_metadata; /* NULL unless --keep-foreign-metadata requested */
} wav_decode_options_t;
typedef struct {
decode_options_t common;
FLAC__bool is_big_endian;
FLAC__bool is_unsigned_samples;
} raw_decode_options_t;
FileFormat format;
union {
struct {
FLAC__bool is_big_endian;
FLAC__bool is_unsigned_samples;
} raw;
struct {
foreign_metadata_t *foreign_metadata; /* NULL unless --keep-foreign-metadata requested */
} iff;
} format_options;
} decode_options_t;
/* outfile == 0 => test only */
int flac__decode_aiff(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, wav_decode_options_t options);
int flac__decode_wav(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, wav_decode_options_t options);
int flac__decode_raw(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, raw_decode_options_t options);
int flac__decode_file(const char *infilename, const char *outfilename, FLAC__bool analysis_mode, analysis_options aopts, decode_options_t options);
#endif
......@@ -54,6 +54,7 @@
#define max(x,y) ((x)>(y)?(x):(y))
/* this MUST be >= 588 so that sector aligning can take place with one read */
/* this MUST be < 2^sizeof(size_t) / ( FLAC__MAX_CHANNELS * (FLAC__MAX_BITS_PER_SAMPLE/8) ) */
#define CHUNK_OF_SAMPLES 2048