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.
This commit is contained in:
Trek H 2019-03-15 17:07:22 +10:30
parent c7c7ef75f5
commit fdc4d880ac
3 changed files with 63 additions and 41 deletions

View File

@ -227,8 +227,9 @@ func (e *Encoder) calcHead(sample []byte) (int, error) {
// - First two bytes contain the first 16-bit sample uncompressed. // - 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. // - 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) { func (e *Encoder) EncodeBlock(block []byte) (int, error) {
writ := 0
if len(block) != PcmBS { 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]) writ, err := e.calcHead(block[0:2])
@ -249,6 +250,50 @@ func (e *Encoder) EncodeBlock(block []byte) (int, error) {
return writ, nil 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) { func (e *Encoder) Write(inPcm []byte) (int, error) {
numBlocks := len(inPcm) / PcmBS numBlocks := len(inPcm) / PcmBS
writ := 0 writ := 0
@ -264,34 +309,17 @@ func (e *Encoder) Write(inPcm []byte) (int, error) {
return writ, nil return writ, nil
} }
// DecodeBlock takes a slice of 256 bytes, each byte after the first 4 should contain two ADPCM encoded nibbles. func (d *Decoder) Write(inAdpcm []byte) (int, error) {
// It outputs the resulting decoded (decompressed) 16-bit PCM samples to the decoder's dest writer. numBlocks := len(inAdpcm) / AdpcmBS
func (d *Decoder) DecodeBlock(block []byte) error { writ := 0
if len(block) != AdpcmBS { for i := 0; i < numBlocks; i++ {
return fmt.Errorf("unsupported block size. Given: %v, expected: %v", len(block), AdpcmBS) 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. return writ, nil
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
} }

View File

@ -75,13 +75,10 @@ func TestDecodeBlock(t *testing.T) {
numBlocks := len(comp) / AdpcmBS numBlocks := len(comp) / AdpcmBS
decoded := bytes.NewBuffer(make([]byte, 0, PcmBS*numBlocks)) decoded := bytes.NewBuffer(make([]byte, 0, PcmBS*numBlocks))
dec := NewDecoder(decoded) dec := NewDecoder(decoded)
for i := 0; i < numBlocks; i++ { _, err = dec.Write(comp)
block := comp[AdpcmBS*i : AdpcmBS*(i+1)]
err := dec.DecodeBlock(block)
if err != nil { if err != nil {
t.Errorf("Unable to write to decoder: %v", err) t.Errorf("Unable to write to decoder: %v", err)
} }
}
// Read expected pcm file. // Read expected pcm file.
exp, err := ioutil.ReadFile("../../../test/test-data/av/output/decoded-voice.pcm") exp, err := ioutil.ReadFile("../../../test/test-data/av/output/decoded-voice.pcm")

View File

@ -57,13 +57,10 @@ func main() {
numBlocks := len(comp) / adpcm.AdpcmBS numBlocks := len(comp) / adpcm.AdpcmBS
decoded := bytes.NewBuffer(make([]byte, 0, adpcm.PcmBS*numBlocks)) decoded := bytes.NewBuffer(make([]byte, 0, adpcm.PcmBS*numBlocks))
dec := adpcm.NewDecoder(decoded) dec := adpcm.NewDecoder(decoded)
for i := 0; i < numBlocks; i++ { _, err = dec.Write(comp)
block := comp[adpcm.AdpcmBS*i : adpcm.AdpcmBS*(i+1)]
err := dec.DecodeBlock(block)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
}
// Save pcm to file. // Save pcm to file.
err = ioutil.WriteFile(outPath, decoded.Bytes(), 0644) err = ioutil.WriteFile(outPath, decoded.Bytes(), 0644)