flacgo/frame/subframe.go
mewmew f583bf6928 all: Fix issues located by golint.
- "error strings should not end with punctuation"
2013-10-29 14:45:05 +01:00

429 lines
12 KiB
Go

package frame
import (
"errors"
dbg "fmt"
"fmt"
"math"
"github.com/mewkiz/pkg/bit"
)
// A SubFrame contains the decoded audio data of a channel.
type SubFrame struct {
// Header specifies the attributes of the subframe, like prediction method
// and order, residual coding parameters, etc.
Header *SubHeader
// Samples contains the decoded audio samples of the channel.
Samples []Sample
}
// A Sample is an audio sample. The size of each sample is between 4 and 32 bits.
type Sample uint32
// NewSubFrame parses and returns a new subframe, which consists of a subframe
// header and encoded audio samples.
//
// Subframe format (pseudo code):
//
// type SUBFRAME struct {
// header SUBFRAME_HEADER
// enc_samples SUBFRAME_CONSTANT || SUBFRAME_FIXED || SUBFRAME_LPC || SUBFRAME_VERBATIM
// }
//
// ref: http://flac.sourceforge.net/format.html#subframe
func (h *Header) NewSubFrame(br bit.Reader) (subframe *SubFrame, err error) {
// Parse subframe header.
subframe = new(SubFrame)
subframe.Header, err = h.NewSubHeader(br)
if err != nil {
return nil, err
}
// Decode samples.
sh := subframe.Header
switch sh.PredMethod {
case PredConstant:
subframe.Samples, err = h.DecodeConstant(br)
case PredFixed:
subframe.Samples, err = h.DecodeFixed(br, int(sh.PredOrder))
case PredLPC:
subframe.Samples, err = h.DecodeLPC(br, int(sh.PredOrder))
case PredVerbatim:
subframe.Samples, err = h.DecodeVerbatim(br)
default:
return nil, fmt.Errorf("Header.NewSubFrame: unknown subframe prediction method: %d", sh.PredMethod)
}
if err != nil {
return nil, err
}
return subframe, nil
}
// A SubHeader is a subframe header, which contains information about how the
// subframe audio samples are encoded.
type SubHeader struct {
// PredMethod is the subframe prediction method.
PredMethod PredMethod
// WastedBitCount is the number of wasted bits per sample.
WastedBitCount int8
// PredOrder is the subframe predictor order, which is used accordingly:
// Fixed: Predictor order.
// LPC: LPC order.
PredOrder int8
}
// PredMethod specifies the subframe prediction method.
type PredMethod int8
// Subframe prediction methods.
const (
PredConstant PredMethod = iota
PredFixed
PredLPC
PredVerbatim
)
// NewSubHeader parses and returns a new subframe header.
//
// Subframe header format (pseudo code):
// type SUBFRAME_HEADER struct {
// _ uint1 // zero-padding, to prevent sync-fooling.
// type uint6
// // 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.
// wasted_bit_count uint1+k
// }
//
// ref: http://flac.sourceforge.net/format.html#subframe_header
func (h *Header) NewSubHeader(br bit.Reader) (sh *SubHeader, err error) {
// Padding, 1 bit.
pad, err := br.Read(1)
if err != nil {
return nil, err
}
if pad.Uint64() != 0 {
return nil, errors.New("Header.NewSubHeader: invalid padding; must be 0")
}
// Subframe prediction method, 6 bits.
bits, err := br.Read(6)
if err != nil {
return nil, err
}
// Subframe prediction method.
// 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
sh = new(SubHeader)
n := bits.Uint64()
switch {
case n == 0:
// 000000: SUBFRAME_CONSTANT
sh.PredMethod = PredConstant
case n == 1:
// 000001: SUBFRAME_VERBATIM
sh.PredMethod = PredVerbatim
case n < 8:
// 00001x: reserved
// 0001xx: reserved
return nil, fmt.Errorf("Header.NewSubHeader: invalid subframe prediction method; reserved bit pattern: %06b", n)
case n < 16:
// 001xxx: if(xxx <= 4) SUBFRAME_FIXED, xxx=order ; else reserved
const predOrderMask = 0x07
sh.PredOrder = int8(n) & predOrderMask
if sh.PredOrder > 4 {
return nil, fmt.Errorf("Header.NewSubHeader: invalid subframe prediction method; reserved bit pattern: %06b", n)
}
sh.PredMethod = PredFixed
case n < 32:
// 01xxxx: reserved
return nil, fmt.Errorf("Header.NewSubHeader: invalid subframe prediction method; reserved bit pattern: %06b", n)
case n < 64:
// 1xxxxx: SUBFRAME_LPC, xxxxx=order-1
const predOrderMask = 0x1F
sh.PredOrder = int8(n)&predOrderMask + 1
sh.PredMethod = PredLPC
default:
// should be unreachable.
return nil, fmt.Errorf("Header.NewSubHeader: unhandled subframe prediction method; bit pattern: %06b", n)
}
// Wasted bits-per-sample, 1+k bits.
bits, err = br.Read(1)
if err != nil {
return nil, err
}
if bits.Uint64() != 0 {
// k wasted bits-per-sample in source subblock, k-1 follows, unary coded;
// e.g. k=3 => 001 follows, k=7 => 0000001 follows.
/// ### [ todo ] ###
/// - verify.
/// ### [/ todo ] ###
for {
sh.WastedBitCount++
bits, err := br.Read(1)
if err != nil {
return nil, err
}
if bits.Uint64() == 1 {
break
}
}
}
return sh, nil
}
// DecodeConstant decodes and returns a slice of samples. The first sample is
// constant throughout the entire subframe.
//
// ref: http://flac.sourceforge.net/format.html#subframe_constant
func (h *Header) DecodeConstant(br bit.Reader) (samples []Sample, err error) {
// Read constant sample.
bits, err := br.Read(int(h.SampleSize))
if err != nil {
return nil, err
}
sample := Sample(bits.Uint64())
dbg.Println("Constant sample:", sample)
// Duplicate the constant sample, sample count number of times.
for i := uint16(0); i < h.SampleCount; i++ {
samples = append(samples, sample)
}
return samples, nil
}
// DecodeFixed decodes and returns a slice of samples.
/// ### [ todo ] ###
/// - add more details.
/// ### [/ todo ] ###
//
// ref: http://flac.sourceforge.net/format.html#subframe_fixed
func (h *Header) DecodeFixed(br bit.Reader, predOrder int) (samples []Sample, err error) {
// Unencoded warm-up samples:
// n bits = frame's bits-per-sample * predictor order
for i := 0; i < predOrder; i++ {
bits, err := br.Read(int(h.SampleSize))
if err != nil {
return nil, err
}
sample := Sample(bits.Uint64())
dbg.Println("Fixed warm-up sample:", sample)
samples = append(samples, sample)
}
residuals, err := h.DecodeResidual(br, predOrder)
if err != nil {
return nil, err
}
_ = residuals
/// ### [ todo ] ###
/// - not yet implemented.
/// ### [/ todo ] ###
return nil, errors.New("not yet implemented; Fixed encoding")
}
// DecodeLPC decodes and returns a slice of samples.
/// ### [ todo ] ###
/// - add more details.
/// ### [/ todo ] ###
//
// ref: http://flac.sourceforge.net/format.html#subframe_lpc
func (h *Header) DecodeLPC(br bit.Reader, lpcOrder int) (samples []Sample, err error) {
// Unencoded warm-up samples:
// n bits = frame's bits-per-sample * lpc order
for i := 0; i < lpcOrder; i++ {
bits, err := br.Read(int(h.SampleSize))
if err != nil {
return nil, err
}
sample := Sample(bits.Uint64())
dbg.Println("LPC warm-up sample:", sample)
samples = append(samples, sample)
}
// (Quantized linear predictor coefficients' precision in bits) - 1.
bits, err := br.Read(4)
if err != nil {
return nil, err
}
n := bits.Uint64()
if n == 0x0F {
// 1111: invalid.
return nil, errors.New("Header.DecodeLPC: invalid quantized lpc precision; reserved bit pattern: 1111")
}
qlpcPrec := int(n) + 1
// Quantized linear predictor coefficient shift needed in bits.
bits, err = br.Read(5)
if err != nil {
return nil, err
}
/// ### [ todo ] ###
/// - NOTE: this number is signed two's-complement.
/// - special case for negative numbers required?
/// - the same goes for qlpcPrec.
/// ### [/ todo ] ###
qlpcShift := bits.Uint64()
_ = qlpcShift
// Unencoded predictor coefficients.
for i := 0; i < lpcOrder; i++ {
bits, err = br.Read(qlpcPrec)
if err != nil {
return nil, err
}
pc := bits.Uint64()
dbg.Println("pc:", pc)
_ = pc
}
residuals, err := h.DecodeResidual(br, lpcOrder)
if err != nil {
return nil, err
}
_ = residuals
/// ### [ todo ] ###
/// - not yet implemented.
/// ### [/ todo ] ###
return nil, errors.New("not yet implemented; LPC encoding")
}
// DecodeVerbatim decodes and returns a slice of samples. The samples are stored
// unencoded.
//
// ref: http://flac.sourceforge.net/format.html#subframe_verbatim
func (h *Header) DecodeVerbatim(br bit.Reader) (samples []Sample, err error) {
// Read unencoded samples.
for i := uint16(0); i < h.SampleCount; i++ {
bits, err := br.Read(int(h.SampleSize))
if err != nil {
return nil, err
}
sample := Sample(bits.Uint64())
dbg.Println("Verbatim sample:", sample)
samples = append(samples, sample)
}
return samples, nil
}
// DecodeResidual decodes and returns a slice of residuals.
/// ### [ todo ] ###
/// - add more details.
/// ### [/ todo ] ###
//
// ref: http://flac.sourceforge.net/format.html#residual
func (h *Header) DecodeResidual(br bit.Reader, predOrder int) (residuals []int, err error) {
// Residual coding method.
bits, err := br.Read(2)
if err != nil {
return nil, err
}
method := bits.Uint64()
switch method {
case 0:
// 00: partitioned Rice coding with 4-bit Rice parameter;
// RESIDUAL_CODING_METHOD_PARTITIONED_RICE follows
return h.DecodeRice(br, predOrder)
case 1:
// 01: partitioned Rice coding with 5-bit Rice parameter;
// RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 follows
return h.DecodeRice2(br, predOrder)
}
// 1x: reserved
return nil, fmt.Errorf("Header.DecodeResidual: invalid residual coding method; reserved bit pattern: %02b", method)
}
// DecodeRice decodes and returns a slice of residuals. The residual coding
// method used is partitioned Rice coding with a 4-bit Rice parameter.
//
// ref: http://flac.sourceforge.net/format.html#partitioned_rice
func (h *Header) DecodeRice(br bit.Reader, predOrder int) (residuals []int, err error) {
// Partition order.
bits, err := br.Read(4)
if err != nil {
return nil, err
}
partOrder := bits.Uint64()
// Rice partitions.
partCount := int(math.Pow(2, float64(partOrder)))
partSampleCount := int(h.SampleCount) / partCount
for partNum := 0; partNum < partCount; partNum++ {
// Encoding parameter.
bits, err := br.Read(4)
if err != nil {
return nil, err
}
n := bits.Uint64()
if n == 0x0F {
// 1111: Escape code, meaning the partition is in unencoded binary form
// using n bits per sample; n follows as a 5-bit number.
/// ### [ todo ] ###
/// - not yet implemented.
/// ### [/ todo ] ###
return nil, errors.New("not yet implemented; rice encoding parameter escape code")
}
riceParam := n
_ = riceParam
dbg.Println("riceParam:", riceParam)
// Encoded residual.
sampleCount := partSampleCount
if partNum == 0 {
sampleCount -= predOrder
}
dbg.Println("sampleCount:", sampleCount)
}
/// ### [ todo ] ###
/// - not yet implemented.
/// ### [/ todo ] ###
return nil, errors.New("not yet implemented; rice coding method 0")
}
// DecodeRice2 decodes and returns a slice of residuals. The residual coding
// method used is partitioned Rice coding with a 5-bit Rice parameter.
//
// ref: http://flac.sourceforge.net/format.html#partitioned_rice2
func (h *Header) DecodeRice2(br bit.Reader, predOrder int) (residuals []int, err error) {
/// ### [ todo ] ###
/// - not yet implemented.
/// ### [/ todo ] ###
return nil, errors.New("not yet implemented; rice coding method 1")
}
/**
type SubFrameLpc struct {
Precision uint8
ShiftNeeded uint8
PredictorCoefficients []byte
}
type Rice struct {
Partitions []RicePartition
}
type Rice2 struct {
Partitions []Rice2Partition
}
type RicePartition struct {
EncodingParameter uint16
}
type Rice2Partition struct{}
*/