mirror of https://bitbucket.org/ausocean/av.git
ADPCM: encoder now using byte writer instead of returning byte slices
This commit is contained in:
parent
8f282b1200
commit
edb0ec6de1
|
@ -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.
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue