From 28f458bc53919d0197c1643f2618f64d6e19913e Mon Sep 17 00:00:00 2001 From: WeebDataHoarder <57538841+WeebDataHoarder@users.noreply.github.com> Date: Tue, 26 Jul 2022 17:10:57 +0200 Subject: [PATCH] Automatically select best subframe encoding predictor --- cmd/wav2flac/main.go | 25 ----------------- encode_subframe.go | 66 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/cmd/wav2flac/main.go b/cmd/wav2flac/main.go index d0c97bb..293a7f9 100644 --- a/cmd/wav2flac/main.go +++ b/cmd/wav2flac/main.go @@ -121,16 +121,6 @@ func wav2flac(wavPath string, force bool) error { //break } for _, subframe := range subframes { - subHdr := frame.SubHeader{ - // Specifies the prediction method used to encode the audio sample of the - // subframe. - Pred: frame.PredVerbatim, - // Prediction order used by fixed and FIR linear prediction decoding. - Order: 0, - // Wasted bits-per-sample. - Wasted: 0, - } - subframe.SubHeader = subHdr subframe.NSamples = n / nchannels subframe.Samples = subframe.Samples[:subframe.NSamples] } @@ -138,21 +128,6 @@ func wav2flac(wavPath string, force bool) error { subframe := subframes[i%nchannels] subframe.Samples[i/nchannels] = int32(sample) } - // Check if the subframe may be encoded as constant; when all samples are - // the same. - for _, subframe := range subframes { - sample := subframe.Samples[0] - constant := true - for _, s := range subframe.Samples[1:] { - if sample != s { - constant = false - } - } - if constant { - fmt.Println("constant method") - subframe.SubHeader.Pred = frame.PredConstant - } - } // Encode FLAC frame. channels, err := getChannels(nchannels) diff --git a/encode_subframe.go b/encode_subframe.go index 6c2c39a..4105bf2 100644 --- a/encode_subframe.go +++ b/encode_subframe.go @@ -1,6 +1,7 @@ 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" @@ -11,6 +12,25 @@ import ( // 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) @@ -23,7 +43,7 @@ func encodeSubframe(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe return errutil.Err(err) } case frame.PredVerbatim: - if err := encodeVerbatimSamples(bw, hdr, subframe.Samples); err != nil { + if err := encodeVerbatimSamples(bw, hdr.BlockSize, hdr.BitsPerSample, subframe.Samples); err != nil { return errutil.Err(err) } //case frame.PredFixed: @@ -40,6 +60,42 @@ func encodeSubframe(bw *bitio.Writer, hdr frame.Header, subframe *frame.Subframe 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. @@ -113,13 +169,13 @@ func encodeConstantSamples(bw *bitio.Writer, bps byte, samples []int32) error { // encodeVerbatimSamples stores the given samples verbatim (uncompressed), // writing to bw. -func encodeVerbatimSamples(bw *bitio.Writer, hdr frame.Header, samples []int32) error { +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(hdr.BlockSize) != len(samples) { - return errutil.Newf("block size and sample count mismatch; expected %d, got %d", hdr.BlockSize, len(samples)) + 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), hdr.BitsPerSample); err != nil { + if err := bw.WriteBits(uint64(sample), bps); err != nil { return errutil.Err(err) } }