Skip to content

Commit

Permalink
flac: fix encoding of 256- and 576-multiple block size
Browse files Browse the repository at this point in the history
Also, fix encoding of Picture metadata block type,
add encoding of Padding metadata blocks, and re-enable
encoding test cases.
  • Loading branch information
mewmew committed Oct 25, 2023
1 parent 4200480 commit 38455e9
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 30 deletions.
102 changes: 78 additions & 24 deletions enc_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//go:build ignore
// +build ignore

package flac_test

import (
"bytes"
"io"
"io/ioutil"
"testing"

Expand All @@ -19,21 +17,36 @@ func TestEncode(t *testing.T) {
"meta/testdata/input-SCVPAP.flac",
"meta/testdata/input-VA.flac",
"meta/testdata/silence.flac",
"testdata/19875.flac",
"testdata/44127.flac",
"testdata/59996.flac",
"testdata/80574.flac",
"testdata/172960.flac",
"testdata/189983.flac",
"testdata/191885.flac",
"testdata/212768.flac",
"testdata/220014.flac",
"testdata/243749.flac",
"testdata/256529.flac",
"testdata/257344.flac",
"testdata/8297-275156-0011.flac",
"testdata/love.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",
}
loop:
for _, path := range paths {
// Decode source file.
stream, err := flac.ParseFile(path)
Expand All @@ -43,10 +56,31 @@ func TestEncode(t *testing.T) {
}
defer stream.Close()

// Encode FLAC stream.
// Open encoder for FLAC stream.
out := new(bytes.Buffer)
if err := flac.Encode(out, stream); err != nil {
t.Errorf("%q: unable to encode FLAC stream; %v", path, err)
enc, err := flac.NewEncoder(out, stream.Info, stream.Blocks...)
if err != nil {
t.Errorf("%q: unable to create encoder for FLAC stream; %v", path, err)
continue
}
// Encode audio samples.
for {
frame, err := stream.ParseNext()
if err != nil {
if err == io.EOF {
break
}
t.Errorf("%q: unable to parse audio frame of FLAC stream; %v", path, err)
continue loop
}
if err := enc.WriteFrame(frame); err != nil {
t.Errorf("%q: unable to encode audio frame of FLAC stream; %v", path, err)
continue loop
}
}
// Close encoder and flush pending writes.
if err := enc.Close(); err != nil {
t.Errorf("%q: unable to close encoder for FLAC stream; %v", path, err)
continue
}

Expand All @@ -66,7 +100,8 @@ func TestEncode(t *testing.T) {

func TestEncodeComment(t *testing.T) {
// Decode FLAC file.
src, err := flac.ParseFile("testdata/love.flac")
const path = "meta/testdata/input-VA.flac"
src, err := flac.ParseFile(path)
if err != nil {
t.Fatalf("unable to parse input FLAC file; %v", err)
}
Expand All @@ -80,12 +115,31 @@ func TestEncodeComment(t *testing.T) {
}
}

// Encode FLAC file.
// Open encoder for FLAC stream.
out := new(bytes.Buffer)
if err := flac.Encode(out, src); err != nil {
t.Fatalf("unable to encode FLAC file; %v", err)
enc, err := flac.NewEncoder(out, src.Info, src.Blocks...)
if err != nil {
t.Fatalf("%q: unable to create encoder for FLAC stream; %v", path, err)
}
// Encode audio samples.
for {
frame, err := src.ParseNext()
if err != nil {
if err == io.EOF {
break
}
t.Fatalf("%q: unable to parse audio frame of FLAC stream; %v", path, err)
}
if err := enc.WriteFrame(frame); err != nil {
t.Fatalf("%q: unable to encode audio frame of FLAC stream; %v", path, err)
}
}
// Close encoder and flush pending writes.
if err := enc.Close(); err != nil {
t.Fatalf("%q: unable to close encoder for FLAC stream; %v", path, err)
}

// Parse encoded FLAC file.
stream, err := flac.Parse(out)
if err != nil {
t.Fatalf("unable to parse output FLAC file; %v", err)
Expand Down
2 changes: 1 addition & 1 deletion encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func NewEncoder(w io.Writer, info *meta.StreamInfo, blocks ...*meta.Block) (*Enc
return nil, errutil.Err(err)
}
for i, block := range blocks {
if err := encodeBlock(bw, block.Body, i == len(blocks)-1); err != nil {
if err := encodeBlock(bw, block, i == len(blocks)-1); err != nil {
return nil, errutil.Err(err)
}
}
Expand Down
5 changes: 3 additions & 2 deletions encode_frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package flac
import (
"encoding/binary"
"io"
"math"

"github.com/icza/bitio"
"github.com/mewkiz/flac/frame"
Expand Down Expand Up @@ -202,10 +203,10 @@ func encodeFrameHeaderBlockSize(bw *bitio.Writer, blockSize uint16) (nblockSizeS
bits = 0x1
case 576, 1152, 2304, 4608:
// 0010-0101 : 576 * (2^(n-2)) samples, i.e. 576/1152/2304/4608
bits = 0x2 + uint64(blockSize/576) - 1
bits = 0x2 + uint64(math.Log2(float64(blockSize/576)))
case 256, 512, 1024, 2048, 4096, 8192, 16384, 32768:
// 1000-1111 : 256 * (2^(n-8)) samples, i.e. 256/512/1024/2048/4096/8192/16384/32768
bits = 0x8 + uint64(blockSize/256) - 1
bits = 0x8 + uint64(math.Log2(float64(blockSize/256)))
default:
if blockSize <= 256 {
// 0110 : get 8 bit (blocksize-1) from end of header
Expand Down
29 changes: 26 additions & 3 deletions encode_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import (
// --- [ Metadata block ] ------------------------------------------------------

// encodeBlock encodes the metadata block, writing to bw.
func encodeBlock(bw *bitio.Writer, body interface{}, last bool) error {
switch body := body.(type) {
func encodeBlock(bw *bitio.Writer, block *meta.Block, last bool) error {
if block.Type == meta.TypePadding {
return encodePadding(bw, block.Length, last)
}
switch body := block.Body.(type) {
case *meta.StreamInfo:
return encodeStreamInfo(bw, body, last)
case *meta.Application:
Expand Down Expand Up @@ -107,6 +110,26 @@ func encodeStreamInfo(bw *bitio.Writer, info *meta.StreamInfo, last bool) error
return nil
}

// --- [ Padding ] ----------------------------------------------------------

// encodePadding encodes the Padding metadata block, writing to bw.
func encodePadding(bw *bitio.Writer, length int64, last bool) error {
// Store metadata block header.
hdr := &meta.Header{
IsLast: last,
Type: meta.TypePadding,
Length: length,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
if _, err := io.CopyN(bw, ioutilx.Zero, length); err != nil {
return errutil.Err(err)
}
return nil
}

// --- [ Application ] ---------------------------------------------------------

// encodeApplication encodes the Application metadata block, writing to bw.
Expand Down Expand Up @@ -320,7 +343,7 @@ func encodePicture(bw *bitio.Writer, pic *meta.Picture, last bool) error {
nbits := int64(32 + 32 + 8*len(pic.MIME) + 32 + 8*len(pic.Desc) + 32 + 32 + 32 + 32 + 32 + 8*len(pic.Data))
hdr := &meta.Header{
IsLast: last,
Type: meta.TypeCueSheet,
Type: meta.TypePicture,
Length: nbits / 8,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions encode_subframe.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ func encodeSubframe(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe
if err := encodeVerbatimSamples(bw, hdr, subframe.Samples); err != nil {
return errutil.Err(err)
}
// TODO: implement support for LPC encoding of audio samples.
//case frame.PredFixed:
// if err := encodeFixedSamples(bw, hdr, subframe.Samples, subframe.Order); err != nil {
// return errutil.Err(err)
// }
// TODO: implement support for LPC encoding of audio samples.
//case frame.PredFIR:
// if err := encodeFIRSamples(bw, hdr, subframe.Samples, subframe.Order); err != nil {
// return errutil.Err(err)
Expand Down
1 change: 1 addition & 0 deletions frame/subframe.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package frame
import (
"errors"
"fmt"

"github.com/mewkiz/flac/internal/bits"
)

Expand Down

0 comments on commit 38455e9

Please sign in to comment.