184 lines
5.4 KiB
Go
184 lines
5.4 KiB
Go
package flac
|
|
|
|
import (
|
|
"bytes"
|
|
"git.gammaspectra.live/S.O.N.G/flacgo/frame"
|
|
iobits "git.gammaspectra.live/S.O.N.G/flacgo/internal/bits"
|
|
"github.com/icza/bitio"
|
|
"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 {
|
|
//Select predictor mode
|
|
predicted := 0
|
|
subHeader := frame.SubHeader{
|
|
Pred: frame.PredVerbatim,
|
|
}
|
|
|
|
for i := frame.PredConstant; i < (frame.PredFIR + 1); i++ {
|
|
shd, estimate := estimateSize(i, hdr, subframe)
|
|
if estimate > 0 {
|
|
if predicted == 0 || estimate < predicted {
|
|
predicted = estimate
|
|
subHeader = shd
|
|
}
|
|
}
|
|
}
|
|
|
|
//Use best
|
|
subframe.SubHeader = subHeader
|
|
|
|
// 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, hdr.BitsPerSample, subframe.Samples); err != nil {
|
|
return errutil.Err(err)
|
|
}
|
|
case frame.PredVerbatim:
|
|
if err := encodeVerbatimSamples(bw, hdr.BlockSize, hdr.BitsPerSample, subframe.Samples); err != nil {
|
|
return errutil.Err(err)
|
|
}
|
|
//case frame.PredFixed:
|
|
// if err := encodeFixedSamples(bw, hdr, subframe.Samples, subframe.Order); err != nil {
|
|
// return errutil.Err(err)
|
|
// }
|
|
//case frame.PredFIR:
|
|
// if err := encodeFIRSamples(bw, hdr, subframe.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
|
|
}
|
|
|
|
func estimateSize(pred frame.Pred, hdr frame.Header, subframe *frame.Subframe) (frame.SubHeader, int) {
|
|
buf := bytes.NewBuffer([]byte{})
|
|
bw := bitio.NewWriter(buf)
|
|
|
|
switch pred {
|
|
case frame.PredConstant:
|
|
if err := encodeConstantSamples(bw, hdr.BitsPerSample, subframe.Samples); err != nil {
|
|
return frame.SubHeader{}, 0
|
|
}
|
|
return frame.SubHeader{
|
|
Pred: pred,
|
|
Order: 0,
|
|
Wasted: 0,
|
|
}, buf.Len()
|
|
case frame.PredVerbatim:
|
|
if err := encodeVerbatimSamples(bw, hdr.BlockSize, hdr.BitsPerSample, subframe.Samples); err != nil {
|
|
return frame.SubHeader{}, 0
|
|
}
|
|
return frame.SubHeader{
|
|
Pred: pred,
|
|
Order: 0,
|
|
Wasted: 0,
|
|
}, buf.Len()
|
|
//case frame.PredFixed:
|
|
// if err := encodeFixedSamples(bw, hdr, subframe.Samples, subframe.Order); err != nil {
|
|
// return errutil.Err(err)
|
|
// }
|
|
//case frame.PredFIR:
|
|
// if err := encodeFIRSamples(bw, hdr, subframe.Samples, subframe.Order); err != nil {
|
|
// return errutil.Err(err)
|
|
// }
|
|
|
|
}
|
|
return frame.SubHeader{}, 0
|
|
}
|
|
|
|
// --- [ 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
|
|
}
|
|
|
|
// --- [ Constant samples ] ----------------------------------------------------
|
|
|
|
// encodeConstantSamples stores the given constant sample, writing to bw.
|
|
func encodeConstantSamples(bw *bitio.Writer, bps byte, samples []int32) error {
|
|
sample := samples[0]
|
|
for _, s := range samples[1:] {
|
|
if sample != s {
|
|
return errutil.Newf("constant sample mismatch; expected %v, got %v", sample, s)
|
|
}
|
|
}
|
|
// Unencoded constant value of the subblock, n = frame's bits-per-sample.
|
|
if err := bw.WriteBits(uint64(sample), bps); 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, blockSize uint16, bps byte, samples []int32) error {
|
|
// Unencoded subblock; n = frame's bits-per-sample, i = frame's blocksize.
|
|
if int(blockSize) != len(samples) {
|
|
return errutil.Newf("block size and sample count mismatch; expected %d, got %d", blockSize, len(samples))
|
|
}
|
|
for _, sample := range samples {
|
|
if err := bw.WriteBits(uint64(sample), bps); err != nil {
|
|
return errutil.Err(err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|