196 lines
4.8 KiB
Go
196 lines
4.8 KiB
Go
/*
|
|
Links:
|
|
http://code.google.com/p/goflac-meta/source/browse/flacmeta_test.go
|
|
http://flac.sourceforge.net/api/hierarchy.html
|
|
http://flac.sourceforge.net/documentation_format_overview.html
|
|
http://flac.sourceforge.net/format.html
|
|
http://jflac.sourceforge.net/
|
|
http://ffmpeg.org/doxygen/trunk/libavcodec_2flacdec_8c-source.html#l00485
|
|
http://mi.eng.cam.ac.uk/reports/svr-ftp/auto-pdf/robinson_tr156.pdf
|
|
*/
|
|
|
|
// Package flac provides access to FLAC [1] (Free Lossless Audio Codec) files.
|
|
//
|
|
// The basic structure of a FLAC bitstream is:
|
|
// - The four byte string "fLaC".
|
|
// - The StreamInfo metadata block.
|
|
// - Zero or more other metadata blocks.
|
|
// - One or more audio frames.
|
|
//
|
|
// [1]: http://flac.sourceforge.net/format.html
|
|
package flac
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/mewkiz/flac/frame"
|
|
"github.com/mewkiz/flac/meta"
|
|
)
|
|
|
|
// A Stream is a FLAC bitstream.
|
|
type Stream struct {
|
|
// The underlying reader of the stream.
|
|
r io.ReadSeeker
|
|
// Metadata blocks.
|
|
MetaBlocks []*meta.Block
|
|
// Audio frames.
|
|
Frames []*frame.Frame
|
|
}
|
|
|
|
// Parse reads the provided file and returns a parsed FLAC bitstream. It parses
|
|
// all metadata blocks and all audio frames. Use Open instead for more
|
|
// granularity.
|
|
func Parse(filePath string) (s *Stream, err error) {
|
|
f, err := os.Open(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
return ParseStream(f)
|
|
}
|
|
|
|
// Open validates the FLAC signature of the provided file and returns a handle
|
|
// to the FLAC bitstream. Callers should close the stream when done reading from
|
|
// it.
|
|
func Open(filePath string) (s *Stream, err error) {
|
|
f, err := os.Open(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewStream(f)
|
|
}
|
|
|
|
// Close closes the streams underlying reader.
|
|
func (s *Stream) Close() error {
|
|
r, ok := s.r.(io.Closer)
|
|
if ok {
|
|
return r.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ParseStream reads from the provided io.ReadSeeker and returns a parsed FLAC
|
|
// bitstream. It parses all metadata blocks and all audio frames. Use NewStream
|
|
// instead for more granularity.
|
|
func ParseStream(r io.ReadSeeker) (s *Stream, err error) {
|
|
s, err = NewStream(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = s.Parse()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// FlacSignature is present at the beginning of each FLAC file.
|
|
const FlacSignature = "fLaC"
|
|
|
|
// NewStream validates the FLAC signature of the provided io.ReadSeeker and
|
|
// returns a handle to the FLAC bitstream.
|
|
func NewStream(r io.ReadSeeker) (s *Stream, err error) {
|
|
// Verify "fLaC" signature (size: 4 bytes).
|
|
buf := make([]byte, 4)
|
|
_, err = io.ReadFull(r, buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := string(buf)
|
|
if sig != FlacSignature {
|
|
return nil, fmt.Errorf("flac.NewStream: invalid signature; expected %q, got %q", FlacSignature, sig)
|
|
}
|
|
|
|
s = &Stream{r: r}
|
|
return s, nil
|
|
}
|
|
|
|
// Parse reads and parses all metadata blocks and audio frames of the stream.
|
|
// Use ParseBlocks and ParseFrames instead for more granularity.
|
|
func (s *Stream) Parse() (err error) {
|
|
err = s.ParseBlocks(meta.TypeAll)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = s.ParseFrames()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ParseBlocks reads and parses the specified metadata blocks of the stream,
|
|
// based on the provided types bitfield. The StreamInfo block type is always
|
|
// included.
|
|
func (s *Stream) ParseBlocks(types meta.BlockType) (err error) {
|
|
// The StreamInfo block type is always included.
|
|
types |= meta.TypeStreamInfo
|
|
|
|
// Read metadata blocks.
|
|
isFirst := true
|
|
var isLast bool
|
|
for !isLast {
|
|
// Read metadata block header.
|
|
block, err := meta.NewBlock(s.r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if block.Header.IsLast {
|
|
isLast = true
|
|
}
|
|
|
|
// The first block type must be StreamInfo.
|
|
if isFirst {
|
|
if block.Header.BlockType != meta.TypeStreamInfo {
|
|
return fmt.Errorf("flac.NewStream: first block type is invalid; expected %d (StreamInfo), got %d", meta.TypeStreamInfo, block.Header.BlockType)
|
|
}
|
|
isFirst = false
|
|
}
|
|
|
|
// Check if the metadata block type is present in the provided types
|
|
// bitfield.
|
|
if block.Header.BlockType & types != 0 {
|
|
// Read metadata block body.
|
|
err = block.Parse()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// Ignore metadata block body.
|
|
err = block.Skip()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Store the decoded metadata block.
|
|
s.MetaBlocks = append(s.MetaBlocks, block)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ParseFrames reads and parses the audio frames of the stream.
|
|
func (s *Stream) ParseFrames() (err error) {
|
|
// The first block is always a StreamInfo block.
|
|
si := s.MetaBlocks[0].Body.(*meta.StreamInfo)
|
|
|
|
// Read audio frames.
|
|
// uint64 won't overflow since the max value of SampleCount is
|
|
// 0x0000000FFFFFFFFF.
|
|
var i uint64
|
|
for i < si.SampleCount {
|
|
f, err := frame.NewFrame(s.r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Frames = append(s.Frames, f)
|
|
i += uint64(len(f.SubFrames[0].Samples))
|
|
}
|
|
|
|
return nil
|
|
}
|