<li>Much better recovery for corrupted files</li>
<li>Better multichannel support</li>
<li>Large file (>2GB) support everywhere</li>
- <li><span class="commandname">flac</span> now supports FLAC as input to the encoder (i.e. can re-encode FLAC to FLAC) and preserve all the metadata like tags, etc.</li>
+ <li><span class="commandname">flac</span> now supports FLAC and Ogg FLAC as input to the encoder (i.e. can re-encode FLAC to FLAC) and preserve all the metadata like tags, etc.</li>
<li>New <span class="code"><a href="format.html#def_PICTURE">PICTURE</a></span> metadata block for storing things like cover art, new <span class="argument"><a href="documentation.html#flac_options_picture">--picture</a></span> option to <span class="commandname">flac</span> and <span class="argument"><a href="documentation.html#metaflac_shorthand_import_picture_from">--import-picture-from</a></span> option to <span class="commandname">metaflac</span> for importing pictures, new <span class="argument"><a href="documentation.html#metaflac_shorthand_export_picture_to">--export-picture-to</a></span> option to <span class="commandname">metaflac</span> for exporting pictures, and metadata API <a href="api/group__flac__metadata__level0.html#ga3">additions</a> for searching for suitable pictures based on type, size and color constraints.</li>
<li>Support for new <tt>REPLAYGAIN_REFERENCE_LOUDNESS</tt> tag.</li>
<li>In the developer libraries, the interface has been simplfied by merging the three decoding layers into a single class; ditto for the encoders. Also, libOggFLAC has been merged into libFLAC and libOggFLAC++ has been merged into libFLAC++ so there is a single API supporting both native FLAC and Ogg FLAC.</li>
flac:
<ul>
<li>Improved the <span class="argument"><a href="documentation.html#flac_options_decode_through_errors">-F</a></span> option to allow decoding of FLAC files whose metadata is corrupted, and other kinds of severe corruption.</li>
- <li>Encoder can now take FLAC as input. The output FLAC file will have all the same metadata as the original unless overridden with options on the command line.</li>
+ <li>Encoder can now take FLAC and Ogg FLAC as input. The output FLAC file will have all the same metadata as the original unless overridden with options on the command line.</li>
<li>Encoder can now take WAVEFORMATEXTENSIBLE WAVE files as input; decoder will output WAVEFORMATEXTENSIBLE WAVE files when necessary to conform to the latest Microsoft specifications.</li>
<li>Now properly supports AIFF and WAVEFORMATEXTENSIBLE multichannel input, performing necessary channel reordering both for encoding and decoding. WAVEFORMATEXTENSIBLE channel mask is also saved to a tag on encoding and restored on decoding for situations when there is no natural mapping to FLAC channel assignments.</li>
<li>Expanded support for "odd" sample resolutions to WAVE and AIFF input; all resolutions from 4 to 24 bits-per-sample now supported for all input types.</li>
</div>
<div class="box_header"></div>
<div class="box_body">
- <span class="commandname">flac</span> is the command-line file encoder/decoder. The encoder currently supports as input RIFF WAVE, AIFF, or 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.
+ <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 /><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 ".ogg" 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 file systems like FAT-16).
+ <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 ".ogg" of 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 />
<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 />
- <span class="commandname">flac</span> can also re-encode FLAC files. In other words, you can specify a FLAC file as an input to the encoder and it will decoder it and re-encode it according to the options you specify. It will also preserve all the metadata unless you override it with other options (e.g. specifying new tags, seekpoints, cuesheet, padding, etc.).
+ <span class="commandname">flac</span> can also re-encode FLAC files. In other words, you can specify a FLAC or Ogg FLAC file as an input to the encoder and it will decoder it and re-encode it according to the options you specify. It will also preserve all the metadata unless you override it with other options (e.g. specifying new tags, seekpoints, cuesheet, padding, etc.).
<br /><br />
<table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#EEEED4"><tr><td>
<refsynopsisdiv>
<cmdsynopsis>
<command>flac</command>
- <arg choice=opt><replaceable>options</replaceable></arg>
+ <arg choice=opt><replaceable>OPTIONS</replaceable></arg>
<group rep=repeat>
<arg><replaceable>infile.wav</replaceable></arg>
<arg><replaceable>infile.aiff</replaceable></arg>
<arg><replaceable>infile.raw</replaceable></arg>
<arg><replaceable>infile.flac</replaceable></arg>
+ <arg><replaceable>infile.ogg</replaceable></arg>
<arg>-</arg>
</group>
</cmdsynopsis>
<arg>-a</arg> <arg>--analyze</arg>
</group>
<arg choice=opt><replaceable>OPTIONS</replaceable></arg>
- <arg choice=opt rep=repeat><replaceable>infile.flac</replaceable></arg>
+ <group rep=repeat>
+ <arg><replaceable>infile.flac</replaceable></arg>
+ <arg><replaceable>infile.ogg</replaceable></arg>
+ <arg>-</arg>
+ </group>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
return EncoderSession_finish_ok(&encoder_session, info_align_carry, info_align_zero);
}
-int flac__encode_flac(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, flac_encode_options_t options)
+int flac__encode_flac(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, flac_encode_options_t options, FLAC__bool input_is_ogg)
{
EncoderSession encoder_session;
FLAC__StreamDecoder *decoder = 0;
goto fubar1; /*@@@ yuck */
}
- if (FLAC__stream_decoder_init_stream(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=*/&decoder_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+ if (input_is_ogg) {
+ if (FLAC__stream_decoder_init_ogg_stream(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=*/&decoder_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+ flac__utils_printf(stderr, 1, "%s: ERROR: initializing decoder for Ogg FLAC input, state = %s\n", encoder_session.inbasefilename, FLAC__stream_decoder_get_resolved_state_string(decoder));
+ goto fubar1; /*@@@ yuck */
+ }
+ }
+ else if (FLAC__stream_decoder_init_stream(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=*/&decoder_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
flac__utils_printf(stderr, 1, "%s: ERROR: initializing decoder for FLAC input, state = %s\n", encoder_session.inbasefilename, FLAC__stream_decoder_get_resolved_state_string(decoder));
goto fubar1; /*@@@ yuck */
}
/* get the rest from file */
if (*bytes > n) {
*bytes = n + fread(buffer, 1, *bytes-n, data->encoder_session->fin);
- return ferror(data->encoder_session->fin)? FLAC__STREAM_DECODER_READ_STATUS_ABORT : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ if(ferror(data->encoder_session->fin))
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ else if(0 == *bytes && feof(data->encoder_session->fin))
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
else
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
int flac__encode_aif(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options, FLAC__bool is_aifc);
int flac__encode_wav(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, wav_encode_options_t options);
int flac__encode_raw(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, raw_encode_options_t options);
-int flac__encode_flac(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, flac_encode_options_t options);
+int flac__encode_flac(FILE *infile, off_t infilesize, const char *infilename, const char *outfilename, const FLAC__byte *lookahead, unsigned lookahead_length, flac_encode_options_t options, FLAC__bool input_is_ogg);
#endif
# include "share/getopt.h"
#endif
-typedef enum { RAW, WAV, AIF, FLAC } FileFormat;
+typedef enum { RAW, WAV, AIF, FLAC, OGGFLAC } FileFormat;
static int do_it();
fmt= AIF;
else if(strlen(infilename) >= 5 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-5), ".flac"))
fmt= FLAC;
+ else if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".ogg"))
+ fmt= OGGFLAC;
/* attempt to guess the file type based on the first 12 bytes */
if((lookahead_length = fread(lookahead, 1, 12, encode_infile)) < 12) {
}
else if(!memcmp(lookahead, FLAC__STREAM_SYNC_STRING, sizeof(FLAC__STREAM_SYNC_STRING)))
fmt= FLAC;
+ /* this could be made more accurate by looking at the first packet */
+ else if(!memcmp(lookahead, "OggS", 4))
+ fmt= OGGFLAC;
else {
if(fmt != RAW)
format_mistake(infilename, fmt == AIF ? "AIFF" : "WAVE", "raw");
outfilename
);
}
+ else if(fmt == OGGFLAC) {
+ /* need more detailed error message when re-flac'ing to avoid confusing the user */
+ flac__utils_printf(stderr, 1,
+ "ERROR: output file %s already exists.\n\n"
+ "By default 'flac -ogg' encodes files to Ogg FLAC format; if you meant to decode\n"
+ "this file from Ogg FLAC to something else, use -d. If you meant to re-encode\n"
+ "this file from Ogg FLAC to Ogg FLAC again, use -f to force writing to the same\n"
+ "file, or -o to specify a different output filename.\n",
+ outfilename
+ );
+ }
else
flac__utils_printf(stderr, 1, "ERROR: output file %s already exists, use -f to override\n", outfilename);
return 1;
}
}
- if(option_values.sector_align && fmt == FLAC) {
- flac__utils_printf(stderr, 1, "ERROR: can't use --sector-align when the input file is FLAC\n");
+ if(option_values.sector_align && (fmt == FLAC || fmt == OGGFLAC)) {
+ flac__utils_printf(stderr, 1, "ERROR: can't use --sector-align when the input file is FLAC or Ogg FLAC\n");
return 1;
}
if(option_values.sector_align && fmt == RAW && infilesize < 0) {
retval = flac__encode_raw(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options);
}
- else if(fmt == FLAC) {
+ else if(fmt == FLAC || fmt == OGGFLAC) {
flac_encode_options_t options;
options.common = common_options;
- retval = flac__encode_flac(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options);
+ retval = flac__encode_flac(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options, fmt==OGGFLAC);
}
else {
wav_encode_options_t options;
rm -f rt.wav rt.flac rt2.flac
}
+# assumes input file is WAVE; does not check the metadata-preserving features of flac-to-flac; that is checked later
+rt_test_ogg_flac ()
+{
+ f="$1"
+ echo -n "round-trip test ($f->oggflac->oggflac->wav) encode... "
+ run_flac $SILENT --force --verify --channel-map=none --lax -o rt.ogg --ogg $f || die "ERROR"
+ echo -n "re-encode... "
+ run_flac $SILENT --force --verify --lax -o rt2.ogg --ogg rt.ogg || die "ERROR"
+ echo -n "decode... "
+ run_flac $SILENT --force --decode --channel-map=none -o rt.wav rt2.ogg || die "ERROR"
+ echo -n "compare... "
+ cmp $f rt.wav || die "ERROR: file mismatch"
+ echo "OK"
+ rm -f rt.wav rt.ogg rt2.ogg
+}
+
for f in rt-*.raw ; do
rt_test_raw $f
done
for f in rt-*.wav ; do
rt_test_flac $f
done
+if [ $has_ogg = yes ] ; then
+ for f in rt-*.wav ; do
+ rt_test_ogg_flac $f
+ done
+fi
############################################################################
# test --skip and --until
raw_dopt="$wav_dopt --force-raw-format --endian=big --sign=signed"
#
-# convert them to WAVE and AIFF files
+# convert them to WAVE/AIFF/Ogg FLAC files
#
convert_to_wav ()
{
convert_to_aiff 50c.skip20.until30 "$raw_eopt" "$wav_dopt"
convert_to_aiff 50c.skip20.until40 "$raw_eopt" "$wav_dopt"
+convert_to_ogg ()
+{
+ run_flac "$wav_eopt" --ogg $1.wav || die "ERROR converting $1.raw to Ogg FLAC"
+}
+if [ $has_ogg = yes ] ; then
+ convert_to_ogg 50c
+ convert_to_ogg 50c.skip10
+ convert_to_ogg 50c.skip11
+ convert_to_ogg 50c.skip20
+ convert_to_ogg 50c.skip30
+ convert_to_ogg 50c.skip40
+ convert_to_ogg 50c.until10
+ convert_to_ogg 50c.until20
+ convert_to_ogg 50c.until30
+ convert_to_ogg 50c.until39
+ convert_to_ogg 50c.until40
+ convert_to_ogg 50c.skip10.until30
+ convert_to_ogg 50c.skip10.until39
+ convert_to_ogg 50c.skip10.until40
+ convert_to_ogg 50c.skip20.until30
+ convert_to_ogg 50c.skip20.until40
+fi
+
test_skip_until ()
{
in_fmt=$1
out_fmt=$2
- [ "$in_fmt" = wav ] || [ "$in_fmt" = aiff ] || [ "$in_fmt" = raw ] || [ "$in_fmt" = flac ] || die "ERROR: internal error, bad 'in' format '$in_fmt'"
+ [ "$in_fmt" = wav ] || [ "$in_fmt" = aiff ] || [ "$in_fmt" = raw ] || [ "$in_fmt" = flac ] || [ "$in_fmt" = ogg ] || die "ERROR: internal error, bad 'in' format '$in_fmt'"
[ "$out_fmt" = flac ] || [ "$out_fmt" = ogg ] || die "ERROR: internal error, bad 'out' format '$out_fmt'"
dopt="$wav_dopt"
fi
- if [ $in_fmt = flac ] && [ $out_fmt = flac ] ; then
+ if ( [ $in_fmt = flac ] || [ $in_fmt = ogg ] ) && ( [ $out_fmt = flac ] || [ $out_fmt = ogg ] ) ; then
CMP=md5cmp
else
CMP=cmp
test_skip_until wav flac
test_skip_until aiff flac
test_skip_until flac flac
+#@@@if [ $has_ogg = yes ] ; then
+#@@@ #@@@ doesn't work yet because md5cmp doesn't work because metaflac doesn't work on ogg flac yet
+#@@@ test_skip_until ogg flac
+#@@@fi
-if [ $has_ogg = "yes" ] ; then
+if [ $has_ogg = yes ] ; then
test_skip_until raw ogg
test_skip_until wav ogg
test_skip_until aiff ogg
- #@@@ doesn't work yet, no comparison step written since metaflac doesn't work on ogg flac yet
+ #@@@ doesn't work yet because md5cmp doesn't work because metaflac doesn't work on ogg flac yet
#@@@test_skip_until flac ogg
+ #@@@test_skip_until ogg ogg
fi
echo "testing seek extremes:"
in_fmt=$1
out_fmt=$2
- [ "$in_fmt" = wav ] || [ "$in_fmt" = aiff ] || [ "$in_fmt" = raw ] || [ "$in_fmt" = flac ] || die "ERROR: internal error, bad 'in' format '$in_fmt'"
+ [ "$in_fmt" = wav ] || [ "$in_fmt" = aiff ] || [ "$in_fmt" = raw ] || [ "$in_fmt" = flac ] || [ "$in_fmt" = ogg ] || die "ERROR: internal error, bad 'in' format '$in_fmt'"
[ "$out_fmt" = flac ] || [ "$out_fmt" = ogg ] || die "ERROR: internal error, bad 'out' format '$out_fmt'"
dopt="$wav_dopt"
fi
- if [ $in_fmt = flac ] && [ $out_fmt = flac ] ; then
+ if ( [ $in_fmt = flac ] || [ $in_fmt = ogg ] ) && ( [ $out_fmt = flac ] || [ $out_fmt = ogg ] ) ; then
CMP=md5cmp
else
CMP=cmp
test_cue wav flac
test_cue aiff flac
test_cue flac flac
+#@@@if [ $has_ogg = yes ] ; then
+#@@@ #@@@ doesn't work yet because md5cmp doesn't work because metaflac doesn't work on ogg flac yet
+#@@@ test_cue ogg flac
+#@@@fi
-if [ $has_ogg = "yes" ] ; then
+if [ $has_ogg = yes ] ; then
test_cue raw ogg
test_cue wav ogg
test_cue aiff ogg
- #@@@ doesn't work yet, no comparison step written since metaflac doesn't work on ogg flac yet
+ #@@@ doesn't work yet because md5cmp doesn't work because metaflac doesn't work on ogg flac yet
#@@@test_cue flac ogg
+ #@@@test_cue ogg ogg
fi
############################################################################
encode_options="$encode_options --sector-align"
fi
- if [ $input_type = flac ] ; then
+ if [ $input_type = flac ] || [ $input_type = ogg ] ; then
CMP=md5cmp
else
CMP=cmp
done
}
-for input_type in raw wav aiff flac ; do
+input_types="raw wav aiff flac"
+#@@@ doesn't work yet because md5cmp doesn't work because metaflac doesn't work on ogg flac yet
+#@@@if [ $has_ogg = yes ] ; then
+#@@@ input_types="$input_types ogg"
+#@@@fi
+for input_type in $input_types ; do
echo "Testing multiple $input_type files without verify..."
test_multifile $input_type flac no_sector_align ""
echo "Testing multiple $input_type files with verify..."
test_multifile $input_type flac no_sector_align "--verify"
- if [ $input_type != flac ] ; then # --sector-align not supported for FLAC input
+ if [ $input_type != flac ] && [ $input_type != ogg ] ; then # --sector-align not supported for FLAC input
echo "Testing multiple $input_type files with --sector-align, without verify..."
test_multifile $input_type flac sector_align ""
test_multifile $input_type flac sector_align "--verify"
fi
- if [ $has_ogg = "yes" ] ; then
+ if [ $has_ogg = yes ] ; then
echo "Testing multiple $input_type files with --ogg, without verify..."
test_multifile $input_type ogg no_sector_align ""
rm -f out.flac out.meta
+#@@@ when metaflac handles ogg flac, duplicate flac2flac tests here
+
cd ..