Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 105 additions & 28 deletions enc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,116 @@ import (

func TestEncode(t *testing.T) {
paths := []string{
// metadata test cases.
"meta/testdata/input-SCPAP.flac",
"meta/testdata/input-SCVA.flac",
"meta/testdata/input-SCVPAP.flac",
"meta/testdata/input-VA.flac",
"meta/testdata/input-SCVAUP.flac", // empty metadata block (of type 0x7e)
"meta/testdata/input-SVAUP.flac", // empty metadata block (of type 0x7e)
"meta/testdata/silence.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/19875.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/44127.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/59996.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/80574.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/172960.flac",
// TODO: fix: support for prediction method 2 not yet implemented
//"testdata/189983.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/191885.flac",
// TODO: fix: support for prediction method 2 not yet implemented
//"testdata/212768.flac",
// TODO: fix: support for prediction method 2 not yet implemented
//"testdata/220014.flac",
// TODO: fix: support for prediction method 2 not yet implemented
//"testdata/243749.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/256529.flac",
// TODO: fix: support for prediction method 3 not yet implemented
//"testdata/257344.flac",
// TODO: fix: support for prediction method 2 not yet implemented
//"testdata/8297-275156-0011.flac",
// TODO: fix: support for prediction method 2 not yet implemented
//"testdata/love.flac",
// flac test cases.
"testdata/19875.flac", // prediction method 3 (FIR)
"testdata/44127.flac", // prediction method 3 (FIR)
"testdata/59996.flac",
"testdata/80574.flac", // prediction method 3 (FIR)
"testdata/172960.flac",
"testdata/189983.flac",
"testdata/191885.flac",
"testdata/212768.flac",
"testdata/220014.flac", // prediction method 2 (Fixed)
"testdata/243749.flac", // prediction method 2 (Fixed)
"testdata/256529.flac",
"testdata/257344.flac", // prediction method 3 (FIR)
"testdata/8297-275156-0011.flac", // prediction method 3 (FIR)
"testdata/love.flac", // wasted bits
// IETF test cases.
"testdata/flac-test-files/subset/01 - blocksize 4096.flac",
"testdata/flac-test-files/subset/02 - blocksize 4608.flac",
"testdata/flac-test-files/subset/03 - blocksize 16.flac",
"testdata/flac-test-files/subset/04 - blocksize 192.flac",
"testdata/flac-test-files/subset/05 - blocksize 254.flac",
"testdata/flac-test-files/subset/06 - blocksize 512.flac",
"testdata/flac-test-files/subset/07 - blocksize 725.flac",
"testdata/flac-test-files/subset/08 - blocksize 1000.flac",
"testdata/flac-test-files/subset/09 - blocksize 1937.flac",
"testdata/flac-test-files/subset/10 - blocksize 2304.flac",
"testdata/flac-test-files/subset/11 - partition order 8.flac",
"testdata/flac-test-files/subset/12 - qlp precision 15 bit.flac",
"testdata/flac-test-files/subset/13 - qlp precision 2 bit.flac",
"testdata/flac-test-files/subset/14 - wasted bits.flac",
"testdata/flac-test-files/subset/15 - only verbatim subframes.flac",
"testdata/flac-test-files/subset/16 - partition order 8 containing escaped partitions.flac",
"testdata/flac-test-files/subset/17 - all fixed orders.flac",
"testdata/flac-test-files/subset/18 - precision search.flac",
"testdata/flac-test-files/subset/19 - samplerate 35467Hz.flac",
"testdata/flac-test-files/subset/20 - samplerate 39kHz.flac",
"testdata/flac-test-files/subset/21 - samplerate 22050Hz.flac",
"testdata/flac-test-files/subset/22 - 12 bit per sample.flac",
"testdata/flac-test-files/subset/23 - 8 bit per sample.flac",
"testdata/flac-test-files/subset/24 - variable blocksize file created with flake revision 264.flac",
"testdata/flac-test-files/subset/25 - variable blocksize file created with flake revision 264, modified to create smaller blocks.flac",
// NOTE: the only diff is that "26 - ...flac" uses `block_size: 0b111
// (end of header (16 bit))` to encode the block size at the end of the
// header, whereas mewkiz/flac encodes it directly `block_size: 4096
// (0b1100)`. Notably, the computed md5 hash of the decoded audio samples
// is identical (MD5: 3b2939b39ae7369b80451c77865e60c1). Thus, ignore the
// test case.
//"testdata/flac-test-files/subset/26 - variable blocksize file created with CUETools.Flake 2.1.6.flac",
// NOTE: the only diff is that "27 - ...flac" uses `block_size: 0b111
// (end of header (16 bit))` to encode the block size at the end of the
// header, whereas mewkiz/flac encodes it directly `block_size: 4608
// (0b101)`. Notably, the computed md5 hash of the decoded audio samples
// is identical (MD5: 9fb66177d2f735d4b1f501a5af1320a3). Thus, ignore the
// test case.
//"testdata/flac-test-files/subset/27 - old format variable blocksize file created with Flake 0.11.flac",
"testdata/flac-test-files/subset/28 - high resolution audio, default settings.flac",
"testdata/flac-test-files/subset/29 - high resolution audio, blocksize 16384.flac",
"testdata/flac-test-files/subset/30 - high resolution audio, blocksize 13456.flac",
"testdata/flac-test-files/subset/31 - high resolution audio, using only 32nd order predictors.flac",
"testdata/flac-test-files/subset/32 - high resolution audio, partition order 8 containing escaped partitions.flac",
"testdata/flac-test-files/subset/33 - samplerate 192kHz.flac",
// NOTE: the only diff is that "34 - ...flac" uses `0b1100 (end of header
// (8 bit*1000))` to encode the sample rate at the end of the header,
// whereas mewkiz/flac encodes it directly `192000 (0b11)`. Notably, the
// computed md5 hash of the decoded audio samples is identical
// (MD5: 942f56e503437dfd4c269c331774b2e3). Thus, ignore the test case.
//"testdata/flac-test-files/subset/34 - samplerate 192kHz, using only 32nd order predictors.flac",
"testdata/flac-test-files/subset/35 - samplerate 134560Hz.flac",
"testdata/flac-test-files/subset/36 - samplerate 384kHz.flac",
"testdata/flac-test-files/subset/37 - 20 bit per sample.flac",
"testdata/flac-test-files/subset/38 - 3 channels (3.0).flac",
"testdata/flac-test-files/subset/39 - 4 channels (4.0).flac",
"testdata/flac-test-files/subset/40 - 5 channels (5.0).flac",
"testdata/flac-test-files/subset/41 - 6 channels (5.1).flac",
"testdata/flac-test-files/subset/42 - 7 channels (6.1).flac",
"testdata/flac-test-files/subset/43 - 8 channels (7.1).flac",
// NOTE: the only diff is that "44 - ...flac" uses `0b1100 (end of header
// (8 bit*1000))` to encode the sample rate at the end of the header,
// whereas mewkiz/flac encodes it directly `192000 (0b11)`. Notably, the
// computed md5 hash of the decoded audio samples is identical
// (MD5: cdf531d4d4b95233986bc499518a89db). Thus, ignore the test case.
//"testdata/flac-test-files/subset/44 - 8-channel surround, 192kHz, 24 bit, using only 32nd order predictors.flac",
"testdata/flac-test-files/subset/45 - no total number of samples set.flac",
"testdata/flac-test-files/subset/46 - no min-max framesize set.flac",
"testdata/flac-test-files/subset/47 - only STREAMINFO.flac",
"testdata/flac-test-files/subset/48 - Extremely large SEEKTABLE.flac",
"testdata/flac-test-files/subset/49 - Extremely large PADDING.flac",
"testdata/flac-test-files/subset/50 - Extremely large PICTURE.flac",
"testdata/flac-test-files/subset/51 - Extremely large VORBISCOMMENT.flac",
"testdata/flac-test-files/subset/52 - Extremely large APPLICATION.flac",
"testdata/flac-test-files/subset/53 - CUESHEET with very many indexes.flac",
"testdata/flac-test-files/subset/54 - 1000x repeating VORBISCOMMENT.flac",
"testdata/flac-test-files/subset/55 - file 48-53 combined.flac",
"testdata/flac-test-files/subset/56 - JPG PICTURE.flac",
"testdata/flac-test-files/subset/57 - PNG PICTURE.flac",
"testdata/flac-test-files/subset/58 - GIF PICTURE.flac",
"testdata/flac-test-files/subset/59 - AVIF PICTURE.flac",
"testdata/flac-test-files/subset/60 - mono audio.flac",
"testdata/flac-test-files/subset/61 - predictor overflow check, 16-bit.flac",
"testdata/flac-test-files/subset/62 - predictor overflow check, 20-bit.flac",
"testdata/flac-test-files/subset/63 - predictor overflow check, 24-bit.flac",
"testdata/flac-test-files/subset/64 - rice partitions with escape code zero.flac",
}
for _, path := range paths {
t.Run(path, func(t *testing.T) {
Expand Down
27 changes: 22 additions & 5 deletions encode_frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ func (enc *Encoder) WriteFrame(f *frame.Frame) error {
return errutil.Newf("subframe and channel count mismatch; expected %d, got %d", nchannels, len(f.Subframes))
}
nsamplesPerChannel := f.Subframes[0].NSamples
if !(16 <= nsamplesPerChannel && nsamplesPerChannel <= 65535) {
return errutil.Newf("invalid number of samples per channel; expected >= 16 && <= 65535, got %d", nsamplesPerChannel)
}
for i, subframe := range f.Subframes {
if nsamplesPerChannel != len(subframe.Samples) {
return errutil.Newf("invalid number of samples in channel %d; expected %d, got %d", i, nsamplesPerChannel, len(subframe.Samples))
Expand Down Expand Up @@ -64,10 +61,30 @@ func (enc *Encoder) WriteFrame(f *frame.Frame) error {
return errutil.Err(err)
}

// Inter-channel decorrelation of subframe samples.
f.Decorrelate()
defer f.Correlate() // NOTE: revert decorrelation of audio samples after encoding is done (to make encode non-destructive).

// Encode subframes.
bw := bitio.NewWriter(hw)
for _, subframe := range f.Subframes {
if err := encodeSubframe(bw, f.Header, subframe); err != nil {
for channel, subframe := range f.Subframes {
// The side channel requires an extra bit per sample when using
// inter-channel decorrelation.
bps := uint(f.BitsPerSample)
switch f.Channels {
case frame.ChannelsSideRight:
// channel 0 is the side channel.
if channel == 0 {
bps++
}
case frame.ChannelsLeftSide, frame.ChannelsMidSide:
// channel 1 is the side channel.
if channel == 1 {
bps++
}
}

if err := encodeSubframe(bw, f.Header, subframe, bps); err != nil {
return errutil.Err(err)
}
}
Expand Down
20 changes: 20 additions & 0 deletions encode_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ func encodeBlock(bw *bitio.Writer, block *meta.Block, last bool) error {
if block.Type == meta.TypePadding {
return encodePadding(bw, block.Length, last)
}
if block.Length == 0 {
return encodeEmptyBlock(bw, block.Type, last)
}
switch body := block.Body.(type) {
case *meta.StreamInfo:
return encodeStreamInfo(bw, body, last)
Expand All @@ -38,6 +41,23 @@ func encodeBlock(bw *bitio.Writer, block *meta.Block, last bool) error {

// --- [ Metadata block header ] -----------------------------------------------

// encodeEmptyBlock encodes the metadata block header of an empty metadata
// block with the specified type, writing to bw.
func encodeEmptyBlock(bw *bitio.Writer, typ meta.Type, last bool) error {
// Store metadata block header.
hdr := &meta.Header{
IsLast: last,
Type: typ,
Length: 0,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
return nil
}

// --- [ Metadata block header ] -----------------------------------------------

// encodeBlockHeader encodes the metadata block header, writing to bw.
func encodeBlockHeader(bw *bitio.Writer, hdr *meta.Header) error {
// 1 bit: IsLast.
Expand Down
Loading