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 }