mirror of https://bitbucket.org/ausocean/av.git
adpcm: naming and syntactical changes
This commit is contained in:
parent
a1fe6c6deb
commit
762653b59a
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue