ADPCM: encoder now using byte writer instead of returning byte slices

This commit is contained in:
Trek H 2019-02-28 12:53:51 +10:30
parent 8f282b1200
commit edb0ec6de1
2 changed files with 21 additions and 23 deletions

View File

@ -43,7 +43,7 @@ import (
// pred and index hold state that persists between calls to encodeSample and calcHead. // pred and index hold state that persists between calls to encodeSample and calcHead.
// dest is the output, ie. where the encoded ADPCM data is written to. // dest is the output, ie. where the encoded ADPCM data is written to.
type Encoder struct { type Encoder struct {
dest io.Writer dest io.ByteWriter
pred int16 pred int16
index int16 index int16
} }
@ -52,7 +52,7 @@ type Encoder struct {
// pred, index, and step hold state that persists between calls to decodeSample. // pred, index, and step hold state that persists between calls to decodeSample.
// dest is the output, ie. where the decoded PCM data is written to. // dest is the output, ie. where the decoded PCM data is written to.
type Decoder struct { type Decoder struct {
dest io.Writer dest io.ByteWriter
pred int16 pred int16
index int16 index int16
step int16 step int16
@ -81,7 +81,7 @@ var stepTable = []int16{
} }
// NewEncoder retuns a new ADPCM encoder. // NewEncoder retuns a new ADPCM encoder.
func NewEncoder(dst io.Writer) *Encoder { func NewEncoder(dst io.ByteWriter) *Encoder {
e := Encoder{ e := Encoder{
dest: dst, dest: dst,
} }
@ -89,7 +89,7 @@ func NewEncoder(dst io.Writer) *Encoder {
} }
// NewDecoder retuns a new ADPCM decoder. // NewDecoder retuns a new ADPCM decoder.
func NewDecoder(dst io.Writer) *Decoder { func NewDecoder(dst io.ByteWriter) *Decoder {
d := Decoder{ d := Decoder{
step: stepTable[0], step: stepTable[0],
dest: dst, dest: dst,
@ -203,48 +203,47 @@ func capAdd16(a, b int16) int16 {
} }
} }
func (e *Encoder) calcHead(sample []byte) ([]byte, error) { func (e *Encoder) calcHead(sample []byte) error {
// check that we are given 1 16-bit sample (2 bytes) // check that we are given 1 16-bit sample (2 bytes)
sampSize := 2 sampSize := 2
if len(sample) != sampSize { if len(sample) != sampSize {
return nil, fmt.Errorf("length of given byte array is: %v, expected: %v", len(sample), sampSize) return fmt.Errorf("length of given byte array is: %v, expected: %v", len(sample), sampSize)
} }
intSample := int16(binary.LittleEndian.Uint16(sample)) intSample := int16(binary.LittleEndian.Uint16(sample))
e.encodeSample(intSample) e.encodeSample(intSample)
head := make([]byte, 2, 4) e.dest.WriteByte(sample[0])
head[0] = sample[0] e.dest.WriteByte(sample[1])
head[1] = sample[1]
head = append(head, byte(uint16(e.index))) e.dest.WriteByte(byte(uint16(e.index)))
head = append(head, byte(0x00)) e.dest.WriteByte(byte(0x00))
return head, nil return nil
} }
// EncodeBlock takes a slice of 1010 bytes (505 16-bit PCM samples). // EncodeBlock takes a slice of 1010 bytes (505 16-bit PCM samples).
// It returns a byte slice containing encoded (compressed) ADPCM nibbles (each byte contains two nibbles). // It returns a byte slice containing encoded (compressed) ADPCM nibbles (each byte contains two nibbles).
func (e *Encoder) EncodeBlock(block []byte) ([]byte, error) { func (e *Encoder) EncodeBlock(block []byte) error {
bSize := 1010 bSize := 1010
if len(block) != bSize { if len(block) != bSize {
return nil, fmt.Errorf("unsupported block size. Given: %v, expected: %v, ie. 505 16-bit PCM samples", len(block), bSize) return fmt.Errorf("unsupported block size. Given: %v, expected: %v, ie. 505 16-bit PCM samples", len(block), bSize)
} }
result, err := e.calcHead(block[0:2]) err := e.calcHead(block[0:2])
if err != nil { if err != nil {
return nil, err return err
} }
for i := 2; i < len(block); i++ { for i := 2; i < len(block); i++ {
if (i+1)%4 == 0 { if (i+1)%4 == 0 {
sample2 := e.encodeSample(int16(binary.LittleEndian.Uint16(block[i-1 : i+1]))) sample2 := e.encodeSample(int16(binary.LittleEndian.Uint16(block[i-1 : i+1])))
sample := e.encodeSample(int16(binary.LittleEndian.Uint16(block[i+1 : i+3]))) sample := e.encodeSample(int16(binary.LittleEndian.Uint16(block[i+1 : i+3])))
result = append(result, byte((sample<<4)|sample2)) e.dest.WriteByte(byte((sample << 4) | sample2))
} }
} }
return result, nil return nil
} }
// DecodeBlock takes a slice of 256 bytes, each byte should contain two ADPCM encoded nibbles. // DecodeBlock takes a slice of 256 bytes, each byte should contain two ADPCM encoded nibbles.

View File

@ -47,15 +47,14 @@ func TestEncodeBlock(t *testing.T) {
inBSize := 1010 inBSize := 1010
numBlocks := len(pcm) / inBSize numBlocks := len(pcm) / inBSize
outBSize := int(float64(inBSize)/4 + 3.5) // compression is 4:1 and 3.5 bytes of info are added to each block outBSize := int(float64(inBSize)/4 + 3.5) // compression is 4:1 and 3.5 bytes of info are added to each block
comp := make([]byte, 0, outBSize*numBlocks) comp := bytes.NewBuffer(make([]byte, 0, outBSize*numBlocks))
enc := NewEncoder(nil) enc := NewEncoder(comp)
for i := 0; i < numBlocks; i++ { for i := 0; i < numBlocks; i++ {
block := pcm[inBSize*i : inBSize*(i+1)] block := pcm[inBSize*i : inBSize*(i+1)]
encBlock, err := enc.EncodeBlock(block) err := enc.EncodeBlock(block)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
comp = append(comp, encBlock...)
} }
//read expected adpcm file //read expected adpcm file
@ -64,7 +63,7 @@ func TestEncodeBlock(t *testing.T) {
t.Errorf("Unable to read expected ADPCM file: %v", err) t.Errorf("Unable to read expected ADPCM file: %v", err)
} }
if !bytes.Equal(comp, exp) { if !bytes.Equal(comp.Bytes(), exp) {
t.Error("ADPCM generated does not match expected ADPCM") t.Error("ADPCM generated does not match expected ADPCM")
} }
} }