From fdc4d880ac835661bcba2763d917ca7248b30962 Mon Sep 17 00:00:00 2001 From: Trek H Date: Fri, 15 Mar 2019 17:07:22 +1030 Subject: [PATCH] ADPCM: added a Write function to decoder so that it implements io.Writer, and also so that it can decode adpcm of arbitrary length. Updated test and decode command to use Write. --- audio/adpcm/adpcm.go | 86 ++++++++++++++++++++---------- audio/adpcm/adpcm_test.go | 9 ++-- exp/adpcm/decode-pcm/decode-pcm.go | 9 ++-- 3 files changed, 63 insertions(+), 41 deletions(-) diff --git a/audio/adpcm/adpcm.go b/audio/adpcm/adpcm.go index 40613bde..5aaa98b1 100644 --- a/audio/adpcm/adpcm.go +++ b/audio/adpcm/adpcm.go @@ -227,8 +227,9 @@ func (e *Encoder) calcHead(sample []byte) (int, error) { // - First two bytes contain the first 16-bit sample uncompressed. // - Third byte is the decoder's starting index for the block, the fourth is padding and ignored. func (e *Encoder) EncodeBlock(block []byte) (int, error) { + writ := 0 if len(block) != PcmBS { - return 0, fmt.Errorf("unsupported block size. Given: %v, expected: %v, ie. 505 16-bit PCM samples", len(block), PcmBS) + return writ, fmt.Errorf("unsupported block size. Given: %v, expected: %v, ie. 505 16-bit PCM samples", len(block), PcmBS) } writ, err := e.calcHead(block[0:2]) @@ -249,6 +250,50 @@ func (e *Encoder) EncodeBlock(block []byte) (int, error) { return writ, nil } +// DecodeBlock takes a slice of 256 bytes, each byte after the first 4 should contain two ADPCM encoded nibbles. +// It outputs the resulting decoded (decompressed) 16-bit PCM samples to the decoder's dest writer. +func (d *Decoder) DecodeBlock(block []byte) (int, error) { + writ := 0 + if len(block) != AdpcmBS { + return writ, fmt.Errorf("unsupported block size. Given: %v, expected: %v", len(block), AdpcmBS) + } + + // Initialize decoder with first 4 bytes of the block. + d.pred = int16(binary.LittleEndian.Uint16(block[0:2])) + d.index = int16(block[2]) + d.step = stepTable[d.index] + writ, err := d.dest.Write(block[0:2]) + if err != nil { + return writ, err + } + + // For each byte, seperate it into two nibbles (each nibble is a compressed sample), + // then decode each nibble and output the resulting 16-bit samples. + for i := 4; i < AdpcmBS; i++ { + twoNibs := block[i] + nib2 := byte(twoNibs >> 4) + nib1 := byte((nib2 << 4) ^ twoNibs) + + firstBytes := make([]byte, 2) + binary.LittleEndian.PutUint16(firstBytes, uint16(d.decodeSample(nib1))) + writS, err := d.dest.Write(firstBytes) + writ += writS + if err != nil { + return writ, err + } + + secondBytes := make([]byte, 2) + binary.LittleEndian.PutUint16(secondBytes, uint16(d.decodeSample(nib2))) + writS, err = d.dest.Write(secondBytes) + writ += writS + if err != nil { + return writ, err + } + } + + return writ, nil +} + func (e *Encoder) Write(inPcm []byte) (int, error) { numBlocks := len(inPcm) / PcmBS writ := 0 @@ -264,34 +309,17 @@ func (e *Encoder) Write(inPcm []byte) (int, error) { return writ, nil } -// DecodeBlock takes a slice of 256 bytes, each byte after the first 4 should contain two ADPCM encoded nibbles. -// It outputs the resulting decoded (decompressed) 16-bit PCM samples to the decoder's dest writer. -func (d *Decoder) DecodeBlock(block []byte) error { - if len(block) != AdpcmBS { - return fmt.Errorf("unsupported block size. Given: %v, expected: %v", len(block), AdpcmBS) +func (d *Decoder) Write(inAdpcm []byte) (int, error) { + numBlocks := len(inAdpcm) / AdpcmBS + writ := 0 + for i := 0; i < numBlocks; i++ { + block := inAdpcm[AdpcmBS*i : AdpcmBS*(i+1)] + writB, err := d.DecodeBlock(block) + writ += writB + if err != nil { + return writ, err + } } - // Initialize decoder with first 4 bytes of the block. - d.pred = int16(binary.LittleEndian.Uint16(block[0:2])) - d.index = int16(block[2]) - d.step = stepTable[d.index] - d.dest.Write(block[0:2]) - - // For each byte, seperate it into two nibbles (each nibble is a compressed sample), - // then decode each nibble and output the resulting 16-bit samples. - for i := 4; i < AdpcmBS; i++ { - twoNibs := block[i] - nib2 := byte(twoNibs >> 4) - nib1 := byte((nib2 << 4) ^ twoNibs) - - firstBytes := make([]byte, 2) - binary.LittleEndian.PutUint16(firstBytes, uint16(d.decodeSample(nib1))) - d.dest.Write(firstBytes) - - secondBytes := make([]byte, 2) - binary.LittleEndian.PutUint16(secondBytes, uint16(d.decodeSample(nib2))) - d.dest.Write(secondBytes) - } - - return nil + return writ, nil } diff --git a/audio/adpcm/adpcm_test.go b/audio/adpcm/adpcm_test.go index cf1a4c71..9df24028 100644 --- a/audio/adpcm/adpcm_test.go +++ b/audio/adpcm/adpcm_test.go @@ -75,12 +75,9 @@ func TestDecodeBlock(t *testing.T) { numBlocks := len(comp) / AdpcmBS decoded := bytes.NewBuffer(make([]byte, 0, PcmBS*numBlocks)) dec := NewDecoder(decoded) - for i := 0; i < numBlocks; i++ { - block := comp[AdpcmBS*i : AdpcmBS*(i+1)] - err := dec.DecodeBlock(block) - if err != nil { - t.Errorf("Unable to write to decoder: %v", err) - } + _, err = dec.Write(comp) + if err != nil { + t.Errorf("Unable to write to decoder: %v", err) } // Read expected pcm file. diff --git a/exp/adpcm/decode-pcm/decode-pcm.go b/exp/adpcm/decode-pcm/decode-pcm.go index 2d5f2625..3b739262 100644 --- a/exp/adpcm/decode-pcm/decode-pcm.go +++ b/exp/adpcm/decode-pcm/decode-pcm.go @@ -57,12 +57,9 @@ func main() { numBlocks := len(comp) / adpcm.AdpcmBS decoded := bytes.NewBuffer(make([]byte, 0, adpcm.PcmBS*numBlocks)) dec := adpcm.NewDecoder(decoded) - for i := 0; i < numBlocks; i++ { - block := comp[adpcm.AdpcmBS*i : adpcm.AdpcmBS*(i+1)] - err := dec.DecodeBlock(block) - if err != nil { - log.Fatal(err) - } + _, err = dec.Write(comp) + if err != nil { + log.Fatal(err) } // Save pcm to file.