adpcm: naming and syntactical changes

This commit is contained in:
Trek H 2019-05-29 02:57:17 +09:30
parent a1fe6c6deb
commit 762653b59a
1 changed files with 39 additions and 47 deletions

View File

@ -27,7 +27,7 @@ LICENSE
Reference algorithms for ADPCM compression and decompression are in part 6.
*/
// Package adpcm provides Encoder and Decoder structs to encode and decode PCM to and from ADPCM.
// Package adpcm provides functions to transcode between PCM and ADPCM.
package adpcm
import (
@ -38,13 +38,13 @@ import (
)
const (
byteDepth = 2 // TODO(Trek): make configurable.
initSamps = 2
byteDepth = 2 // We are working with 16-bit samples. TODO(Trek): make configurable.
initSamps = 2 // Number of samples used to initialise the encoder.
initBytes = initSamps * byteDepth
headBytes = 4
samplesPerEnc = 2
headBytes = 4 // Number of bytes in the header of ADPCM.
samplesPerEnc = 2 // Number of sample encoded at a time eg. 2 16-bit samples get encoded into 1 byte.
bytesPerEnc = samplesPerEnc * byteDepth
compFact = 4
compFact = 4 // In general ADPCM compresses by a factor of 4.
)
// Table of index changes (see spec).
@ -71,31 +71,26 @@ var stepTable = []int16{
// Encoder is used to encode to ADPCM from PCM data.
type Encoder struct {
// dst is the destination for encoded data.
// dst is the destination for ADPCM-encoded data.
dst io.Writer
// est and index hold state that persists between calls to encodeSample and calcHead.
est int16
index int16
est int16 // Estimation of sample based on quantised ADPCM nibble.
idx int16 // Index to step used for estimation.
}
// Decoder is used to decode from ADPCM to PCM data.
type Decoder struct {
// dst is the output buffer that implements io.Writer and io.Bytewriter, ie. where the decoded PCM data is written to.
// dst is the destination for PCM-encoded data.
dst io.Writer
// est, index, and step hold state that persists between calls to decodeSample.
est int16
index int16
step int16
est int16 // Estimation of sample based on quantised ADPCM nibble.
idx int16 // Index to step used for estimation.
step int16
}
// NewEncoder retuns a new ADPCM Encoder.
func NewEncoder(dst io.Writer) *Encoder {
e := Encoder{
dst: dst,
}
return &e
return &Encoder{dst: dst}
}
// encodeSample takes a single 16 bit PCM sample and
@ -111,7 +106,7 @@ func (e *Encoder) encodeSample(sample int16) byte {
delta = -delta
}
step := stepTable[e.index]
step := stepTable[e.idx]
diff := step >> 3
var mask byte = 4
@ -132,13 +127,13 @@ func (e *Encoder) encodeSample(sample int16) byte {
// Adjust estimated sample based on calculated difference.
e.est = capAdd16(e.est, diff)
e.index += indexTable[nib&7]
e.idx += indexTable[nib&7]
// Check for underflow and overflow.
if e.index < 0 {
e.index = 0
} else if e.index > int16(len(stepTable)-1) {
e.index = int16(len(stepTable) - 1)
if e.idx < 0 {
e.idx = 0
} else if e.idx > int16(len(stepTable)-1) {
e.idx = int16(len(stepTable) - 1)
}
return nib
@ -146,7 +141,7 @@ func (e *Encoder) encodeSample(sample int16) byte {
// calcHead sets the state for the Encoder by running the first sample through
// the Encoder, and writing the first sample to the Encoder's io.Writer (dst).
// It returns the number of bytes written to the Encoder's io.Writer (dst) along with any errors.
// It returns the number of bytes written to the Encoder's destination and the first error encountered.
func (e *Encoder) calcHead(sample []byte, pad bool) (int, error) {
// Check that we are given 1 sample.
if len(sample) != byteDepth {
@ -158,7 +153,7 @@ func (e *Encoder) calcHead(sample []byte, pad bool) (int, error) {
return n, err
}
_n, err := e.dst.Write([]byte{byte(int16(e.index))})
_n, err := e.dst.Write([]byte{byte(int16(e.idx))})
if err != nil {
return n, err
}
@ -184,7 +179,7 @@ func (e *Encoder) init(samples []byte) {
int2 := int16(binary.LittleEndian.Uint16(samples[byteDepth:initBytes]))
e.est = int1
halfDiff := math.Abs(math.Abs(float64(int1)) - math.Abs(float64(int2))/2.0)
halfDiff := math.Abs(math.Abs(float64(int1)) - math.Abs(float64(int2))/2)
closest := math.Abs(float64(stepTable[0]) - halfDiff)
var cInd int16
for i, step := range stepTable {
@ -193,7 +188,7 @@ func (e *Encoder) init(samples []byte) {
cInd = int16(i)
}
}
e.index = cInd
e.idx = cInd
}
// Write takes a slice of bytes of arbitrary length representing pcm and encodes it into adpcm.
@ -242,10 +237,7 @@ func (e *Encoder) Write(b []byte) (int, error) {
// NewDecoder retuns a new ADPCM Decoder.
func NewDecoder(dst io.Writer) *Decoder {
d := Decoder{
dst: dst,
}
return &d
return &Decoder{dst: dst}
}
// decodeSample takes a byte, the last 4 bits of which contain a single
@ -273,17 +265,17 @@ func (d *Decoder) decodeSample(nibble byte) int16 {
d.est = capAdd16(d.est, diff)
// Adjust index into step size lookup table using nibble.
d.index += indexTable[nibble]
d.idx += indexTable[nibble]
// Check for overflow and underflow.
if d.index < 0 {
d.index = 0
} else if d.index > int16(len(stepTable)-1) {
d.index = int16(len(stepTable) - 1)
if d.idx < 0 {
d.idx = 0
} else if d.idx > int16(len(stepTable)-1) {
d.idx = int16(len(stepTable) - 1)
}
// Find new quantizer step size.
d.step = stepTable[d.index]
d.step = stepTable[d.idx]
return d.est
}
@ -294,8 +286,8 @@ func (d *Decoder) decodeSample(nibble byte) int16 {
func (d *Decoder) Write(b []byte) (int, error) {
// Initialize Decoder with first 4 bytes of b.
d.est = int16(binary.LittleEndian.Uint16(b[:byteDepth]))
d.index = int16(b[byteDepth])
d.step = stepTable[d.index]
d.idx = int16(b[byteDepth])
d.step = stepTable[d.idx]
n, err := d.dst.Write(b[:byteDepth])
if err != nil {
return n, err
@ -351,13 +343,13 @@ func capAdd16(a, b int16) int16 {
}
}
// EncBytes will return the number of adpcm bytes that will be generated when encoding the given amount of pcm bytes (len).
func EncBytes(len int) int {
// For 'len' pcm bytes, 1 sample is left uncompressed, the rest is compressed by a factor of 4
// EncBytes will return the number of adpcm bytes that will be generated when encoding the given amount of pcm bytes (n).
func EncBytes(n int) int {
// For 'n' pcm bytes, 1 sample is left uncompressed, the rest is compressed by a factor of 4
// and a start index and padding-flag byte are added.
// Also if there are an even number of samples, there will be half a byte of padding added to the last byte.
if len%bytesPerEnc == 0 {
return (len-byteDepth)/compFact + headBytes + 1
if n%bytesPerEnc == 0 {
return (n-byteDepth)/compFact + headBytes + 1
}
return (len-byteDepth)/compFact + headBytes
return (n-byteDepth)/compFact + headBytes
}