Skip to content

Commit

Permalink
Added zero allocation functions
Browse files Browse the repository at this point in the history
Fucntions to encode/decode/transcode using user provided buffers.
  • Loading branch information
zaf committed Dec 14, 2024
1 parent 10c9a56 commit 10be6f3
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 32 deletions.
88 changes: 68 additions & 20 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# g711

[![GoDoc](https://img.shields.io/badge/pkg.go.dev-doc-blue)](http://pkg.go.dev/.)

Package g711 implements encoding and decoding of G711 PCM sound data.
G.711 is an ITU-T standard for audio companding.

The package exposes a high level API for encoding and decoding through
an io.WriteCloser. But also a low level API using preallocated buffers
for cases where performance and memory handling are critical.

For usage details please see the code snippet in the cmd folder.

## Constants
Expand All @@ -26,105 +32,147 @@ Coder encodes 16bit 8000Hz LPCM data to G711 PCM, or
decodes G711 PCM data to 16bit 8000Hz LPCM data, or
directly transcodes between A-law and u-law

#### func [NewCoder](/g711.go#L40)

`func NewCoder(writer io.Writer, input, output int) (*Coder, error)`

NewCoder returns a pointer to a Coder that implements an io.WriteCloser.
It takes as input the destination data Writer and the input/output encoding formats.

#### func (*Coder) [Close](/g711.go#L90)
#### func (*Coder) [Close](/g711.go#L96)

`func (w *Coder) Close() error`

Close closes the Encoder, it implements the io.Closer interface.

#### func (*Coder) [Reset](/g711.go#L96)
#### func (*Coder) [Reset](/g711.go#L105)

`func (w *Coder) Reset(writer io.Writer) error`

Reset discards the Encoder state. This permits reusing an Encoder rather than allocating a new one.

#### func (*Coder) [Write](/g711.go#L107)
#### func (*Coder) [Write](/g711.go#L119)

`func (w *Coder) Write(p []byte) (int, error)`

Write encodes/decodes/transcodes sound data. Writes len(p) bytes from p to the underlying data stream,
returns the number of bytes written from p (0 <= n <= len(p)) and any error encountered
that caused the write to stop early.


## Functions

### func [Alaw2Ulaw](/alaw.go#L115)
### func [Alaw2Ulaw](/alaw.go#L129)

`func Alaw2Ulaw(alaw []byte) []byte`

Alaw2Ulaw performs direct A-law to u-law data conversion

### func [Alaw2UlawFrame](/alaw.go#L124)
### func [Alaw2UlawFrame](/alaw.go#L145)

`func Alaw2UlawFrame(frame uint8) uint8`

Alaw2UlawFrame directly converts an A-law frame to u-law

### func [DecodeAlaw](/alaw.go#L99)
### func [Alaw2UlawTo](/alaw.go#L138)

`func Alaw2UlawTo(alaw, ulaw []byte)`

Alaw2UlawTo performs direct A-law to u-law data conversion
using an already allocated buffer provided by the user.
The user is responsible for ensuring that the buffer is large enough (the size of the A-law data).

### func [DecodeAlaw](/alaw.go#L106)

`func DecodeAlaw(pcm []byte) []byte`

DecodeAlaw decodes A-law PCM data to 16bit LPCM

### func [DecodeAlawFrame](/alaw.go#L110)
### func [DecodeAlawFrame](/alaw.go#L124)

`func DecodeAlawFrame(frame uint8) int16`

DecodeAlawFrame decodes an A-law PCM frame to 16bit LPCM

### func [DecodeUlaw](/ulaw.go#L103)
### func [DecodeAlawTo](/alaw.go#L115)

`func DecodeAlawTo(pcm, lpcm []byte)`

DecodeAlawTo decodes A-law PCM data to 16bit LPCM
using an already allocated buffer provided by the user.
The user is responsible for ensuring that the buffer is large enough (double the size of the PCM data).

### func [DecodeUlaw](/ulaw.go#L110)

`func DecodeUlaw(pcm []byte) []byte`

DecodeUlaw decodes u-law PCM data to 16bit LPCM

### func [DecodeUlawFrame](/ulaw.go#L114)
### func [DecodeUlawFrame](/ulaw.go#L128)

`func DecodeUlawFrame(frame uint8) int16`

DecodeUlawFrame decodes a u-law PCM frame to 16bit LPCM

### func [DecodeUlawTo](/ulaw.go#L119)

`func DecodeUlawTo(pcm, lpcm []byte)`

DecodeUlawTo decodes u-law PCM data to 16bit LPCM
using an already allocated buffer provided by the user.
The user is responsible for ensuring that the buffer is large enough (double the size of the PCM data).

### func [EncodeAlaw](/alaw.go#L74)

`func EncodeAlaw(lpcm []byte) []byte`

EncodeAlaw encodes 16bit LPCM data to G711 A-law PCM

### func [EncodeAlawFrame](/alaw.go#L83)
### func [EncodeAlawFrame](/alaw.go#L90)

`func EncodeAlawFrame(frame int16) uint8`

EncodeAlawFrame encodes a 16bit LPCM frame to G711 A-law PCM

### func [EncodeAlawTo](/alaw.go#L83)

`func EncodeAlawTo(lpcm, alaw []byte)`

EncodeAlawTo encodes 16bit LPCM data to G711 A-law PCM
using an already allocated buffer provided by the user.
The user is responsible for ensuring that the buffer is large enough (half the size of the LPCM data).

### func [EncodeUlaw](/ulaw.go#L79)

`func EncodeUlaw(lpcm []byte) []byte`

EncodeUlaw encodes 16bit LPCM data to G711 u-law PCM

### func [EncodeUlawFrame](/ulaw.go#L88)
### func [EncodeUlawFrame](/ulaw.go#L95)

`func EncodeUlawFrame(frame int16) uint8`

EncodeUlawFrame encodes a 16bit LPCM frame to G711 u-law PCM

### func [Ulaw2Alaw](/ulaw.go#L119)
### func [EncodeUlawTo](/ulaw.go#L88)

`func EncodeUlawTo(lpcm, ulaw []byte)`

EncodeUlawTo encodes 16bit LPCM data to G711 u-law PCM
using an already allocated buffer provided by the user.
The user is responsible for ensuring that the buffer is large enough (half the size of the LPCM data).

### func [Ulaw2Alaw](/ulaw.go#L133)

`func Ulaw2Alaw(ulaw []byte) []byte`

Ulaw2Alaw performs direct u-law to A-law data conversion

### func [Ulaw2AlawFrame](/ulaw.go#L128)
### func [Ulaw2AlawFrame](/ulaw.go#L149)

`func Ulaw2AlawFrame(frame uint8) uint8`

Ulaw2AlawFrame directly converts a u-law frame to A-law

### func [Ulaw2AlawTo](/ulaw.go#L142)

`func Ulaw2AlawTo(ulaw, alaw []byte)`

Ulaw2AlawTo performs direct u-law to A-law data conversion
using an already allocated buffer provided by the user.
The user is responsible for ensuring that the buffer is large enough (the size of the A-law data).

---
27 changes: 24 additions & 3 deletions alaw.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,17 @@ var (
// EncodeAlaw encodes 16bit LPCM data to G711 A-law PCM
func EncodeAlaw(lpcm []byte) []byte {
alaw := make([]byte, len(lpcm)>>1)
EncodeAlawTo(lpcm, alaw)
return alaw
}

// EncodeAlawTo encodes 16bit LPCM data to G711 A-law PCM
// using an already allocated buffer provided by the user.
// The user is responsible for ensuring that the buffer is large enough (half the size of the LPCM data).
func EncodeAlawTo(lpcm, alaw []byte) {
for i := 0; i < len(lpcm)-1; i += 2 {
alaw[i>>1] = EncodeAlawFrame(int16(lpcm[i]) | int16(lpcm[i+1])<<8)
}
return alaw
}

// EncodeAlawFrame encodes a 16bit LPCM frame to G711 A-law PCM
Expand All @@ -98,12 +105,19 @@ func EncodeAlawFrame(frame int16) uint8 {
// DecodeAlaw decodes A-law PCM data to 16bit LPCM
func DecodeAlaw(pcm []byte) []byte {
lpcm := make([]byte, len(pcm)*2)
DecodeAlawTo(pcm, lpcm)
return lpcm
}

// DecodeAlawTo decodes A-law PCM data to 16bit LPCM
// using an already allocated buffer provided by the user.
// The user is responsible for ensuring that the buffer is large enough (double the size of the PCM data).
func DecodeAlawTo(pcm, lpcm []byte) {
for i := 0; i < len(pcm); i++ {
frame := alaw2lpcm[pcm[i]]
lpcm[i*2] = byte(frame)
lpcm[i*2+1] = byte(frame >> 8)
}
return lpcm
}

// DecodeAlawFrame decodes an A-law PCM frame to 16bit LPCM
Expand All @@ -114,10 +128,17 @@ func DecodeAlawFrame(frame uint8) int16 {
// Alaw2Ulaw performs direct A-law to u-law data conversion
func Alaw2Ulaw(alaw []byte) []byte {
ulaw := make([]byte, len(alaw))
Alaw2UlawTo(alaw, ulaw)
return ulaw
}

// Alaw2UlawTo performs direct A-law to u-law data conversion
// using an already allocated buffer provided by the user.
// The user is responsible for ensuring that the buffer is large enough (the size of the A-law data).
func Alaw2UlawTo(alaw, ulaw []byte) {
for i := 0; i < len(alaw); i++ {
ulaw[i] = alaw2ulaw[alaw[i]]
}
return ulaw
}

// Alaw2UlawFrame directly converts an A-law frame to u-law
Expand Down
54 changes: 51 additions & 3 deletions alaw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,23 @@ func BenchmarkEncodeAlaw(b *testing.B) {
b.SetBytes(int64(len(rawData)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
EncodeAlaw(rawData)
alaw := EncodeAlaw(rawData)
_ = alaw
}
}

// Benchmark EncodeAlawTo
func BenchmarkEncodeAlawTo(b *testing.B) {
rawData, err := os.ReadFile("testing/speech.raw")
if err != nil {
b.Fatalf("Failed to read test data: %s\n", err)
}
alaw := make([]byte, len(rawData)>>1)
b.SetBytes(int64(len(rawData)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
EncodeAlawTo(rawData, alaw)
_ = alaw
}
}

Expand All @@ -38,7 +54,23 @@ func BenchmarkDecodeAlaw(b *testing.B) {
b.SetBytes(int64(len(aData)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
DecodeAlaw(aData)
lpcm := DecodeAlaw(aData)
_ = lpcm
}
}

// Benchmark DecodeAlawTo
func BenchmarkDecodeAlawTo(b *testing.B) {
aData, err := os.ReadFile("testing/speech.alaw")
if err != nil {
b.Fatalf("Failed to read test data: %s\n", err)
}
lpcm := make([]byte, len(aData)<<1)
b.SetBytes(int64(len(aData)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
DecodeAlawTo(aData, lpcm)
_ = lpcm
}
}

Expand All @@ -51,6 +83,22 @@ func BenchmarkAlaw2Ulaw(b *testing.B) {
b.SetBytes(int64(len(aData)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
Alaw2Ulaw(aData)
ulaw := Alaw2Ulaw(aData)
_ = ulaw
}
}

// Benchmark Alaw2UlawTo
func BenchmarkAlaw2UlawTo(b *testing.B) {
aData, err := os.ReadFile("testing/speech.alaw")
if err != nil {
b.Fatalf("Failed to read test data: %s\n", err)
}
ulaw := make([]byte, len(aData))
b.SetBytes(int64(len(aData)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
Alaw2UlawTo(aData, ulaw)
_ = ulaw
}
}
3 changes: 3 additions & 0 deletions g711.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
Package g711 implements encoding and decoding of G711 PCM sound data.
G.711 is an ITU-T standard for audio companding.
The package exposes a high level API for encoding and decoding through
an io.WriteCloser. But also a low level API using preallocated buffers
for cases where performance and memory handling are critical.
For usage details please see the code snippet in the cmd folder.
*/
package g711
Expand Down
27 changes: 24 additions & 3 deletions ulaw.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,17 @@ var (
// EncodeUlaw encodes 16bit LPCM data to G711 u-law PCM
func EncodeUlaw(lpcm []byte) []byte {
ulaw := make([]byte, len(lpcm)>>1)
EncodeUlawTo(lpcm, ulaw)
return ulaw
}

// EncodeUlawTo encodes 16bit LPCM data to G711 u-law PCM
// using an already allocated buffer provided by the user.
// The user is responsible for ensuring that the buffer is large enough (half the size of the LPCM data).
func EncodeUlawTo(lpcm, ulaw []byte) {
for i := 0; i < len(lpcm)-1; i += 2 {
ulaw[i>>1] = EncodeUlawFrame(int16(lpcm[i]) | int16(lpcm[i+1])<<8)
}
return ulaw
}

// EncodeUlawFrame encodes a 16bit LPCM frame to G711 u-law PCM
Expand All @@ -102,12 +109,19 @@ func EncodeUlawFrame(frame int16) uint8 {
// DecodeUlaw decodes u-law PCM data to 16bit LPCM
func DecodeUlaw(pcm []byte) []byte {
lpcm := make([]byte, len(pcm)*2)
DecodeUlawTo(pcm, lpcm)
return lpcm
}

// DecodeUlawTo decodes u-law PCM data to 16bit LPCM
// using an already allocated buffer provided by the user.
// The user is responsible for ensuring that the buffer is large enough (double the size of the PCM data).
func DecodeUlawTo(pcm, lpcm []byte) {
for i := 0; i < len(pcm); i++ {
frame := ulaw2lpcm[pcm[i]]
lpcm[i*2] = byte(frame)
lpcm[i*2+1] = byte(frame >> 8)
}
return lpcm
}

// DecodeUlawFrame decodes a u-law PCM frame to 16bit LPCM
Expand All @@ -118,10 +132,17 @@ func DecodeUlawFrame(frame uint8) int16 {
// Ulaw2Alaw performs direct u-law to A-law data conversion
func Ulaw2Alaw(ulaw []byte) []byte {
alaw := make([]byte, len(ulaw))
Ulaw2AlawTo(ulaw, alaw)
return alaw
}

// Ulaw2AlawTo performs direct u-law to A-law data conversion
// using an already allocated buffer provided by the user.
// The user is responsible for ensuring that the buffer is large enough (the size of the A-law data).
func Ulaw2AlawTo(ulaw, alaw []byte) {
for i := 0; i < len(alaw); i++ {
alaw[i] = ulaw2alaw[ulaw[i]]
}
return alaw
}

// Ulaw2AlawFrame directly converts a u-law frame to A-law
Expand Down
Loading

0 comments on commit 10be6f3

Please sign in to comment.