meta: Implement CueSheet parsing.

This commit is contained in:
mewmew 2014-08-06 07:19:49 +02:00
parent c71d9382fd
commit 9c19fb744f
3 changed files with 154 additions and 10 deletions

View file

@ -1,5 +1,13 @@
package meta
import (
"bytes"
"encoding/binary"
"errors"
"io"
"io/ioutil"
)
// A CueSheet describes how tracks are laid out within a FLAC stream.
//
// ref: https://www.xiph.org/flac/format.html#metadata_block_cuesheet
@ -19,7 +27,143 @@ type CueSheet struct {
// parseCueSheet reads and parses the body of an CueSheet metadata block.
func (block *Block) parseCueSheet() error {
panic("not yet implemented.")
// Parse cue sheet.
// 128 bytes: MCN.
buf, err := readBytes(block.lr, 128)
if err != nil {
return err
}
cs := new(CueSheet)
block.Body = cs
cs.MCN = stringFromSZ(buf)
// 64 bits: NLeadInSamples.
err = binary.Read(block.lr, binary.BigEndian, &cs.NLeadInSamples)
if err != nil {
return err
}
// 1 bit: IsCompactDisc.
var x uint8
err = binary.Read(block.lr, binary.BigEndian, &x)
if err != nil {
return err
}
if x&1 != 0 {
cs.IsCompactDisc = true
}
// 7 bits and 258 bytes: reserved.
// mask = 11111110
if x&0xFE != 0 {
return ErrInvalidPadding
}
lr := io.LimitReader(block.lr, 258)
zr := zeros{r: lr}
_, err = io.Copy(ioutil.Discard, zr)
if err != nil {
return err
}
// Parse cue sheet tracks.
// 8 bits: (number of tracks)
err = binary.Read(block.lr, binary.BigEndian, &x)
if err != nil {
return err
}
if x < 1 {
return errors.New("meta.Block.parseCueSheet: at least one track required")
}
cs.Tracks = make([]CueSheetTrack, x)
for i := range cs.Tracks {
// 64 bits: Offset.
track := &cs.Tracks[i]
err = binary.Read(block.lr, binary.BigEndian, &track.Offset)
if err != nil {
return err
}
// 8 bits: Num.
err = binary.Read(block.lr, binary.BigEndian, &track.Num)
if err != nil {
return err
}
// 12 bytes: ISRC.
buf, err = readBytes(block.lr, 12)
if err != nil {
return err
}
track.ISRC = stringFromSZ(buf)
// 1 bit: IsAudio.
err = binary.Read(block.lr, binary.BigEndian, &x)
if err != nil {
return err
}
if x&1 == 0 {
track.IsAudio = true
}
// 1 bit: HasPreEmphasis.
if x&2 == 0 {
track.HasPreEmphasis = true
}
// 6 bits and 13 bytes: reserved.
// mask = 11111110
if x&0xFC != 0 {
return ErrInvalidPadding
}
lr = io.LimitReader(block.lr, 13)
zr = zeros{r: lr}
_, err = io.Copy(ioutil.Discard, zr)
if err != nil {
return err
}
// Parse indicies.
// 8 bits: (number of indicies)
err = binary.Read(block.lr, binary.BigEndian, &x)
if err != nil {
return err
}
track.Indicies = make([]CueSheetTrackIndex, x)
for i := range track.Indicies {
index := &track.Indicies[i]
// 64 bits: Offset.
err = binary.Read(block.lr, binary.BigEndian, &index.Offset)
if err != nil {
return err
}
// 8 bits: Num.
err = binary.Read(block.lr, binary.BigEndian, &index.Num)
if err != nil {
return err
}
// 3 bytes: reserved.
lr = io.LimitReader(block.lr, 3)
zr = zeros{r: lr}
_, err = io.Copy(ioutil.Discard, zr)
if err != nil {
return err
}
}
}
return nil
}
// stringFromSZ converts the provided byte slice to a string after terminating
// it at the first occurance of a NULL character.
func stringFromSZ(buf []byte) string {
pos := bytes.IndexByte(buf, 0)
if pos == -1 {
return string(buf)
}
return string(buf[:pos])
}
// CueSheetTrack contains the start offset of a track and other track specific

View file

@ -55,8 +55,8 @@ func Parse(r io.Reader) (block *Block, err error) {
// Errors returned by Parse.
var (
ErrReserved = errors.New("meta.Block.Parse: reserved block type")
ErrInvalid = errors.New("meta.Block.Parse: invalid block type")
ErrReservedType = errors.New("meta.Block.Parse: reserved block type")
ErrInvalidType = errors.New("meta.Block.Parse: invalid block type")
)
// Parse reads and parses the metadata block body.
@ -78,9 +78,9 @@ func (block *Block) Parse() error {
return block.parsePicture()
}
if block.Type >= 7 && block.Type <= 126 {
return ErrReserved
return ErrReservedType
}
return ErrInvalid
return ErrInvalidType
}
// Skip ignores the contents of the metadata block body.

View file

@ -6,11 +6,6 @@ import (
"io/ioutil"
)
// Errors returned by verifyPadding.
var (
ErrInvalidPadding = errors.New("meta.Block.verifyPadding: invalid padding")
)
// verifyPadding verifies the body of a Padding metadata block. It should only
// contain zero-padding.
//
@ -21,6 +16,11 @@ func (block *Block) verifyPadding() error {
return err
}
// Errors returned by zeros.Read.
var (
ErrInvalidPadding = errors.New("invalid padding")
)
// zeros implements an io.Reader, with a Read method which returns an error if
// any byte read isn't zero.
type zeros struct {