flac: add Encoder API to encode audio samples and metadata blocks (#32)

* flac: encode frame header

* flac: calculate CRC-8 when encoding frame headers

* flac: fix encoding of frame header

* flac: add preliminary subframe encoder

* flac: fix UTF-8 encoding of frame number

* frame: add sanity check for sample count in decodeLPC

Updates #31.

* flac: update flac encoding API, depricate flac.Encode

Encode has been removed in favour of using NewEncoder.
The Encode function was temporarily added to support
re-encoding FLAC streams to update the metadata, but
it had no support for encoding audio samples.

The added flac.Encoder has support for encoding both
metadata and audio samples. It also does not require
that you first decode a FLAC file to later re-encode
it by calling Encode (as was the previous behaviour).

* flac: add MD5 running hash of unencoded audio samples to StreamInfo

* flac: remove unused encodePadding

Reported by golangci

* flac: fix golangci lint issues

	frame/utf8.go:57:6: `decodeUTF8Int` is unused (deadcode)
	func decodeUTF8Int(r io.Reader) (n uint64, err error) {
		  ^
	internal/utf8/encode.go:32:16: unnecessary conversion (unconvert)
			bits = uint64(t2 | (x>>6)&mask2)
							 ^
	internal/utf8/encode.go:37:16: unnecessary conversion (unconvert)
			bits = uint64(t3 | (x>>(6*2))&mask3)
							 ^
	internal/utf8/encode.go:42:16: unnecessary conversion (unconvert)
			bits = uint64(t4 | (x>>(6*3))&mask4)
							 ^

* flac: fix golangci lint issues

	encode_frame.go:89:1: cyclomatic complexity 52 of func `(*Encoder).encodeFrameHeader` is high (> 30) (gocyclo)
	func (enc *Encoder) encodeFrameHeader(w io.Writer, hdr frame.Header) error {
	^
	internal/utf8/encode.go:66:17: unnecessary conversion (unconvert)
			bits := uint64(tx | (x>>uint(6*i))&maskx)
							  ^
	encode_subframe.go:105:46: unnecessary conversion (unconvert)
			if err := bw.WriteBits(uint64(sample), byte(hdr.BitsPerSample)); err != nil {
																	 ^

* flac: clarify that frame.Header.Num is calculated by the encoder

* flac: minor re-phrasing
This commit is contained in:
Robin Eklind 2018-08-19 03:18:12 +09:00 committed by GitHub
parent 4309906bb8
commit 3e3f4b5fcf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1393 additions and 589 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.flac
*.wav

209
cmd/wav2flac/main.go Normal file
View file

@ -0,0 +1,209 @@
package main
import (
"flag"
"fmt"
"log"
"os"
"github.com/go-audio/audio"
"github.com/go-audio/wav"
"github.com/mewkiz/flac"
"github.com/mewkiz/flac/frame"
"github.com/mewkiz/flac/meta"
"github.com/mewkiz/pkg/osutil"
"github.com/mewkiz/pkg/pathutil"
"github.com/pkg/errors"
)
func main() {
// Parse command line arguments.
var (
// force overwrite FLAC file if already present.
force bool
)
flag.BoolVar(&force, "f", false, "force overwrite")
flag.Parse()
for _, wavPath := range flag.Args() {
if err := wav2flac(wavPath, force); err != nil {
log.Fatalf("%+v", err)
}
}
}
func wav2flac(wavPath string, force bool) error {
// Create WAV decoder.
r, err := os.Open(wavPath)
if err != nil {
return errors.WithStack(err)
}
defer r.Close()
dec := wav.NewDecoder(r)
if !dec.IsValidFile() {
return errors.Errorf("invalid WAV file %q", wavPath)
}
sampleRate, nchannels, bps := int(dec.SampleRate), int(dec.NumChans), int(dec.BitDepth)
// Create FLAC encoder.
flacPath := pathutil.TrimExt(wavPath) + ".flac"
if !force && osutil.Exists(flacPath) {
return errors.Errorf("FLAC file %q already present; use -f flag to force overwrite", flacPath)
}
w, err := os.Create(flacPath)
if err != nil {
return errors.WithStack(err)
}
info := &meta.StreamInfo{
// Minimum block size (in samples) used in the stream; between 16 and
// 65535 samples.
BlockSizeMin: 16, // adjusted by encoder.
// Maximum block size (in samples) used in the stream; between 16 and
// 65535 samples.
BlockSizeMax: 65535, // adjusted by encoder.
// Minimum frame size in bytes; a 0 value implies unknown.
//FrameSizeMin // set by encoder.
// Maximum frame size in bytes; a 0 value implies unknown.
//FrameSizeMax // set by encoder.
// Sample rate in Hz; between 1 and 655350 Hz.
SampleRate: uint32(sampleRate),
// Number of channels; between 1 and 8 channels.
NChannels: uint8(nchannels),
// Sample size in bits-per-sample; between 4 and 32 bits.
BitsPerSample: uint8(bps),
// Total number of inter-channel samples in the stream. One second of
// 44.1 KHz audio will have 44100 samples regardless of the number of
// channels. A 0 value implies unknown.
//NSamples // set by encoder.
// MD5 checksum of the unencoded audio data.
//MD5sum // set by encoder.
}
enc, err := flac.NewEncoder(w, info)
if err != nil {
return errors.WithStack(err)
}
defer enc.Close()
// Encode samples.
if err := dec.FwdToPCM(); err != nil {
return errors.WithStack(err)
}
// Number of samples per channel and block.
const nsamplesPerChannel = 16
nsamplesPerBlock := nchannels * nsamplesPerChannel
buf := &audio.IntBuffer{
Format: &audio.Format{
NumChannels: nchannels,
SampleRate: sampleRate,
},
Data: make([]int, nsamplesPerBlock),
SourceBitDepth: bps,
}
subframes := make([]*frame.Subframe, nchannels)
subHdr := frame.SubHeader{
// Specifies the prediction method used to encode the audio sample of the
// subframe.
Pred: frame.PredVerbatim,
// Prediction order used by fixed and FIR linear prediction decoding.
Order: 0,
// Wasted bits-per-sample.
Wasted: 0,
}
for i := range subframes {
subframe := &frame.Subframe{
SubHeader: subHdr,
Samples: make([]int32, nsamplesPerChannel),
}
subframes[i] = subframe
}
for j := 0; !dec.EOF(); j++ {
// Decode WAV samples.
n, err := dec.PCMBuffer(buf)
if err != nil {
return errors.WithStack(err)
}
if n == 0 {
break
}
for _, subframe := range subframes {
subframe.NSamples = n / nchannels
subframe.Samples = subframe.Samples[:subframe.NSamples]
}
for i, sample := range buf.Data {
subframe := subframes[i%nchannels]
subframe.Samples[i/nchannels] = int32(sample)
}
fmt.Println("j:", j)
// Encode FLAC frame.
channels, err := getChannels(nchannels)
if err != nil {
return errors.WithStack(err)
}
hdr := frame.Header{
// Specifies if the block size is fixed or variable.
HasFixedBlockSize: false,
// Block size in inter-channel samples, i.e. the number of audio samples
// in each subframe.
BlockSize: uint16(nsamplesPerChannel),
// Sample rate in Hz; a 0 value implies unknown, get sample rate from
// StreamInfo.
SampleRate: uint32(sampleRate),
// Specifies the number of channels (subframes) that exist in the frame,
// their order and possible inter-channel decorrelation.
Channels: channels,
// Sample size in bits-per-sample; a 0 value implies unknown, get sample
// size from StreamInfo.
BitsPerSample: uint8(bps),
// Specifies the frame number if the block size is fixed, and the first
// sample number in the frame otherwise. When using fixed block size, the
// first sample number in the frame can be derived by multiplying the
// frame number with the block size (in samples).
//Num // set by encoder.
}
f := &frame.Frame{
Header: hdr,
Subframes: subframes,
}
if err := enc.WriteFrame(f); err != nil {
return errors.WithStack(err)
}
}
return nil
}
// getChannels returns the channels assignment matching the given number of
// channels.
func getChannels(nchannels int) (frame.Channels, error) {
switch nchannels {
case 1:
// 1 channel: mono.
return frame.ChannelsMono, nil
case 2:
// 2 channels: left, right.
return frame.ChannelsLR, nil
//return frame.ChannelsLeftSide, nil // 2 channels: left, side; using inter-channel decorrelation.
//return frame.ChannelsSideRight, nil // 2 channels: side, right; using inter-channel decorrelation.
//return frame.ChannelsMidSide, nil // 2 channels: mid, side; using inter-channel decorrelation.
case 3:
// 3 channels: left, right, center.
return frame.ChannelsLRC, nil
case 4:
// 4 channels: left, right, left surround, right surround.
return frame.ChannelsLRLsRs, nil
case 5:
// 5 channels: left, right, center, left surround, right surround.
return frame.ChannelsLRCLsRs, nil
case 6:
// 6 channels: left, right, center, LFE, left surround, right surround.
return frame.ChannelsLRCLfeLsRs, nil
case 7:
// 7 channels: left, right, center, LFE, center surround, side left, side right.
return frame.ChannelsLRCLfeCsSlSr, nil
case 8:
// 8 channels: left, right, center, LFE, left surround, right surround, side left, side right.
return frame.ChannelsLRCLfeLsRsSlSr, nil
default:
return 0, errors.Errorf("support for %d number of channels not yet implemented", nchannels)
}
}

548
enc.go
View file

@ -1,548 +0,0 @@
package flac
import (
"bytes"
"encoding/binary"
"io"
"log"
"github.com/icza/bitio"
"github.com/mewkiz/flac/meta"
"github.com/mewkiz/pkg/errutil"
)
// Encode writes the FLAC audio stream to w.
func Encode(w io.Writer, stream *Stream) error {
// Create a bit writer to the output stream.
// TODO: Remove buf when me manage to find a way to flush bits without
// closing the underlying writer.
// Use a temporary buffer to avoid closing the underlying writer when calling
// `Close` on the bit writer to flushing pending bits.
buf := new(bytes.Buffer)
enc := &encoder{bw: bitio.NewWriter(buf)}
// Store FLAC signature.
if _, err := enc.bw.Write(flacSignature); err != nil {
return errutil.Err(err)
}
// Store the StreamInfo metadata block.
infoHdr := meta.Header{
IsLast: len(stream.Blocks) == 0,
Type: meta.TypeStreamInfo,
}
if err := enc.writeStreamInfo(infoHdr, stream.Info); err != nil {
return errutil.Err(err)
}
// Store metadata blocks.
for i, block := range stream.Blocks {
if block.Type > meta.TypePicture {
log.Printf("ignoring metadata block of unknown block type %d", block.Type)
continue
}
// Store metadata block body.
var err error
hdr := block.Header
hdr.IsLast = i == len(stream.Blocks)-1
switch body := block.Body.(type) {
case *meta.Application:
err = enc.writeApplication(hdr, body)
case *meta.SeekTable:
err = enc.writeSeekTable(hdr, body)
case *meta.VorbisComment:
err = enc.writeVorbisComment(hdr, body)
case *meta.CueSheet:
err = enc.writeCueSheet(hdr, body)
case *meta.Picture:
err = enc.writePicture(hdr, body)
default:
err = enc.writePadding(hdr)
}
if err != nil {
return errutil.Err(err)
}
}
// Flush pending bit writes.
if err := enc.bw.Close(); err != nil {
return errutil.Err(err)
}
// Copy buffer to output stream.
if _, err := io.Copy(w, buf); err != nil {
return errutil.Err(err)
}
// TODO: Implement proper encoding support for audio samples. For now, copy
// the audio sample stream verbatim from the source file.
if _, err := io.Copy(w, stream.r); err != nil {
return errutil.Err(err)
}
return nil
}
// An encoder represents a FLAC encoder.
type encoder struct {
// Bit writer to the output stream.
bw bitio.Writer
}
// writeBlockHeader writes the header of a metadata block.
func (enc *encoder) writeBlockHeader(hdr meta.Header) error {
// 1 bit: IsLast.
x := uint64(0)
if hdr.IsLast {
x = 1
}
if err := enc.bw.WriteBits(x, 1); err != nil {
return errutil.Err(err)
}
// 7 bits: Type.
if err := enc.bw.WriteBits(uint64(hdr.Type), 7); err != nil {
return errutil.Err(err)
}
// 24 bits: Length.
if err := enc.bw.WriteBits(uint64(hdr.Length), 24); err != nil {
return errutil.Err(err)
}
return nil
}
// writeStreamInfo stores the body of a StreamInfo metadata block.
func (enc *encoder) writeStreamInfo(hdr meta.Header, si *meta.StreamInfo) error {
// Store metadata block header.
const (
BlockSizeMinBits = 16
BlockSizeMaxBits = 16
FrameSizeMinBits = 24
FrameSizeMaxBits = 24
SampleRateBits = 20
NChannelsBits = 3
BitsPerSampleBits = 5
NSamplesBits = 36
MD5sumBits = 8 * 16
)
nbits := int64(BlockSizeMinBits + BlockSizeMaxBits + FrameSizeMinBits +
FrameSizeMaxBits + SampleRateBits + NChannelsBits + BitsPerSampleBits +
NSamplesBits + MD5sumBits)
hdr.Length = nbits / 8
if err := enc.writeBlockHeader(hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 16 bits: BlockSizeMin.
if err := enc.bw.WriteBits(uint64(si.BlockSizeMin), 16); err != nil {
return errutil.Err(err)
}
// 16 bits: BlockSizeMax.
if err := enc.bw.WriteBits(uint64(si.BlockSizeMax), 16); err != nil {
return errutil.Err(err)
}
// 24 bits: FrameSizeMin.
if err := enc.bw.WriteBits(uint64(si.FrameSizeMin), 24); err != nil {
return errutil.Err(err)
}
// 24 bits: FrameSizeMax.
if err := enc.bw.WriteBits(uint64(si.FrameSizeMax), 24); err != nil {
return errutil.Err(err)
}
// 20 bits: SampleRate.
if err := enc.bw.WriteBits(uint64(si.SampleRate), 20); err != nil {
return errutil.Err(err)
}
// 3 bits: NChannels; stored as (number of channels) - 1.
if err := enc.bw.WriteBits(uint64(si.NChannels-1), 3); err != nil {
return errutil.Err(err)
}
// 5 bits: BitsPerSample; stored as (bits-per-sample) - 1.
if err := enc.bw.WriteBits(uint64(si.BitsPerSample-1), 5); err != nil {
return errutil.Err(err)
}
// 36 bits: NSamples.
if err := enc.bw.WriteBits(si.NSamples, 36); err != nil {
return errutil.Err(err)
}
// 16 bytes: MD5sum.
if _, err := enc.bw.Write(si.MD5sum[:]); err != nil {
return errutil.Err(err)
}
return nil
}
// writePadding writes the body of a Padding metadata block.
func (enc *encoder) writePadding(hdr meta.Header) error {
// Store metadata block header.
if err := enc.writeBlockHeader(hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
for i := 0; i < int(hdr.Length); i++ {
if err := enc.bw.WriteByte(0); err != nil {
return errutil.Err(err)
}
}
return nil
}
// writeApplication writes the body of an Application metadata block.
func (enc *encoder) writeApplication(hdr meta.Header, app *meta.Application) error {
// Store metadata block header.
const (
IDBits = 32
)
nbits := int64(IDBits + 8*len(app.Data))
hdr.Length = nbits / 8
if err := enc.writeBlockHeader(hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 32 bits: ID.
if err := enc.bw.WriteBits(uint64(app.ID), 32); err != nil {
return errutil.Err(err)
}
// Check if the Application block only contains an ID.
if _, err := enc.bw.Write(app.Data); err != nil {
return errutil.Err(err)
}
return nil
}
// writeSeekTable writes the body of a SeekTable metadata block.
func (enc *encoder) writeSeekTable(hdr meta.Header, table *meta.SeekTable) error {
// Store metadata block header.
const (
SampleNumBits = 64
OffsetBits = 64
NSamplesBits = 16
PointBits = SampleNumBits + OffsetBits + NSamplesBits
)
nbits := int64(PointBits * len(table.Points))
hdr.Length = nbits / 8
if err := enc.writeBlockHeader(hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
for _, point := range table.Points {
if err := binary.Write(enc.bw, binary.BigEndian, point); err != nil {
return errutil.Err(err)
}
}
return nil
}
// writeVorbisComment writes the body of a VorbisComment metadata block.
func (enc *encoder) writeVorbisComment(hdr meta.Header, comment *meta.VorbisComment) error {
// Store metadata block header.
const (
VendorLenBits = 32
NTagsBits = 32
)
nbits := int64(VendorLenBits + 8*len(comment.Vendor) + NTagsBits)
for _, tag := range comment.Tags {
const (
VectorLenBits = 32
EqualBits = 8 * 1
)
nbits += int64(VectorLenBits + 8*len(tag[0]) + EqualBits + 8*len(tag[1]))
}
hdr.Length = nbits / 8
if err := enc.writeBlockHeader(hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 32 bits: vendor length.
x := uint32(len(comment.Vendor))
if err := binary.Write(enc.bw, binary.LittleEndian, x); err != nil {
return errutil.Err(err)
}
// (vendor length) bits: Vendor.
if _, err := enc.bw.Write([]byte(comment.Vendor)); err != nil {
return errutil.Err(err)
}
// Store tags.
// 32 bits: number of tags.
x = uint32(len(comment.Tags))
if err := binary.Write(enc.bw, binary.LittleEndian, x); err != nil {
return errutil.Err(err)
}
for _, tag := range comment.Tags {
// Store tag, which has the following format:
// NAME=VALUE
buf := []byte(tag[0] + "=" + tag[1])
// 32 bits: vector length
x = uint32(len(buf))
if err := binary.Write(enc.bw, binary.LittleEndian, x); err != nil {
return errutil.Err(err)
}
// (vector length): vector.
if _, err := enc.bw.Write(buf); err != nil {
return errutil.Err(err)
}
}
return nil
}
// writeCueSheet writes the body of a CueSheet metadata block.
func (enc *encoder) writeCueSheet(hdr meta.Header, cs *meta.CueSheet) error {
// Store metadata block header.
const (
MCNBits = 8 * 128
NLeadInSamplesBits = 64
IsCompactDiscBits = 1
Reserved1Bits = 7 + 8*258
NTracksBits = 8
)
nbits := int64(MCNBits + NLeadInSamplesBits + IsCompactDiscBits +
Reserved1Bits + NTracksBits)
for _, track := range cs.Tracks {
const (
OffsetBits = 64
NumBits = 8
ISRCBits = 8 * 12
IsAudioBits = 1
HasPreEmphasisBits = 1
Reserved2Bits = 6 + 8*13
NIndicesBits = 8
)
nbits += OffsetBits + NumBits + ISRCBits + IsAudioBits + HasPreEmphasisBits + Reserved2Bits + NIndicesBits
for range track.Indicies {
const (
OffsetBits = 64
NumBits = 8
Reserved3Bits = 8 * 3
)
nbits += OffsetBits + NumBits + Reserved3Bits
}
}
hdr.Length = nbits / 8
if err := enc.writeBlockHeader(hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// Parse cue sheet.
// 128 bytes: MCN.
mcn := make([]byte, 128)
copy(mcn, cs.MCN)
if _, err := enc.bw.Write(mcn); err != nil {
return errutil.Err(err)
}
// 64 bits: NLeadInSamples.
if err := enc.bw.WriteBits(cs.NLeadInSamples, 64); err != nil {
return errutil.Err(err)
}
// 1 bit: IsCompactDisc.
x := uint64(0)
if cs.IsCompactDisc {
x = 1
}
if err := enc.bw.WriteBits(x, 1); err != nil {
return errutil.Err(err)
}
// 7 bits and 258 bytes: reserved.
if err := enc.bw.WriteBits(0, 7); err != nil {
return errutil.Err(err)
}
// TODO: Remove unnecessary allocation.
padding := make([]byte, 258)
if _, err := enc.bw.Write(padding); err != nil {
return errutil.Err(err)
}
// Parse cue sheet tracks.
// 8 bits: (number of tracks)
x = uint64(len(cs.Tracks))
if err := enc.bw.WriteBits(x, 8); err != nil {
return errutil.Err(err)
}
for _, track := range cs.Tracks {
// 64 bits: Offset.
if err := enc.bw.WriteBits(track.Offset, 64); err != nil {
return errutil.Err(err)
}
// 8 bits: Num.
if err := enc.bw.WriteBits(uint64(track.Num), 8); err != nil {
return errutil.Err(err)
}
// 12 bytes: ISRC.
isrc := make([]byte, 12)
copy(isrc, track.ISRC)
if _, err := enc.bw.Write(isrc); err != nil {
return errutil.Err(err)
}
// 1 bit: IsAudio.
x := uint64(0)
if !track.IsAudio {
x = 1
}
if err := enc.bw.WriteBits(x, 1); err != nil {
return errutil.Err(err)
}
// 1 bit: HasPreEmphasis.
// mask = 01000000
x = 0
if track.HasPreEmphasis {
x = 1
}
if err := enc.bw.WriteBits(x, 1); err != nil {
return errutil.Err(err)
}
// 6 bits and 13 bytes: reserved.
// mask = 00111111
if err := enc.bw.WriteBits(0, 6); err != nil {
return errutil.Err(err)
}
// TODO: Remove unnecessary allocation.
padding := make([]byte, 13)
if _, err := enc.bw.Write(padding); err != nil {
return errutil.Err(err)
}
// Parse indicies.
// 8 bits: (number of indicies)
x = uint64(len(track.Indicies))
if err := enc.bw.WriteBits(x, 8); err != nil {
return errutil.Err(err)
}
for _, index := range track.Indicies {
// 64 bits: Offset.
if err := enc.bw.WriteBits(index.Offset, 64); err != nil {
return errutil.Err(err)
}
// 8 bits: Num.
if err := enc.bw.WriteBits(uint64(index.Num), 8); err != nil {
return errutil.Err(err)
}
// 3 bytes: reserved.
// TODO: Remove unnecessary allocation.
padding := make([]byte, 3)
if _, err := enc.bw.Write(padding); err != nil {
return errutil.Err(err)
}
}
}
return nil
}
// writePicture writes the body of a Picture metadata block.
func (enc *encoder) writePicture(hdr meta.Header, pic *meta.Picture) error {
// Store metadata block header.
const (
TypeBits = 32
MIMELenBits = 32
DescLenBits = 32
WidthBits = 32
HeightBits = 32
DepthBits = 32
NPalColorsBits = 32
DataLenBits = 32
)
nbits := int64(TypeBits + MIMELenBits + 8*len(pic.MIME) + DescLenBits +
8*len(pic.Desc) + WidthBits + HeightBits + DepthBits + NPalColorsBits +
DataLenBits + 8*len(pic.Data))
hdr.Length = nbits / 8
if err := enc.writeBlockHeader(hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 32 bits: Type.
if err := enc.bw.WriteBits(uint64(pic.Type), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: (MIME type length).
x := uint64(len(pic.MIME))
if err := enc.bw.WriteBits(x, 32); err != nil {
return errutil.Err(err)
}
// (MIME type length) bytes: MIME.
if _, err := enc.bw.Write([]byte(pic.MIME)); err != nil {
return errutil.Err(err)
}
// 32 bits: (description length).
x = uint64(len(pic.Desc))
if err := enc.bw.WriteBits(x, 32); err != nil {
return errutil.Err(err)
}
// (description length) bytes: Desc.
if _, err := enc.bw.Write([]byte(pic.Desc)); err != nil {
return errutil.Err(err)
}
// 32 bits: Width.
if err := enc.bw.WriteBits(uint64(pic.Width), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: Height.
if err := enc.bw.WriteBits(uint64(pic.Height), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: Depth.
if err := enc.bw.WriteBits(uint64(pic.Depth), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: NPalColors.
if err := enc.bw.WriteBits(uint64(pic.NPalColors), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: (data length).
x = uint64(len(pic.Data))
if err := enc.bw.WriteBits(x, 32); err != nil {
return errutil.Err(err)
}
// (data length) bytes: Data.
if _, err := enc.bw.Write(pic.Data); err != nil {
return errutil.Err(err)
}
return nil
}

View file

@ -1,3 +1,5 @@
//+build ignore
package flac_test
import (

109
encode.go Normal file
View file

@ -0,0 +1,109 @@
package flac
import (
"crypto/md5"
"hash"
"io"
"github.com/icza/bitio"
"github.com/mewkiz/flac/meta"
"github.com/mewkiz/pkg/errutil"
)
// An Encoder represents a FLAC encoder.
type Encoder struct {
// FLAC stream of encoder.
*Stream
// Underlying io.Writer to the output stream.
w io.Writer
// io.Closer to flush pending writes to output stream.
c io.Closer
// Minimum and maximum block size (in samples) of frames written by encoder.
blockSizeMin, blockSizeMax uint16
// Minimum and maximum frame size (in bytes) of frames written by encoder.
frameSizeMin, frameSizeMax uint32
// MD5 running hash of unencoded audio samples.
md5sum hash.Hash
// Total number of samples (per channel) written by encoder.
nsamples uint64
// Current frame number if block size is fixed, and the first sample number
// of the current frame otherwise.
curNum uint64
}
// NewEncoder returns a new FLAC encoder for the given metadata StreamInfo block
// and optional metadata blocks.
func NewEncoder(w io.Writer, info *meta.StreamInfo, blocks ...*meta.Block) (*Encoder, error) {
// Store FLAC signature.
enc := &Encoder{
Stream: &Stream{
Info: info,
Blocks: blocks,
},
w: w,
md5sum: md5.New(),
}
if c, ok := w.(io.Closer); ok {
enc.c = c
}
bw := bitio.NewWriter(w)
if _, err := bw.Write(flacSignature); err != nil {
return nil, errutil.Err(err)
}
// Encode metadata blocks.
// TODO: consider using bufio.NewWriter.
if err := encodeStreamInfo(bw, info, len(blocks) == 0); err != nil {
return nil, errutil.Err(err)
}
for i, block := range blocks {
if err := encodeBlock(bw, block.Body, i == len(blocks)-1); err != nil {
return nil, errutil.Err(err)
}
}
// Flush pending writes of metadata blocks.
if _, err := bw.Align(); err != nil {
return nil, errutil.Err(err)
}
// Return encoder to be used for encoding audio samples.
return enc, nil
}
// Close closes the underlying io.Writer of the encoder and flushes any pending
// writes. If the io.Writer implements io.Seeker, the encoder will update the
// StreamInfo metadata block with the MD5 checksum of the unencoded audio
// samples, the number of samples, and the minimum and maximum frame size and
// block size.
func (enc *Encoder) Close() error {
// TODO: check if bit writer should be flushed before seeking on enc.w.
// Update StreamInfo metadata block.
if ws, ok := enc.w.(io.WriteSeeker); ok {
if _, err := ws.Seek(int64(len(flacSignature)), io.SeekStart); err != nil {
return errutil.Err(err)
}
// Update minimum and maximum block size (in samples) of FLAC stream.
enc.Info.BlockSizeMin = enc.blockSizeMin
enc.Info.BlockSizeMax = enc.blockSizeMax
// Update minimum and maximum frame size (in bytes) of FLAC stream.
enc.Info.FrameSizeMin = enc.frameSizeMin
enc.Info.FrameSizeMax = enc.frameSizeMax
// Update total number of samples (per channel) of FLAC stream.
enc.Info.NSamples = enc.nsamples
// Update MD5 checksum of the unencoded audio samples.
sum := enc.md5sum.Sum(nil)
for i := range sum {
enc.Info.MD5sum[i] = sum[i]
}
bw := bitio.NewWriter(ws)
// Write updated StreamInfo metadata block to output stream.
if err := encodeStreamInfo(bw, enc.Info, len(enc.Blocks) == 0); err != nil {
return errutil.Err(err)
}
if _, err := bw.Align(); err != nil {
return errutil.Err(err)
}
}
if enc.c != nil {
return enc.c.Close()
}
return nil
}

407
encode_frame.go Normal file
View file

@ -0,0 +1,407 @@
package flac
import (
"encoding/binary"
"io"
"github.com/icza/bitio"
"github.com/mewkiz/flac/frame"
"github.com/mewkiz/flac/internal/hashutil/crc16"
"github.com/mewkiz/flac/internal/hashutil/crc8"
"github.com/mewkiz/flac/internal/utf8"
"github.com/mewkiz/pkg/errutil"
)
// --- [ Frame ] ---------------------------------------------------------------
// WriteFrame encodes the given audio frame to the output stream. The Num field
// of the frame header is automatically calculated by the encoder.
func (enc *Encoder) WriteFrame(f *frame.Frame) error {
// Sanity checks.
nchannels := int(enc.Info.NChannels)
if nchannels != len(f.Subframes) {
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))
}
}
if nchannels != f.Channels.Count() {
return errutil.Newf("channel count mismatch; expected %d, got %d", nchannels, f.Channels.Count())
}
// Create a new CRC-16 hash writer which adds the data from all write
// operations to a running hash.
h := crc16.NewIBM()
hw := io.MultiWriter(h, enc.w)
// Encode frame header.
f.Num = enc.curNum
if f.HasFixedBlockSize {
enc.curNum++
} else {
enc.curNum += uint64(nsamplesPerChannel)
}
enc.nsamples += uint64(nsamplesPerChannel)
blockSize := uint16(nsamplesPerChannel)
if enc.blockSizeMin == 0 || blockSize < enc.blockSizeMin {
enc.blockSizeMin = blockSize
}
if enc.blockSizeMax == 0 || blockSize > enc.blockSizeMax {
enc.blockSizeMax = blockSize
}
// TODO: track number of bytes written to hw, to update values of
// frameSizeMin and frameSizeMax.
// Add unencoded audio samples to running MD5 hash.
f.Hash(enc.md5sum)
if err := enc.encodeFrameHeader(hw, f.Header); err != nil {
return errutil.Err(err)
}
// Encode subframes.
bw := bitio.NewWriter(hw)
for _, subframe := range f.Subframes {
if err := encodeSubframe(bw, f.Header, subframe); err != nil {
return errutil.Err(err)
}
}
// Zero-padding to byte alignment.
// Flush pending writes to subframe.
if _, err := bw.Align(); err != nil {
return errutil.Err(err)
}
// CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with 0) of
// everything before the crc, back to and including the frame header sync
// code.
crc := h.Sum16()
if err := binary.Write(enc.w, binary.BigEndian, crc); err != nil {
return errutil.Err(err)
}
return nil
}
// --- [ Frame header ] --------------------------------------------------------
// encodeFrameHeader encodes the given frame header, writing to w.
func (enc *Encoder) encodeFrameHeader(w io.Writer, hdr frame.Header) error {
// Create a new CRC-8 hash writer which adds the data from all write
// operations to a running hash.
h := crc8.NewATM()
hw := io.MultiWriter(h, w)
bw := bitio.NewWriter(hw)
enc.c = bw
// Sync code: 11111111111110
if err := bw.WriteBits(0x3FFE, 14); err != nil {
return errutil.Err(err)
}
// Reserved: 0
if err := bw.WriteBits(0x0, 1); err != nil {
return errutil.Err(err)
}
// Blocking strategy:
// 0 : fixed-blocksize stream; frame header encodes the frame number
// 1 : variable-blocksize stream; frame header encodes the sample number
if err := bw.WriteBool(!hdr.HasFixedBlockSize); err != nil {
return errutil.Err(err)
}
// Encode block size.
nblockSizeSuffixBits, err := encodeFrameHeaderBlockSize(bw, hdr.BlockSize)
if err != nil {
return errutil.Err(err)
}
// Encode sample rate.
sampleRateSuffixBits, nsampleRateSuffixBits, err := encodeFrameHeaderSampleRate(bw, hdr.SampleRate)
if err != nil {
return errutil.Err(err)
}
// Encode channels assignment.
if err := encodeFrameHeaderChannels(bw, hdr.Channels); err != nil {
return errutil.Err(err)
}
// Encode bits-per-sample.
if err := encodeFrameHeaderBitsPerSample(bw, hdr.BitsPerSample); err != nil {
return errutil.Err(err)
}
// Reserved: 0
if err := bw.WriteBits(0x0, 1); err != nil {
return errutil.Err(err)
}
// if (variable blocksize)
// <8-56>:"UTF-8" coded sample number (decoded number is 36 bits)
// else
// <8-48>:"UTF-8" coded frame number (decoded number is 31 bits)
if err := utf8.Encode(bw, hdr.Num); err != nil {
return errutil.Err(err)
}
// Write block size after the frame header (used for uncommon block sizes).
if nblockSizeSuffixBits > 0 {
// 0110 : get 8 bit (blocksize-1) from end of header
// 0111 : get 16 bit (blocksize-1) from end of header
if err := bw.WriteBits(uint64(hdr.BlockSize-1), nblockSizeSuffixBits); err != nil {
return errutil.Err(err)
}
}
// Write sample rate after the frame header (used for uncommon sample rates).
if nsampleRateSuffixBits > 0 {
if err := bw.WriteBits(sampleRateSuffixBits, nsampleRateSuffixBits); err != nil {
return errutil.Err(err)
}
}
// Flush pending writes to frame header.
if _, err := bw.Align(); err != nil {
return errutil.Err(err)
}
// CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) of
// everything before the crc, including the sync code.
crc := h.Sum8()
if err := binary.Write(w, binary.BigEndian, crc); err != nil {
return errutil.Err(err)
}
return nil
}
// ~~~ [ Block size ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// encodeFrameHeaderBlockSize encodes the block size of the frame header,
// writing to bw. It returns the number of bits used to store block size after
// the frame header.
func encodeFrameHeaderBlockSize(bw bitio.Writer, blockSize uint16) (nblockSizeSuffixBits byte, err error) {
// Block size in inter-channel samples:
// 0000 : reserved
// 0001 : 192 samples
// 0010-0101 : 576 * (2^(n-2)) samples, i.e. 576/1152/2304/4608
// 0110 : get 8 bit (blocksize-1) from end of header
// 0111 : get 16 bit (blocksize-1) from end of header
// 1000-1111 : 256 * (2^(n-8)) samples, i.e. 256/512/1024/2048/4096/8192/16384/32768
var bits uint64
switch blockSize {
case 192:
// 0001
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
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
default:
if blockSize <= 256 {
// 0110 : get 8 bit (blocksize-1) from end of header
bits = 0x6
nblockSizeSuffixBits = 8
} else {
// 0111 : get 16 bit (blocksize-1) from end of header
bits = 0x7
nblockSizeSuffixBits = 16
}
}
if err := bw.WriteBits(bits, 4); err != nil {
return 0, errutil.Err(err)
}
return nblockSizeSuffixBits, nil
}
// ~~~ [ Sample rate ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// encodeFrameHeaderSampleRate encodes the sample rate of the frame header,
// writing to bw. It returns the bits and the number of bits used to store
// sample rate after the frame header.
func encodeFrameHeaderSampleRate(bw bitio.Writer, sampleRate uint32) (sampleRateSuffixBits uint64, nsampleRateSuffixBits byte, err error) {
// Sample rate:
// 0000 : get from STREAMINFO metadata block
// 0001 : 88.2kHz
// 0010 : 176.4kHz
// 0011 : 192kHz
// 0100 : 8kHz
// 0101 : 16kHz
// 0110 : 22.05kHz
// 0111 : 24kHz
// 1000 : 32kHz
// 1001 : 44.1kHz
// 1010 : 48kHz
// 1011 : 96kHz
// 1100 : get 8 bit sample rate (in kHz) from end of header
// 1101 : get 16 bit sample rate (in Hz) from end of header
// 1110 : get 16 bit sample rate (in tens of Hz) from end of header
// 1111 : invalid, to prevent sync-fooling string of 1s
var bits uint64
switch sampleRate {
case 0:
// 0000 : get from STREAMINFO metadata block
bits = 0
case 88200:
// 0001 : 88.2kHz
bits = 0x1
case 176400:
// 0010 : 176.4kHz
bits = 0x2
case 192000:
// 0011 : 192kHz
bits = 0x3
case 8000:
// 0100 : 8kHz
bits = 0x4
case 16000:
// 0101 : 16kHz
bits = 0x5
case 22050:
// 0110 : 22.05kHz
bits = 0x6
case 24000:
// 0111 : 24kHz
bits = 0x7
case 32000:
// 1000 : 32kHz
bits = 0x8
case 44100:
// 1001 : 44.1kHz
bits = 0x9
case 48000:
// 1010 : 48kHz
bits = 0xA
case 96000:
// 1011 : 96kHz
bits = 0xB
default:
switch {
case sampleRate <= 255000 && sampleRate%1000 == 0:
// 1100 : get 8 bit sample rate (in kHz) from end of header
bits = 0xC
sampleRateSuffixBits = uint64(sampleRate / 1000)
nsampleRateSuffixBits = 8
case sampleRate <= 65535:
// 1101 : get 16 bit sample rate (in Hz) from end of header
bits = 0xD
sampleRateSuffixBits = uint64(sampleRate)
nsampleRateSuffixBits = 16
case sampleRate <= 655350 && sampleRate%10 == 0:
// 1110 : get 16 bit sample rate (in tens of Hz) from end of header
bits = 0xE
sampleRateSuffixBits = uint64(sampleRate / 10)
nsampleRateSuffixBits = 16
default:
return 0, 0, errutil.Newf("unable to encode sample rate %v", sampleRate)
}
}
if err := bw.WriteBits(bits, 4); err != nil {
return 0, 0, errutil.Err(err)
}
return sampleRateSuffixBits, nsampleRateSuffixBits, nil
}
// ~~~ [ Channels assignment ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// encodeFrameHeaderChannels encodes the channels assignment of the frame
// header, writing to bw.
func encodeFrameHeaderChannels(bw bitio.Writer, channels frame.Channels) error {
// Channel assignment.
// 0000-0111 : (number of independent channels)-1. Where defined, the channel order follows SMPTE/ITU-R recommendations. The assignments are as follows:
// 1 channel: mono
// 2 channels: left, right
// 3 channels: left, right, center
// 4 channels: front left, front right, back left, back right
// 5 channels: front left, front right, front center, back/surround left, back/surround right
// 6 channels: front left, front right, front center, LFE, back/surround left, back/surround right
// 7 channels: front left, front right, front center, LFE, back center, side left, side right
// 8 channels: front left, front right, front center, LFE, back left, back right, side left, side right
// 1000 : left/side stereo: channel 0 is the left channel, channel 1 is the side(difference) channel
// 1001 : right/side stereo: channel 0 is the side(difference) channel, channel 1 is the right channel
// 1010 : mid/side stereo: channel 0 is the mid(average) channel, channel 1 is the side(difference) channel
// 1011-1111 : reserved
var bits uint64
switch channels {
case frame.ChannelsMono, frame.ChannelsLR, frame.ChannelsLRC, frame.ChannelsLRLsRs, frame.ChannelsLRCLsRs, frame.ChannelsLRCLfeLsRs, frame.ChannelsLRCLfeCsSlSr, frame.ChannelsLRCLfeLsRsSlSr:
// 1 channel: mono.
// 2 channels: left, right.
// 3 channels: left, right, center.
// 4 channels: left, right, left surround, right surround.
// 5 channels: left, right, center, left surround, right surround.
// 6 channels: left, right, center, LFE, left surround, right surround.
// 7 channels: left, right, center, LFE, center surround, side left, side right.
// 8 channels: left, right, center, LFE, left surround, right surround, side left, side right.
bits = uint64(channels.Count() - 1)
case frame.ChannelsLeftSide:
// 2 channels: left, side; using inter-channel decorrelation.
// 1000 : left/side stereo: channel 0 is the left channel, channel 1 is the side(difference) channel
bits = 0x8
case frame.ChannelsSideRight:
// 2 channels: side, right; using inter-channel decorrelation.
// 1001 : right/side stereo: channel 0 is the side(difference) channel, channel 1 is the right channel
bits = 0x9
case frame.ChannelsMidSide:
// 2 channels: mid, side; using inter-channel decorrelation.
// 1010 : mid/side stereo: channel 0 is the mid(average) channel, channel 1 is the side(difference) channel
bits = 0xA
default:
return errutil.Newf("support for channel assignment %v not yet implemented", channels)
}
if err := bw.WriteBits(bits, 4); err != nil {
return errutil.Err(err)
}
return nil
}
// ~~~ [ Bits-per-sample ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// encodeFrameHeaderBitsPerSample encodes the bits-per-sample of the frame
// header, writing to bw.
func encodeFrameHeaderBitsPerSample(bw bitio.Writer, bps uint8) error {
// Sample size in bits:
// 000 : get from STREAMINFO metadata block
// 001 : 8 bits per sample
// 010 : 12 bits per sample
// 011 : reserved
// 100 : 16 bits per sample
// 101 : 20 bits per sample
// 110 : 24 bits per sample
// 111 : reserved
var bits uint64
switch bps {
case 0:
// 000 : get from STREAMINFO metadata block
bits = 0x0
case 8:
// 001 : 8 bits per sample
bits = 0x1
case 12:
// 010 : 12 bits per sample
bits = 0x2
case 16:
// 100 : 16 bits per sample
bits = 0x4
case 20:
// 101 : 20 bits per sample
bits = 0x5
case 24:
// 110 : 24 bits per sample
bits = 0x6
default:
return errutil.Newf("support for sample size %v not yet implemented", bps)
}
if err := bw.WriteBits(bits, 3); err != nil {
return errutil.Err(err)
}
return nil
}

376
encode_meta.go Normal file
View file

@ -0,0 +1,376 @@
package flac
import (
"encoding/binary"
"fmt"
"io"
"github.com/icza/bitio"
"github.com/mewkiz/flac/internal/ioutilx"
"github.com/mewkiz/flac/meta"
"github.com/mewkiz/pkg/errutil"
)
// --- [ Metadata block ] ------------------------------------------------------
// encodeBlock encodes the metadata block, writing to bw.
func encodeBlock(bw bitio.Writer, body interface{}, last bool) error {
switch body := body.(type) {
case *meta.StreamInfo:
return encodeStreamInfo(bw, body, last)
case *meta.Application:
return encodeApplication(bw, body, last)
case *meta.SeekTable:
return encodeSeekTable(bw, body, last)
case *meta.VorbisComment:
return encodeVorbisComment(bw, body, last)
case *meta.CueSheet:
return encodeCueSheet(bw, body, last)
case *meta.Picture:
return encodePicture(bw, body, last)
default:
panic(fmt.Errorf("support for metadata block body type %T not yet implemented", body))
}
}
// --- [ Metadata block header ] -----------------------------------------------
// encodeBlockHeader encodes the metadata block header, writing to bw.
func encodeBlockHeader(bw bitio.Writer, hdr *meta.Header) error {
// 1 bit: IsLast.
if err := bw.WriteBool(hdr.IsLast); err != nil {
return errutil.Err(err)
}
// 7 bits: Type.
if err := bw.WriteBits(uint64(hdr.Type), 7); err != nil {
return errutil.Err(err)
}
// 24 bits: Length.
if err := bw.WriteBits(uint64(hdr.Length), 24); err != nil {
return errutil.Err(err)
}
return nil
}
// --- [ StreamInfo ] ----------------------------------------------------------
// encodeStreamInfo encodes the StreamInfo metadata block, writing to bw.
func encodeStreamInfo(bw bitio.Writer, info *meta.StreamInfo, last bool) error {
// Store metadata block header.
const nbits = 16 + 16 + 24 + 24 + 20 + 3 + 5 + 36 + 8*16
hdr := &meta.Header{
IsLast: last,
Type: meta.TypeStreamInfo,
Length: nbits / 8,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 16 bits: BlockSizeMin.
if err := bw.WriteBits(uint64(info.BlockSizeMin), 16); err != nil {
return errutil.Err(err)
}
// 16 bits: BlockSizeMax.
if err := bw.WriteBits(uint64(info.BlockSizeMax), 16); err != nil {
return errutil.Err(err)
}
// 24 bits: FrameSizeMin.
if err := bw.WriteBits(uint64(info.FrameSizeMin), 24); err != nil {
return errutil.Err(err)
}
// 24 bits: FrameSizeMax.
if err := bw.WriteBits(uint64(info.FrameSizeMax), 24); err != nil {
return errutil.Err(err)
}
// 20 bits: SampleRate.
if err := bw.WriteBits(uint64(info.SampleRate), 20); err != nil {
return errutil.Err(err)
}
// 3 bits: NChannels; stored as (number of channels) - 1.
if err := bw.WriteBits(uint64(info.NChannels-1), 3); err != nil {
return errutil.Err(err)
}
// 5 bits: BitsPerSample; stored as (bits-per-sample) - 1.
if err := bw.WriteBits(uint64(info.BitsPerSample-1), 5); err != nil {
return errutil.Err(err)
}
// 36 bits: NSamples.
if err := bw.WriteBits(info.NSamples, 36); err != nil {
return errutil.Err(err)
}
// 16 bytes: MD5sum.
if _, err := bw.Write(info.MD5sum[:]); err != nil {
return errutil.Err(err)
}
return nil
}
// --- [ Application ] ---------------------------------------------------------
// encodeApplication encodes the Application metadata block, writing to bw.
func encodeApplication(bw bitio.Writer, app *meta.Application, last bool) error {
// Store metadata block header.
nbits := int64(32 + 8*len(app.Data))
hdr := &meta.Header{
IsLast: last,
Type: meta.TypeApplication,
Length: nbits / 8,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 32 bits: ID.
if err := bw.WriteBits(uint64(app.ID), 32); err != nil {
return errutil.Err(err)
}
// TODO: check if the Application block may contain only an ID.
if _, err := bw.Write(app.Data); err != nil {
return errutil.Err(err)
}
return nil
}
// --- [ SeekTable ] -----------------------------------------------------------
// encodeSeekTable encodes the SeekTable metadata block, writing to bw.
func encodeSeekTable(bw bitio.Writer, table *meta.SeekTable, last bool) error {
// Store metadata block header.
nbits := int64((64 + 64 + 16) * len(table.Points))
hdr := &meta.Header{
IsLast: last,
Type: meta.TypeSeekTable,
Length: nbits / 8,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
for _, point := range table.Points {
if err := binary.Write(bw, binary.BigEndian, point); err != nil {
return errutil.Err(err)
}
}
return nil
}
// --- [ VorbisComment ] -------------------------------------------------------
// encodeVorbisComment encodes the VorbisComment metadata block, writing to bw.
func encodeVorbisComment(bw bitio.Writer, comment *meta.VorbisComment, last bool) error {
// Store metadata block header.
nbits := int64(32 + 8*len(comment.Vendor) + 32)
for _, tag := range comment.Tags {
nbits += int64(32 + 8*(len(tag[0])+1+len(tag[1])))
}
hdr := &meta.Header{
IsLast: last,
Type: meta.TypeVorbisComment,
Length: nbits / 8,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 32 bits: vendor length.
// TODO: verify that little-endian encoding is used; otherwise, switch to
// using bw.WriteBits.
if err := binary.Write(bw, binary.LittleEndian, uint32(len(comment.Vendor))); err != nil {
return errutil.Err(err)
}
// (vendor length) bits: Vendor.
if _, err := bw.Write([]byte(comment.Vendor)); err != nil {
return errutil.Err(err)
}
// Store tags.
// 32 bits: number of tags.
if err := binary.Write(bw, binary.LittleEndian, uint32(len(comment.Tags))); err != nil {
return errutil.Err(err)
}
for _, tag := range comment.Tags {
// Store tag, which has the following format:
// NAME=VALUE
buf := []byte(fmt.Sprintf("%s=%s", tag[0], tag[1]))
// 32 bits: vector length
if err := binary.Write(bw, binary.LittleEndian, uint32(len(buf))); err != nil {
return errutil.Err(err)
}
// (vector length): vector.
if _, err := bw.Write(buf); err != nil {
return errutil.Err(err)
}
}
return nil
}
// --- [ CueSheet ] ------------------------------------------------------------
// encodeCueSheet encodes the CueSheet metadata block, writing to bw.
func encodeCueSheet(bw bitio.Writer, cs *meta.CueSheet, last bool) error {
// Store metadata block header.
nbits := int64(8*128 + 64 + 1 + 7 + 8*258 + 8)
for _, track := range cs.Tracks {
nbits += 64 + 8 + 8*12 + 1 + 1 + 6 + 8*13 + 8
for range track.Indicies {
nbits += 64 + 8 + 8*3
}
}
hdr := &meta.Header{
IsLast: last,
Type: meta.TypeCueSheet,
Length: nbits / 8,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// Store cue sheet.
// 128 bytes: MCN.
var mcn [128]byte
copy(mcn[:], cs.MCN)
if _, err := bw.Write(mcn[:]); err != nil {
return errutil.Err(err)
}
// 64 bits: NLeadInSamples.
if err := bw.WriteBits(cs.NLeadInSamples, 64); err != nil {
return errutil.Err(err)
}
// 1 bit: IsCompactDisc.
if err := bw.WriteBool(cs.IsCompactDisc); err != nil {
return errutil.Err(err)
}
// 7 bits and 258 bytes: reserved.
if err := bw.WriteBits(0, 7); err != nil {
return errutil.Err(err)
}
if _, err := io.CopyN(bw, ioutilx.Zero, 258); err != nil {
return errutil.Err(err)
}
// Store cue sheet tracks.
// 8 bits: (number of tracks)
if err := bw.WriteBits(uint64(len(cs.Tracks)), 8); err != nil {
return errutil.Err(err)
}
for _, track := range cs.Tracks {
// 64 bits: Offset.
if err := bw.WriteBits(track.Offset, 64); err != nil {
return errutil.Err(err)
}
// 8 bits: Num.
if err := bw.WriteBits(uint64(track.Num), 8); err != nil {
return errutil.Err(err)
}
// 12 bytes: ISRC.
var isrc [12]byte
copy(isrc[:], track.ISRC)
if _, err := bw.Write(isrc[:]); err != nil {
return errutil.Err(err)
}
// 1 bit: IsAudio.
if err := bw.WriteBool(!track.IsAudio); err != nil {
return errutil.Err(err)
}
// 1 bit: HasPreEmphasis.
// mask = 01000000
if err := bw.WriteBool(track.HasPreEmphasis); err != nil {
return errutil.Err(err)
}
// 6 bits and 13 bytes: reserved.
// mask = 00111111
if err := bw.WriteBits(0, 6); err != nil {
return errutil.Err(err)
}
if _, err := io.CopyN(bw, ioutilx.Zero, 13); err != nil {
return errutil.Err(err)
}
// Store indicies.
// 8 bits: (number of indicies)
if err := bw.WriteBits(uint64(len(track.Indicies)), 8); err != nil {
return errutil.Err(err)
}
for _, index := range track.Indicies {
// 64 bits: Offset.
if err := bw.WriteBits(index.Offset, 64); err != nil {
return errutil.Err(err)
}
// 8 bits: Num.
if err := bw.WriteBits(uint64(index.Num), 8); err != nil {
return errutil.Err(err)
}
// 3 bytes: reserved.
if _, err := io.CopyN(bw, ioutilx.Zero, 3); err != nil {
return errutil.Err(err)
}
}
}
return nil
}
// --- [ Picture ] -------------------------------------------------------------
// encodePicture encodes the Picture metadata block, writing to bw.
func encodePicture(bw bitio.Writer, pic *meta.Picture, last bool) error {
// Store metadata block header.
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,
Length: nbits / 8,
}
if err := encodeBlockHeader(bw, hdr); err != nil {
return errutil.Err(err)
}
// Store metadata block body.
// 32 bits: Type.
if err := bw.WriteBits(uint64(pic.Type), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: (MIME type length).
if err := bw.WriteBits(uint64(len(pic.MIME)), 32); err != nil {
return errutil.Err(err)
}
// (MIME type length) bytes: MIME.
if _, err := bw.Write([]byte(pic.MIME)); err != nil {
return errutil.Err(err)
}
// 32 bits: (description length).
if err := bw.WriteBits(uint64(len(pic.Desc)), 32); err != nil {
return errutil.Err(err)
}
// (description length) bytes: Desc.
if _, err := bw.Write([]byte(pic.Desc)); err != nil {
return errutil.Err(err)
}
// 32 bits: Width.
if err := bw.WriteBits(uint64(pic.Width), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: Height.
if err := bw.WriteBits(uint64(pic.Height), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: Depth.
if err := bw.WriteBits(uint64(pic.Depth), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: NPalColors.
if err := bw.WriteBits(uint64(pic.NPalColors), 32); err != nil {
return errutil.Err(err)
}
// 32 bits: (data length).
if err := bw.WriteBits(uint64(len(pic.Data)), 32); err != nil {
return errutil.Err(err)
}
// (data length) bytes: Data.
if _, err := bw.Write(pic.Data); err != nil {
return errutil.Err(err)
}
return nil
}

110
encode_subframe.go Normal file
View file

@ -0,0 +1,110 @@
package flac
import (
"github.com/icza/bitio"
"github.com/mewkiz/flac/frame"
iobits "github.com/mewkiz/flac/internal/bits"
"github.com/mewkiz/pkg/errutil"
)
// --- [ Subframe ] ------------------------------------------------------------
// encodeSubframe encodes the given subframe, writing to bw.
func encodeSubframe(bw bitio.Writer, hdr frame.Header, subframe *frame.Subframe) error {
// Encode subframe header.
if err := encodeSubframeHeader(bw, subframe.SubHeader); err != nil {
return errutil.Err(err)
}
// Encode audio samples.
switch subframe.Pred {
//case frame.PredConstant:
// if err := encodeConstantSamples(bw, samples); err != nil {
// return errutil.Err(err)
// }
case frame.PredVerbatim:
if err := encodeVerbatimSamples(bw, hdr, subframe.Samples); err != nil {
return errutil.Err(err)
}
//case frame.PredFixed:
// if err := encodeFixedSamples(bw, samples, subframe.Order); err != nil {
// return errutil.Err(err)
// }
//case frame.PredFIR:
// if err := encodeFIRSamples(bw, samples, subframe.Order); err != nil {
// return errutil.Err(err)
// }
default:
return errutil.Newf("support for prediction method %v not yet implemented", subframe.Pred)
}
return nil
}
// --- [ Subframe header ] -----------------------------------------------------
// encodeSubframeHeader encodes the given subframe header, writing to bw.
func encodeSubframeHeader(bw bitio.Writer, hdr frame.SubHeader) error {
// Zero bit padding, to prevent sync-fooling string of 1s.
if err := bw.WriteBits(0x0, 1); err != nil {
return errutil.Err(err)
}
// Subframe type:
// 000000 : SUBFRAME_CONSTANT
// 000001 : SUBFRAME_VERBATIM
// 00001x : reserved
// 0001xx : reserved
// 001xxx : if(xxx <= 4) SUBFRAME_FIXED, xxx=order ; else reserved
// 01xxxx : reserved
// 1xxxxx : SUBFRAME_LPC, xxxxx=order-1
var bits uint64
switch hdr.Pred {
case frame.PredConstant:
// 000000 : SUBFRAME_CONSTANT
bits = 0x00
case frame.PredVerbatim:
// 000001 : SUBFRAME_VERBATIM
bits = 0x01
case frame.PredFixed:
// 001xxx : if(xxx <= 4) SUBFRAME_FIXED, xxx=order ; else reserved
bits = 0x08 | uint64(hdr.Order)
case frame.PredFIR:
// 1xxxxx : SUBFRAME_LPC, xxxxx=order-1
bits = 0x20 | uint64(hdr.Order-1)
}
if err := bw.WriteBits(bits, 6); err != nil {
return errutil.Err(err)
}
// <1+k> 'Wasted bits-per-sample' flag:
//
// 0 : no wasted bits-per-sample in source subblock, k=0
// 1 : k wasted bits-per-sample in source subblock, k-1 follows, unary coded; e.g. k=3 => 001 follows, k=7 => 0000001 follows.
hasWastedBits := hdr.Wasted > 0
if err := bw.WriteBool(hasWastedBits); err != nil {
return errutil.Err(err)
}
if hasWastedBits {
if err := iobits.WriteUnary(bw, uint64(hdr.Wasted)); err != nil {
return errutil.Err(err)
}
}
return nil
}
// --- [ Verbatim samples ] ----------------------------------------------------
// encodeVerbatimSamples stores the given samples verbatim (uncompressed),
// writing to bw.
func encodeVerbatimSamples(bw bitio.Writer, hdr frame.Header, samples []int32) error {
// Unencoded subblock; n = frame's bits-per-sample, i = frame's blocksize.
if int(hdr.BlockSize) != len(samples) {
return errutil.Newf("block size and sample count mismatch; expected %d, got %d", hdr.BlockSize, len(samples))
}
for _, sample := range samples {
if err := bw.WriteBits(uint64(sample), hdr.BitsPerSample); err != nil {
return errutil.Err(err)
}
}
return nil
}

View file

@ -37,6 +37,7 @@ import (
"github.com/mewkiz/flac/internal/hashutil"
"github.com/mewkiz/flac/internal/hashutil/crc16"
"github.com/mewkiz/flac/internal/hashutil/crc8"
"github.com/mewkiz/flac/internal/utf8"
)
// A Frame contains the header and subframes of an audio frame. It holds the
@ -137,7 +138,7 @@ func (frame *Frame) Parse() error {
}
got := frame.crc.Sum16()
if got != want {
log.Printf("frame.Frame.Parse: CRC-16 checksum mismatch; expected 0x%04X, got 0x%04X", want, got)
return fmt.Errorf("frame.Frame.Parse: CRC-16 checksum mismatch; expected 0x%04X, got 0x%04X", want, got)
}
return nil
@ -285,7 +286,7 @@ func (frame *Frame) parseHeader() error {
// 1-6 bytes: UTF-8 encoded frame number.
// else
// 1-7 bytes: UTF-8 encoded sample number.
frame.Num, err = decodeUTF8Int(hr)
frame.Num, err = utf8.Decode(hr)
if err != nil {
return unexpected(err)
}
@ -307,7 +308,7 @@ func (frame *Frame) parseHeader() error {
}
got := h.Sum8()
if want != got {
log.Printf("frame.Frame.parseHeader: CRC-8 checksum mismatch; expected 0x%02X, got 0x%02X", want, got)
return fmt.Errorf("frame.Frame.parseHeader: CRC-8 checksum mismatch; expected 0x%02X, got 0x%02X", want, got)
}
return nil

View file

@ -55,7 +55,6 @@ func (frame *Frame) parseSubframe(br *bits.Reader, bps uint) (subframe *Subframe
for i, sample := range subframe.Samples {
subframe.Samples[i] = sample << subframe.Wasted
}
return subframe, err
}
@ -461,6 +460,9 @@ func (subframe *Subframe) decodeLPC(coeffs []int32, shift int32) error {
if shift < 0 {
return fmt.Errorf("frame.Subframe.decodeLPC: invalid negative shift")
}
if subframe.NSamples != len(subframe.Samples) {
return fmt.Errorf("frame.Subframe.decodeLPC: subframe sample count mismatch; expected %d, got %d", subframe.NSamples, len(subframe.Samples))
}
for i := subframe.Order; i < subframe.NSamples; i++ {
var sample int64
for j, c := range coeffs {

View file

@ -1,5 +1,9 @@
package bits
import (
"github.com/icza/bitio"
)
// ReadUnary decodes and returns an unary coded integer, whose value is
// represented by the number of leading zeros before a one.
//
@ -25,3 +29,27 @@ func (br *Reader) ReadUnary() (x uint64, err error) {
}
return x, nil
}
// WriteUnary encodes x as an unary coded integer, whose value is represented by
// the number of leading zeros before a one.
//
// Examples of unary coded binary on the left and decoded decimal on the right:
//
// 0 => 1
// 1 => 01
// 2 => 001
// 3 => 0001
// 4 => 00001
// 5 => 000001
// 6 => 0000001
func WriteUnary(bw bitio.Writer, x uint64) error {
bits := uint64(1)
n := byte(1)
for ; x > 0; x-- {
n++
}
if err := bw.WriteBits(bits, n); err != nil {
return err
}
return nil
}

24
internal/ioutilx/byte.go Normal file
View file

@ -0,0 +1,24 @@
// Package ioutilx implements extended input/output utility functions.
package ioutilx
import (
"io"
)
// ReadByte reads and returns the next byte from r.
func ReadByte(r io.Reader) (byte, error) {
var buf [1]byte
if _, err := io.ReadFull(r, buf[:]); err != nil {
return 0, err
}
return buf[0], nil
}
// WriteByte writes the given byte to w.
func WriteByte(w io.Writer, b byte) error {
buf := [1]byte{b}
if _, err := w.Write(buf[:]); err != nil {
return err
}
return nil
}

17
internal/ioutilx/zero.go Normal file
View file

@ -0,0 +1,17 @@
package ioutilx
// Zero is an io.Reader which always reads zero bytes.
var Zero zero
// zero is an io.Reader which always reads zero bytes.
type zero struct {
}
// Read reads len(b) zero bytes into b. It returns the number of bytes read and
// a nil error value.
func (zero) Read(b []byte) (n int, err error) {
for i := range b {
b[i] = 0
}
return len(b), nil
}

View file

@ -1,9 +1,12 @@
package frame
// Package utf8 implements encoding and decoding of UTF-8 coded numbers.
package utf8
import (
"errors"
"fmt"
"io"
"github.com/mewkiz/flac/internal/ioutilx"
)
const (
@ -29,9 +32,10 @@ const (
rune4Max = 1<<21 - 1
rune5Max = 1<<26 - 1
rune6Max = 1<<31 - 1
rune7Max = 1<<36 - 1
)
// decodeUTF8Int decodes a "UTF-8" coded number and returns it.
// Decode decodes a "UTF-8" coded number and returns it.
//
// ref: http://permalink.gmane.org/gmane.comp.audio.compression.flac.devel/3033
//
@ -54,8 +58,8 @@ const (
// - if B does not match 10xxxxxx, the encoding is invalid
// - set R = R or <the lower 6 bits from B>
// - the read value is R
func decodeUTF8Int(r io.Reader) (n uint64, err error) {
c0, err := readByte(r)
func Decode(r io.Reader) (x uint64, err error) {
c0, err := ioutilx.ReadByte(r)
if err != nil {
return 0, err
}
@ -80,38 +84,38 @@ func decodeUTF8Int(r io.Reader) (n uint64, err error) {
// if c0 == 110xxxxx
// total: 11 bits (5 + 6)
l = 1
n = uint64(c0 & mask2)
x = uint64(c0 & mask2)
case c0 < t4:
// if c0 == 1110xxxx
// total: 16 bits (4 + 6 + 6)
l = 2
n = uint64(c0 & mask3)
x = uint64(c0 & mask3)
case c0 < t5:
// if c0 == 11110xxx
// total: 21 bits (3 + 6 + 6 + 6)
l = 3
n = uint64(c0 & mask4)
x = uint64(c0 & mask4)
case c0 < t6:
// if c0 == 111110xx
// total: 26 bits (2 + 6 + 6 + 6 + 6)
l = 4
n = uint64(c0 & mask5)
x = uint64(c0 & mask5)
case c0 < t7:
// if c0 == 1111110x
// total: 31 bits (1 + 6 + 6 + 6 + 6 + 6)
l = 5
n = uint64(c0 & mask6)
x = uint64(c0 & mask6)
case c0 < t8:
// if c0 == 11111110
// total: 36 bits (0 + 6 + 6 + 6 + 6 + 6 + 6)
l = 6
n = 0
x = 0
}
// store bits from continuation bytes.
for i := 0; i < l; i++ {
n <<= 6
c, err := readByte(r)
x <<= 6
c, err := ioutilx.ReadByte(r)
if err != nil {
if err == io.EOF {
return 0, io.ErrUnexpectedEOF
@ -122,46 +126,35 @@ func decodeUTF8Int(r io.Reader) (n uint64, err error) {
// if c != 10xxxxxx
return 0, errors.New("frame.decodeUTF8Int: expected continuation byte")
}
n |= uint64(c & maskx)
x |= uint64(c & maskx)
}
// check if number representation is larger than necessary.
switch l {
case 1:
if n <= rune1Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; n (%d) stored in %d bytes, could be stored in %d bytes", n, l+1, l)
if x <= rune1Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
}
case 2:
if n <= rune2Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; n (%d) stored in %d bytes, could be stored in %d bytes", n, l+1, l)
if x <= rune2Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
}
case 3:
if n <= rune3Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; n (%d) stored in %d bytes, could be stored in %d bytes", n, l+1, l)
if x <= rune3Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
}
case 4:
if n <= rune4Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; n (%d) stored in %d bytes, could be stored in %d bytes", n, l+1, l)
if x <= rune4Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
}
case 5:
if n <= rune5Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; n (%d) stored in %d bytes, could be stored in %d bytes", n, l+1, l)
if x <= rune5Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
}
case 6:
if n <= rune6Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; n (%d) stored in %d bytes, could be stored in %d bytes", n, l+1, l)
if x <= rune6Max {
return 0, fmt.Errorf("frame.decodeUTF8Int: larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
}
}
return n, nil
}
// readByte reads and returns the next byte from the provided io.Reader.
func readByte(r io.Reader) (c byte, err error) {
buf := make([]byte, 1)
_, err = io.ReadFull(r, buf)
if err != nil {
return 0, err
}
return buf[0], nil
return x, nil
}

72
internal/utf8/encode.go Normal file
View file

@ -0,0 +1,72 @@
package utf8
import (
"io"
"github.com/mewkiz/flac/internal/ioutilx"
"github.com/mewkiz/pkg/errutil"
)
// Encode encodes x as a "UTF-8" coded number.
func Encode(w io.Writer, x uint64) error {
// 1-byte, 7-bit sequence?
if x <= rune1Max {
if err := ioutilx.WriteByte(w, byte(x)); err != nil {
return errutil.Err(err)
}
return nil
}
// get number of continuation bytes and store bits of c0.
var (
// number of continuation bytes.,
l int
// bits of c0.
bits uint64
)
switch {
case x <= rune2Max:
// if c0 == 110xxxxx
// total: 11 bits (5 + 6)
l = 1
bits = t2 | (x>>6)&mask2
case x <= rune3Max:
// if c0 == 1110xxxx
// total: 16 bits (4 + 6 + 6)
l = 2
bits = t3 | (x>>(6*2))&mask3
case x <= rune4Max:
// if c0 == 11110xxx
// total: 21 bits (3 + 6 + 6 + 6)
l = 3
bits = t4 | (x>>(6*3))&mask4
case x <= rune5Max:
// if c0 == 111110xx
// total: 26 bits (2 + 6 + 6 + 6 + 6)
l = 4
bits = t5 | (x>>(6*4))&mask5
case x <= rune6Max:
// if c0 == 1111110x
// total: 31 bits (1 + 6 + 6 + 6 + 6 + 6)
l = 5
bits = t6 | (x>>(6*5))&mask6
case x <= rune7Max:
// if c0 == 11111110
// total: 36 bits (0 + 6 + 6 + 6 + 6 + 6 + 6)
l = 6
bits = 0
}
// Store bits of c0.
if err := ioutilx.WriteByte(w, byte(bits)); err != nil {
return errutil.Err(err)
}
// Store continuation bytes.
for i := l - 1; i >= 0; i-- {
bits := tx | (x>>uint(6*i))&maskx
if err := ioutilx.WriteByte(w, byte(bits)); err != nil {
return errutil.Err(err)
}
}
return nil
}