diff --git a/Readme.md b/Readme.md index faa3a51..eb21492 100644 --- a/Readme.md +++ b/Readme.md @@ -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 @@ -26,26 +32,19 @@ 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)` @@ -53,78 +52,127 @@ Write encodes/decodes/transcodes sound data. Writes len(p) bytes from p to the u 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). + --- diff --git a/alaw.go b/alaw.go index 105c881..3e761f4 100644 --- a/alaw.go +++ b/alaw.go @@ -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 @@ -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 @@ -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 diff --git a/alaw_test.go b/alaw_test.go index f3c0f52..3722d3d 100644 --- a/alaw_test.go +++ b/alaw_test.go @@ -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 } } @@ -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 } } @@ -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 } } diff --git a/g711.go b/g711.go index ee104fa..c6ddd74 100644 --- a/g711.go +++ b/g711.go @@ -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 diff --git a/ulaw.go b/ulaw.go index 8752dac..2a91537 100644 --- a/ulaw.go +++ b/ulaw.go @@ -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 @@ -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 @@ -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 diff --git a/ulaw_test.go b/ulaw_test.go index b9efdd6..4ea4489 100644 --- a/ulaw_test.go +++ b/ulaw_test.go @@ -25,7 +25,23 @@ func BenchmarkEncodeUlaw(b *testing.B) { b.SetBytes(int64(len(rawData))) b.ResetTimer() for i := 0; i < b.N; i++ { - EncodeUlaw(rawData) + ualw := EncodeUlaw(rawData) + _ = ualw + } +} + +// Benchmark EncodeUlawTo +func BenchmarkEncodeUlawTo(b *testing.B) { + rawData, err := os.ReadFile("testing/speech.raw") + if err != nil { + b.Fatalf("Failed to read test data: %s\n", err) + } + b.SetBytes(int64(len(rawData))) + ulaw := make([]byte, len(rawData)>>1) + b.ResetTimer() + for i := 0; i < b.N; i++ { + EncodeUlawTo(rawData, ulaw) + _ = ulaw } } @@ -38,7 +54,23 @@ func BenchmarkDecodeUlaw(b *testing.B) { b.SetBytes(int64(len(uData))) b.ResetTimer() for i := 0; i < b.N; i++ { - DecodeUlaw(uData) + lpcm := DecodeUlaw(uData) + _ = lpcm + } +} + +// Benchmark DecodeUlawTo +func BenchmarkDecodeUlawTo(b *testing.B) { + uData, err := os.ReadFile("testing/speech.ulaw") + if err != nil { + b.Fatalf("Failed to read test data: %s\n", err) + } + b.SetBytes(int64(len(uData))) + lpcm := make([]byte, len(uData)<<1) + b.ResetTimer() + for i := 0; i < b.N; i++ { + DecodeUlawTo(uData, lpcm) + _ = lpcm } } @@ -51,6 +83,22 @@ func BenchmarkUlaw2Alaw(b *testing.B) { b.SetBytes(int64(len(uData))) b.ResetTimer() for i := 0; i < b.N; i++ { - Ulaw2Alaw(uData) + alaw := Ulaw2Alaw(uData) + _ = alaw + } +} + +// Benchmark Ulaw2AlawTo +func BenchmarkUlaw2AlawTo(b *testing.B) { + uData, err := os.ReadFile("testing/speech.ulaw") + if err != nil { + b.Fatalf("Failed to read test data: %s\n", err) + } + b.SetBytes(int64(len(uData))) + alaw := make([]byte, len(uData)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Ulaw2AlawTo(uData, alaw) + _ = alaw } }