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

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>
......
This diff is collapsed.
......@@ -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>
......
This diff is collapsed.
......@@ -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
This diff is collapsed.
This diff is collapsed.
......@@ -27,21 +27,30 @@
#include "utils.h"
/* WATCHOUT: these enums are used to index internal arrays */
typedef enum { FOREIGN_BLOCK_TYPE__AIFF = 0, FOREIGN_BLOCK_TYPE__RIFF = 1 } foreign_block_type_t;
typedef enum {
FOREIGN_BLOCK_TYPE__AIFF = 0, /* for AIFF and AIFF-C */
FOREIGN_BLOCK_TYPE__RIFF = 1 /* for WAVE and RF64 */
} foreign_block_type_t;
typedef struct {
/* for encoding, this will be the offset in the WAVE/AIFF file of the chunk */
/* for decoding, this will be the offset in the FLAC file of the chunk data inside the APPLICATION block */
off_t offset;
/* size is the actual size in bytes of the chunk to be stored/recreated. */
/* It includes the 8 bytes of chunk type and size, and any padding byte for alignment. */
/* 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 */
FLAC__uint32 size;
} foreign_block_t;
typedef struct {
foreign_block_type_t type; /* currently we don't support multiple foreign types in a stream (an maybe never will) */
foreign_block_type_t type; /* currently we don't support multiple foreign types in a stream (and maybe never will) */
foreign_block_t *blocks;
size_t num_blocks;
size_t format_block; /* block number of 'fmt ' or 'COMM' chunk */
size_t audio_block; /* block number of 'data' or 'SSND' chunk */
FLAC__bool is_rf64; /* always false if type!=RIFF */
FLAC__uint32 ssnd_offset_size; /* 0 if type!=AIFF */
} foreign_metadata_t;
......
......@@ -42,6 +42,11 @@ static FLAC__uint32 unpack32le_(const FLAC__byte *b)
return (FLAC__uint32)b[0] + ((FLAC__uint32)b[1]<<8) + ((FLAC__uint32)b[2]<<16) + ((FLAC__uint32)b[3]<<24);
}
static FLAC__uint64 unpack64le_(const FLAC__byte *b)
{
return (FLAC__uint64)b[0] + ((FLAC__uint64)b[1]<<8) + ((FLAC__uint64)b[2]<<16) + ((FLAC__uint64)b[3]<<24) + ((FLAC__uint64)b[4]<<32) + ((FLAC__uint64)b[5]<<40) + ((FLAC__uint64)b[6]<<48) + ((FLAC__uint64)b[7]<<56);
}
static FLAC__uint32 unpack32_(const FLAC__byte *b, foreign_block_type_t type)
{
if(type == FOREIGN_BLOCK_TYPE__AIFF)
......@@ -53,7 +58,7 @@ static FLAC__uint32 unpack32_(const FLAC__byte *b, foreign_block_type_t type)
int main(int argc, char *argv[])
{
FILE *f;
char buf[12];
char buf[36];
foreign_metadata_t *fm;
const char *fn, *error;
size_t i;
......@@ -69,7 +74,7 @@ int main(int argc, char *argv[])
return 1;
}
fclose(f);
if(0 == (fm = flac__foreign_metadata_new(memcmp(buf, "RIFF", 4)? FOREIGN_BLOCK_TYPE__AIFF : FOREIGN_BLOCK_TYPE__RIFF))) {
if(0 == (fm = flac__foreign_metadata_new(memcmp(buf, "RIFF", 4) && memcmp(buf, "RF64", 4)? FOREIGN_BLOCK_TYPE__AIFF : FOREIGN_BLOCK_TYPE__RIFF))) {
fprintf(stderr, "ERROR: out of memory\n");
return 1;
}
......@@ -94,7 +99,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "ERROR seeking in %s\n", fn);
return 1;
}
if(fread(buf, 1, 12, f) != 12) {
if(fread(buf, 1, i==0?12:8, f) != (i==0?12:8)) {
fprintf(stderr, "ERROR reading %s\n", fn);
return 1;
}
......@@ -104,6 +109,22 @@ int main(int argc, char *argv[])
printf(" type:[%c%c%c%c]", buf[8], buf[9], buf[10], buf[11]);
else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF && i == fm->audio_block)
printf(" offset size=%08x=(%10u)", fm->ssnd_offset_size, fm->ssnd_offset_size);
else if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && i == 1 && !memcmp(buf, "ds64", 4)) {
if(fread(buf+8, 1, 36-8, f) != 36-8) {
fprintf(stderr, "ERROR reading %s\n", fn);
return 1;
}
#ifdef _MSC_VER
printf(" RIFF size=%016I64x=(%I64u)", unpack64le_(buf+8), unpack64le_(buf+8));
printf(" data size=%016I64x=(%I64u)", unpack64le_(buf+16), unpack64le_(buf+16));
printf(" sample count=%016I64x=(%I64u)", unpack64le_(buf+24), unpack64le_(buf+24));
#else
printf(" RIFF size=%016llx=(%llu)", unpack64le_(buf+8), unpack64le_(buf+8));
printf(" data size=%016llx=(%llu)", unpack64le_(buf+16), unpack64le_(buf+16));
printf(" sample count=%016llx=(%llu)", unpack64le_(buf+24), unpack64le_(buf+24));
#endif
printf(" table size=%08x=(%u)", unpack32le_(buf+32), unpack32le_(buf+32));
}
printf("\n");
}
fclose(f);
......
This diff is collapsed.
......@@ -33,6 +33,16 @@ 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;
......
......@@ -27,7 +27,10 @@
#include "FLAC/format.h" /* for FLAC__StreamMetadata_CueSheet */
#include <stdio.h> /* for FILE */
typedef enum { FORMAT_RAW, FORMAT_WAVE, FORMAT_AIFF, FORMAT_AIFF_C, FORMAT_FLAC, FORMAT_OGGFLAC } FileFormat;
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 struct {
FLAC__bool is_relative; /* i.e. specification string started with + or - */
......
......@@ -101,6 +101,20 @@ static FLAC__bool write_little_endian_int32(FILE *f, FLAC__int32 x)
}
#endif
static FLAC__bool write_little_endian_uint64(FILE *f, FLAC__uint64 x)
{
return
fputc(x, f) != EOF &&
fputc(x >> 8, f) != EOF &&
fputc(x >> 16, f) != EOF &&
fputc(x >> 24, f) != EOF &&
fputc(x >> 32, f) != EOF &&
fputc(x >> 40, f) != EOF &&
fputc(x >> 48, f) != EOF &&
fputc(x >> 56, f) != EOF
;
}
static FLAC__bool write_big_endian(FILE *f, FLAC__int32 x, size_t bytes)
{
if(bytes < 4)
......@@ -668,7 +682,7 @@ foo:
return false;
}
static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples, FLAC__bool strict)
static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples, FLAC__bool strict, FLAC__bool rf64)
{
const FLAC__bool waveformatextensible = strict && (channels > 2 || (bps%8));
/* ^^^^^^^
......@@ -689,11 +703,27 @@ static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsig
if(0 == (f = fopen(filename, "wb")))
return false;
if(fwrite("RIFF", 1, 4, f) < 4)
if(fwrite(rf64?"RF64":"RIFF", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, padded_size + (waveformatextensible?60:36)))
if(!write_little_endian_uint32(f, rf64? 0xffffffff : padded_size + (waveformatextensible?60:36)))
goto foo;
if(fwrite("WAVEfmt ", 1, 8, f) < 8)
if(fwrite("WAVE", 1, 4, f) < 4)
goto foo;
if(rf64) {
if(fwrite("ds64", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, 28)) /* ds64 chunk size */
goto foo;
if(!write_little_endian_uint64(f, 36 + padded_size + (waveformatextensible?60:36)))
goto foo;
if(!write_little_endian_uint64(f, true_size))
goto foo;
if(!write_little_endian_uint64(f, samples))
goto foo;
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;
......@@ -722,7 +752,7 @@ static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsig
}
if(fwrite("data", 1, 4, f) < 4)
goto foo;
if(!write_little_endian_uint32(f, true_size))
if(!write_little_endian_uint32(f, rf64? 0xffffffff : true_size))
goto foo;
for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) {
......@@ -749,12 +779,12 @@ static FLAC__bool generate_wackywavs(void)
FILE *f;
FLAC__byte wav[] = {
'R', 'I', 'F', 'F', 76, 0, 0, 0,
'W', 'A', 'V', 'E', 'f', 'a', 'c', 't',
'W', 'A', 'V', 'E', 'j', 'u', 'n', 'k',
4, 0, 0, 0 , 'b', 'l', 'a', 'h',
'p', 'a', 'd', ' ', 4, 0, 0, 0,
'B', 'L', 'A', 'H', 'f', 'm', 't', ' ',
16, 0, 0, 0, 1, 0, 1, 0,
0x44,0xAC, 0, 0, 0, 0, 0, 0,
0x44,0xAC, 0, 0,0x88,0x58,0x01, 0,
2, 0, 16, 0, 'd', 'a', 't', 'a',
16, 0, 0, 0, 0, 0, 1, 0,
4, 0, 9, 0, 16, 0, 25, 0,
......@@ -781,6 +811,48 @@ foo:
return false;
}
static FLAC__bool generate_wackyrf64s(void)
{
FILE *f;
FLAC__byte wav[] = {
'R', 'F', '6', '4', 255, 255, 255, 255,
'W', 'A', 'V', 'E', 'd', 's', '6', '4',
28, 0, 0, 0, 112, 0, 0, 0,
0, 0, 0, 0, 16, 0, 0, 0,
0, 0, 0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
'j', 'u', 'n', 'k',
4, 0, 0, 0, 'b', 'l', 'a', 'h',
'p', 'a', 'd', ' ', 4, 0, 0, 0,
'B', 'L', 'A', 'H', 'f', 'm', 't', ' ',
16, 0, 0, 0, 1, 0, 1, 0,
0x44,0xAC, 0, 0,0x88,0x58,0x01, 0,
2, 0, 16, 0, 'd', 'a', 't', 'a',
255, 255, 255, 255, 0, 0, 1, 0,
4, 0, 9, 0, 16, 0, 25, 0,
36, 0, 49, 0, 'p', 'a', 'd', ' ',
4, 0, 0, 0, 'b', 'l', 'a', 'h'
};
if(0 == (f = fopen("wacky1.rf64", "wb")))
return false;
if(fwrite(wav, 1, 120, f) < 120)
goto foo;
fclose(f);
wav[20] += 12;
if(0 == (f = fopen("wacky2.rf64", "wb")))
return false;
if(fwrite(wav, 1, 132, f) < 132)
goto foo;
fclose(f);
return true;
foo:
fclose(f);
return false;
}
int main(int argc, char *argv[])
{
FLAC__uint32 test = 1;
......@@ -898,6 +970,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_wackyrf64s()) return 1;
for(channels = 1; channels <= 8; channels++) {
unsigned bits_per_sample;
for(bits_per_sample = 4; bits_per_sample <= 24; bits_per_sample++) {
......@@ -911,7 +984,11 @@ 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))
if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true, /*rf64=*/false))
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))
return 1;
if(bits_per_sample % 8 == 0) {
......
......@@ -45,7 +45,8 @@ EXTRA_DIST = \
test_grabbag.sh \
test_seeking.sh \
test_streams.sh \
test_bins.sh
test_bins.sh \
write_iff.pl
clean-local:
-rm -f *.raw *.flac *.oga *.ogg *.cmp *.aiff *.wav *.diff *.log *.cue core
-rm -f *.raw *.flac *.oga *.ogg *.cmp *.aiff *.wav *.rf64 *.diff *.log *.cue core
......@@ -52,4 +52,4 @@ valgrind: all
release : all
clean:
rm -f *.raw *.flac *.oga *.ogg *.cmp *.aiff *.wav *.diff *.log *.cue core flac-to-flac-metadata-test-files/out.* metaflac-test-files/out.*
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.*
......@@ -183,12 +183,13 @@ done
rt_test_raw ()
{
f="$1"
extra="$2"
channels=`echo $f | awk -F- '{print $2}'`
bps=`echo $f | awk -F- '{print $3}'`
echo -n "round-trip test ($f) encode... "
run_flac $SILENT --force --verify --force-raw-format --endian=little --sign=signed --sample-rate=44100 --bps=$bps --channels=$channels --no-padding --lax -o rt.flac $f || die "ERROR"
run_flac $SILENT --force --verify --force-raw-format --endian=little --sign=signed --sample-rate=44100 --bps=$bps --channels=$channels --no-padding --lax -o rt.flac $extra $f || die "ERROR"
echo -n "decode... "
run_flac $SILENT --force --decode --force-raw-format --endian=little --sign=signed -o rt.raw rt.flac || die "ERROR"
run_flac $SILENT --force --decode --force-raw-format --endian=little --sign=signed -o rt.raw $extra rt.flac || die "ERROR"
echo -n "compare... "
cmp $f rt.raw || die "ERROR: file mismatch"
echo "OK"
......@@ -198,23 +199,39 @@ rt_test_raw ()
rt_test_wav ()
{
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 $f || die "ERROR"
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.wav rt.flac || die "ERROR"
run_flac $SILENT --force --decode --channel-map=none -o rt.wav $extra rt.flac || die "ERROR"
echo -n "compare... "
cmp $f rt.wav || die "ERROR: file mismatch"
echo "OK"
rm -f rt.flac rt.wav
}
rt_test_rf64 ()
{
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.rf64 $extra rt.flac || die "ERROR"
echo -n "compare... "
cmp $f rt.rf64 || die "ERROR: file mismatch"
echo "OK"
rm -f rt.flac rt.rf64
}
rt_test_aiff ()
{
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 $f || die "ERROR"
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.aiff rt.flac || die "ERROR"
run_flac $SILENT --force --decode --channel-map=none -o rt.aiff $extra rt.flac || die "ERROR"
echo -n "compare... "
cmp $f rt.aiff || die "ERROR: file mismatch"
echo "OK"
......@@ -225,12 +242,13 @@ rt_test_aiff ()
rt_test_flac ()
{
f="$1"
extra="$2"
echo -n "round-trip test ($f->flac->flac->wav) encode... "
run_flac $SILENT --force --verify --channel-map=none --no-padding --lax -o rt.flac $f || die "ERROR"
run_flac $SILENT --force --verify --channel-map=none --no-padding --lax -o rt.flac $extra $f || die "ERROR"
echo -n "re-encode... "
run_flac $SILENT --force --verify --lax -o rt2.flac rt.flac || die "ERROR"
echo -n "decode... "
run_flac $SILENT --force --decode --channel-map=none -o rt.wav rt2.flac || die "ERROR"
run_flac $SILENT --force --decode --channel-map=none -o rt.wav $extra rt2.flac || die "ERROR"
echo -n "compare... "
cmp $f rt.wav || die "ERROR: file mismatch"
echo "OK"
......@@ -241,12 +259,13 @@ rt_test_flac ()
rt_test_ogg_flac ()
{
f="$1"
extra="$2"
echo -n "round-trip test ($f->oggflac->oggflac->wav) encode... "
run_flac $SILENT --force --verify --channel-map=none --no-padding --lax -o rt.oga --ogg $f || die "ERROR"
run_flac $SILENT --force --verify --channel-map=none --no-padding --lax -o rt.oga --ogg $extra $f || die "ERROR"
echo -n "re-encode... "
run_flac $SILENT --force --verify --lax -o rt2.oga --ogg rt.oga || die "ERROR"
echo -n "decode... "
run_flac $SILENT --force --decode --channel-map=none -o rt.wav rt2.oga || die "ERROR"
run_flac $SILENT --force --decode --channel-map=none -o rt.wav $extra rt2.oga || die "ERROR"
echo -n "compare... "
cmp $f rt.wav || die "ERROR: file mismatch"
echo "OK"
......@@ -259,6 +278,9 @@ done
for f in rt-*.wav ; do
rt_test_wav $f
done
for f in rt-*.rf64 ; do
rt_test_rf64 $f
done
for f in rt-*.aiff ; do
rt_test_aiff $f
done
......@@ -1116,6 +1138,18 @@ for input_type in $input_types ; do
done
############################################################################
# test --keep-foreign-metadata
############################################################################
echo "Testing --keep-foreign-metadata..."
rt_test_wav wacky1.wav '--keep-foreign-metadata'
rt_test_wav wacky2.wav '--keep-foreign-metadata'
rt_test_rf64 wacky1.rf64 '--keep-foreign-metadata'
rt_test_rf64 wacky2.rf64 '--keep-foreign-metadata'
############################################################################
# test the metadata-handling properties of flac-to-flac encoding
############################################################################
......
#!/usr/bin/perl -w
use strict;
require Math::BigInt;
my $usage = "
$0 <format> <bps> <channels> <sample-rate> <#samples> <sample-type>
<format> is one of aiff,wave,rf64
<bps> is 8,16,24,32
<channels> is 1-8
<sample-rate> is any 32-bit value
<#samples> is 0-2^64-1
<sample-type> is one of zero,rand
";
die $usage unless @ARGV == 6;
my %formats = ( 'aiff'=>1, 'wave'=>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
my ($format, $bps, $channels, $samplerate, $samples, $sampletype) = @ARGV;
my $bigsamples = new Math::BigInt $samples;
die $usage unless defined $formats{$format};
die $usage unless $bps == 8 || $bps == 16 || $bps == 24 || $bps == 32;
die $usage unless $channels >= 1 && $channels <= 8;
die $usage unless $samplerate >= 0 && $samplerate <= 4294967295;
die $usage unless defined $sampletypes{$sampletype};
# convert bits-per-sample to bytes-per-sample
$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);
# write header
if ($format eq 'aiff') {
die "sample data too big for format\n" if 46 + $datasize + $padding > 4294967295;
# header
print "FORM";
print pack('N', 46 + $datasize + $padding);
print "AIFF";
# COMM chunk
print "COMM";
print pack('N', 18); # chunk size = 18
print pack('n', $channels);
print pack('N', $samples);
print pack('n', $bps * 8);
print pack_sane_extended($samplerate);
# SSND header
print "SSND";
print pack('N', $datasize + 8); # chunk size
print pack('N', 0); # ssnd_offset_size
print pack('N', 0); # blocksize
}
elsif ($format eq 'wave' || $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);
print "WAVE";
}
else {
print "RF64";
print pack('V', 0xffffffff);
print "WAVE";
# ds64 chunk
print "ds64";
print pack('V', 28); # chunk size
my $bigriffsize = $bigdatasize + ($wavx?60:36) + (8+28) + $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
print pack('v', $wavx?65534:1); # compression code
print pack('v', $channels);
print pack('V', $samplerate);
print pack('V', $samplerate * $channels * $bps);
print pack('v', $bps); # block align = channels*((bps+7)/8)
print pack('v', $bps * 8); # bits per sample = ((bps+7)/8)*8
if ($wavx) {
print pack('v', 22); # cbSize
print pack('v', $bps * 8); # validBitsPerSample
print pack('V', $channelmask[$channels]);
# GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}
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);
}
else {
die;
}
# write sample data
if ($sampletype eq 'zero') {
my $chunk = 4096;
my $buf = pack("x[".($channels*$bps*$chunk)."]");
for (my $s = $samples; $s > 0; $s -= $chunk) {
if ($s < $chunk) {
print substr($buf, 0, $channels*$bps*$s);
}
else {
print $buf;
}
}
}
elsif ($sampletype eq 'rand') {
for (my $s = 0; $s < $samples; $s++) {
for (my $c = 0; $c < $channels; $c++) {
for (my $b = 0; $b < $bps; $b++) {
print pack('C', int(rand(256)));
}
}
}
}
else {
die;
}
print "\x00" if $padding;
exit 0;
sub pack_sane_extended
{
my $val = shift;
die unless $val > 0;
my $shift;
for ($shift = 0; ($val>>(31-$shift)) == 0; ++$shift) {
}
$val <<= $shift;
my $exponent = 63 - ($shift + 32);
return pack('nNN', $exponent + 16383, $val, 0);
}
sub pack_64
{
my $c = shift;
my $v1 = shift;
my $v2 = $v1->copy();
if ($c eq 'V') {
$v1->band(0xffffffff);
$v2->brsft(32);
}
elsif ($c eq 'C') {
$v2->band(0xffffffff);
$v1->brsft(32);
}
else {
die;
}
return pack("$c$c", 0+$v1->bstr(), 0+$v2->bstr());
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment