From f0b4e7e865b4dc12dec68b8967bc4c70e3c69d4a Mon Sep 17 00:00:00 2001 From: Lefteris Zafiris Date: Mon, 8 Jan 2024 20:50:44 +0200 Subject: [PATCH 1/7] Small cleanups --- alaw.go | 22 +++++++++------------- g711_test.go | 3 +++ ulaw.go | 22 +++++++++------------- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/alaw.go b/alaw.go index 34986fe..5794775 100644 --- a/alaw.go +++ b/alaw.go @@ -72,26 +72,22 @@ var ( // EncodeAlaw encodes 16bit LPCM data to G711 A-law PCM func EncodeAlaw(lpcm []byte) []byte { - if len(lpcm) < 2 { - return []byte{} - } - alaw := make([]byte, len(lpcm)/2) - for i, j := 0, 0; j <= len(lpcm)-2; i, j = i+1, j+2 { - alaw[i] = EncodeAlawFrame(int16(lpcm[j]) | int16(lpcm[j+1])<<8) + alaw := make([]byte, len(lpcm)>>1) + 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 func EncodeAlawFrame(frame int16) uint8 { - var compressedByte, seg, sign int16 - sign = ((^frame) >> 8) & 0x80 + sign := ((^frame) >> 8) & 0x80 if sign == 0 { frame = ^frame } - compressedByte = frame >> 4 + compressedByte := frame >> 4 if compressedByte > 15 { - seg = int16(12 - bits.LeadingZeros16(uint16(compressedByte))) + seg := int16(12 - bits.LeadingZeros16(uint16(compressedByte))) compressedByte >>= seg - 1 compressedByte -= 16 compressedByte += seg << 4 @@ -102,10 +98,10 @@ 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) - for i, j := 0, 0; i < len(pcm); i, j = i+1, j+2 { + for i := 0; i < len(pcm); i++ { frame := alaw2lpcm[pcm[i]] - lpcm[j] = byte(frame) - lpcm[j+1] = byte(frame >> 8) + lpcm[i*2] = byte(frame) + lpcm[i*2+1] = byte(frame >> 8) } return lpcm } diff --git a/g711_test.go b/g711_test.go index 58f9b1d..f41f5ba 100644 --- a/g711_test.go +++ b/g711_test.go @@ -24,6 +24,7 @@ var EncoderTest = []struct { expected int }{ {[]byte{}, 0}, + {[]byte{0x01}, 0}, {[]byte{0x01, 0x00}, 2}, {[]byte{0x01, 0x00, 0x7c, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde}, 12}, {[]byte{0x01, 0x00, 0x7c, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde, 0xd9}, 12}, @@ -34,6 +35,7 @@ var DecoderTest = []struct { expected int }{ {[]byte{}, 0}, + {[]byte{0x01}, 2}, {[]byte{0x01, 0x00}, 4}, {[]byte{0x01, 0x00, 0x7c, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde}, 24}, {[]byte{0x01, 0x00, 0xdc, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde, 0xd9}, 26}, @@ -44,6 +46,7 @@ var TranscoderTest = []struct { expected int }{ {[]byte{}, 0}, + {[]byte{0x01}, 1}, {[]byte{0x01, 0x00}, 2}, {[]byte{0x01, 0x00, 0x7c, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde}, 12}, {[]byte{0x01, 0x00, 0x7c, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde, 0xd9}, 13}, diff --git a/ulaw.go b/ulaw.go index e04996a..aec8955 100644 --- a/ulaw.go +++ b/ulaw.go @@ -77,20 +77,16 @@ var ( // EncodeUlaw encodes 16bit LPCM data to G711 u-law PCM func EncodeUlaw(lpcm []byte) []byte { - if len(lpcm) < 2 { - return []byte{} - } - ulaw := make([]byte, len(lpcm)/2) - for i, j := 0, 0; j <= len(lpcm)-2; i, j = i+1, j+2 { - ulaw[i] = EncodeUlawFrame(int16(lpcm[j]) | int16(lpcm[j+1])<<8) + ulaw := make([]byte, len(lpcm)>>1) + 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 func EncodeUlawFrame(frame int16) uint8 { - var lowNibble, seg, sign int16 - sign = ((^frame) >> 8) & 0x80 + sign := ((^frame) >> 8) & 0x80 if sign == 0 { frame = ^frame } @@ -98,18 +94,18 @@ func EncodeUlawFrame(frame int16) uint8 { if frame > ulawClip { frame = ulawClip } - seg = int16(16 - bits.LeadingZeros16(uint16(frame>>5))) - lowNibble = 0x000F - ((frame >> (seg)) & 0x000F) + seg := int16(16 - bits.LeadingZeros16(uint16(frame>>5))) + lowNibble := 0x000F - ((frame >> (seg)) & 0x000F) return uint8(sign | ((8 - seg) << 4) | lowNibble) } // DecodeUlaw decodes u-law PCM data to 16bit LPCM func DecodeUlaw(pcm []byte) []byte { lpcm := make([]byte, len(pcm)*2) - for i, j := 0, 0; i < len(pcm); i, j = i+1, j+2 { + for i := 0; i < len(pcm); i++ { frame := ulaw2lpcm[pcm[i]] - lpcm[j] = byte(frame) - lpcm[j+1] = byte(frame >> 8) + lpcm[i*2] = byte(frame) + lpcm[i*2+1] = byte(frame >> 8) } return lpcm } From 09128fbf90bf050fd754fa4d3d671ef0b6587494 Mon Sep 17 00:00:00 2001 From: Lefteris Zafiris Date: Mon, 8 Jan 2024 21:39:50 +0200 Subject: [PATCH 2/7] Added Close() methods, updated Readme --- Readme.md | 254 ++++++++++++++++++++--------------------- cmd/g711dec/g711dec.go | 1 + cmd/g711enc/g711enc.go | 1 + g711.go | 12 ++ g711_test.go | 11 ++ 5 files changed, 147 insertions(+), 132 deletions(-) diff --git a/Readme.md b/Readme.md index 74af993..8d89367 100644 --- a/Readme.md +++ b/Readme.md @@ -1,186 +1,176 @@ # g711 - import "github.com/zaf/g711" -Package g711 implements encoding and decoding of G711 PCM sound data. G.711 is -an ITU-T standard for audio companding. - -For usage details please see the code snippets in the cmd folder. +Package g711 implements encoding and decoding of G711 PCM sound data. +G.711 is an ITU-T standard for audio companding. ## Usage -```go +See code examples in `cmd/` folder. + +## Constants + +```golang const ( - // Input and output formats - Alaw = iota // Alaw G711 encoded PCM data - Ulaw // Ulaw G711 encoded PCM data - Lpcm // Lpcm 16bit signed linear data + // Input and output formats + Alaw = iota // Alaw G711 encoded PCM data + Ulaw // Ulaw G711 encoded PCM data + Lpcm // Lpcm 16bit signed linear data ) ``` +## Types -#### func Alaw2Ulaw +### type [Decoder](/g711.go#L30) -```go -func Alaw2Ulaw(alaw []byte) []byte -``` -Alaw2Ulaw performs direct A-law to u-law data conversion +`type Decoder struct { ... }` -#### func Alaw2UlawFrame +Decoder reads G711 PCM data and decodes it to 16bit 8000Hz LPCM -```go -func Alaw2UlawFrame(frame uint8) uint8 -``` -Alaw2UlawFrame directly converts an A-law frame to u-law +#### func [NewAlawDecoder](/g711.go#L46) -#### func DecodeAlaw +`func NewAlawDecoder(reader io.Reader) (*Decoder, error)` -```go -func DecodeAlaw(pcm []byte) []byte -``` -DecodeAlaw decodes A-law PCM data to 16bit LPCM +NewAlawDecoder returns a pointer to a Decoder that implements an io.Reader. +It takes as input the source data Reader. -#### func DecodeAlawFrame +#### func [NewUlawDecoder](/g711.go#L59) -```go -func DecodeAlawFrame(frame uint8) int16 -``` -DecodeAlawFrame decodes an A-law PCM frame to 16bit LPCM +`func NewUlawDecoder(reader io.Reader) (*Decoder, error)` -#### func DecodeUlaw +NewUlawDecoder returns a pointer to a Decoder that implements an io.Reader. +It takes as input the source data Reader. -```go -func DecodeUlaw(pcm []byte) []byte -``` -DecodeUlaw decodes u-law PCM data to 16bit LPCM +#### func (*Decoder) [Close](/g711.go#L107) -#### func DecodeUlawFrame +`func (r *Decoder) Close() error` -```go -func DecodeUlawFrame(frame uint8) int16 -``` -DecodeUlawFrame decodes a u-law PCM frame to 16bit LPCM +Close closes the Decoder, it implements the io.Closer interface. -#### func EncodeAlaw +#### func (*Decoder) [Read](/g711.go#L138) -```go -func EncodeAlaw(lpcm []byte) []byte -``` -EncodeAlaw encodes 16bit LPCM data to G711 A-law PCM +`func (r *Decoder) Read(p []byte) (i int, err error)` -#### func EncodeAlawFrame +Read decodes G711 data. Reads up to len(p) bytes into p, returns the number +of bytes read and any error encountered. -```go -func EncodeAlawFrame(frame int16) uint8 -``` -EncodeAlawFrame encodes a 16bit LPCM frame to G711 A-law PCM +#### func (*Decoder) [Reset](/g711.go#L119) -#### func EncodeUlaw +`func (r *Decoder) Reset(reader io.Reader) error` -```go -func EncodeUlaw(lpcm []byte) []byte -``` -EncodeUlaw encodes 16bit LPCM data to G711 u-law PCM +Reset discards the Decoder state. This permits reusing a Decoder rather than allocating a new one. -#### func EncodeUlawFrame +### type [Encoder](/g711.go#L37) -```go -func EncodeUlawFrame(frame int16) uint8 -``` -EncodeUlawFrame encodes a 16bit LPCM frame to G711 u-law PCM +`type Encoder struct { ... }` -#### func Ulaw2Alaw +Encoder encodes 16bit 8000Hz LPCM data to G711 PCM or +directly transcodes between A-law and u-law -```go -func Ulaw2Alaw(ulaw []byte) []byte -``` -Ulaw2Alaw performs direct u-law to A-law data conversion +#### func [NewAlawEncoder](/g711.go#L72) -#### func Ulaw2AlawFrame +`func NewAlawEncoder(writer io.Writer, input int) (*Encoder, error)` -```go -func Ulaw2AlawFrame(frame uint8) uint8 -``` -Ulaw2AlawFrame directly converts a u-law frame to A-law +NewAlawEncoder returns a pointer to an Encoder that implements an io.Writer. +It takes as input the destination data Writer and the input encoding format. -#### type Decoder +#### func [NewUlawEncoder](/g711.go#L90) -```go -type Decoder struct { -} -``` +`func NewUlawEncoder(writer io.Writer, input int) (*Encoder, error)` -Decoder reads G711 PCM data and decodes it to 16bit 8000Hz LPCM +NewUlawEncoder returns a pointer to an Encoder that implements an io.Writer. +It takes as input the destination data Writer and the input encoding format. -#### func NewAlawDecoder +#### func (*Encoder) [Close](/g711.go#L113) -```go -func NewAlawDecoder(reader io.Reader) (*Decoder, error) -``` -NewAlawDecoder returns a pointer to a Decoder that implements an io.Reader. It -takes as input the source data Reader. +`func (w *Encoder) Close() error` -#### func NewUlawDecoder +Close closes the Encoder, it implements the io.Closer interface. -```go -func NewUlawDecoder(reader io.Reader) (*Decoder, error) -``` -NewUlawDecoder returns a pointer to a Decoder that implements an io.Reader. It -takes as input the source data Reader. +#### func (*Encoder) [Reset](/g711.go#L128) -#### func (*Decoder) Read +`func (w *Encoder) Reset(writer io.Writer) error` -```go -func (r *Decoder) Read(p []byte) (i int, err error) -``` -Read decodes G711 data. Reads up to len(p) bytes into p, returns the number of -bytes read and any error encountered. +Reset discards the Encoder state. This permits reusing an Encoder rather than allocating a new one. -#### func (*Decoder) Reset +#### func (*Encoder) [Write](/g711.go#L152) -```go -func (r *Decoder) Reset(reader io.Reader) error -``` -Reset discards the Decoder state. This permits reusing a Decoder rather than -allocating a new one. +`func (w *Encoder) Write(p []byte) (i int, err error)` -#### type Encoder +Write encodes G711 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. -```go -type Encoder struct { -} -``` +## Functions -Encoder encodes 16bit 8000Hz LPCM data to G711 PCM or directly transcodes -between A-law and u-law +### func [Alaw2Ulaw](/alaw.go#L115) -#### func NewAlawEncoder +`func Alaw2Ulaw(alaw []byte) []byte` -```go -func NewAlawEncoder(writer io.Writer, input int) (*Encoder, error) -``` -NewAlawEncoder returns a pointer to an Encoder that implements an io.Writer. It -takes as input the destination data Writer and the input encoding format. +Alaw2Ulaw performs direct A-law to u-law data conversion -#### func NewUlawEncoder +### func [Alaw2UlawFrame](/alaw.go#L124) -```go -func NewUlawEncoder(writer io.Writer, input int) (*Encoder, error) -``` -NewUlawEncoder returns a pointer to an Encoder that implements an io.Writer. It -takes as input the destination data Writer and the input encoding format. +`func Alaw2UlawFrame(frame uint8) uint8` -#### func (*Encoder) Reset +Alaw2UlawFrame directly converts an A-law frame to u-law -```go -func (w *Encoder) Reset(writer io.Writer) error -``` -Reset discards the Encoder state. This permits reusing an Encoder rather than -allocating a new one. +### func [DecodeAlaw](/alaw.go#L99) -#### func (*Encoder) Write +`func DecodeAlaw(pcm []byte) []byte` -```go -func (w *Encoder) Write(p []byte) (i int, err error) -``` -Write encodes G711 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. +DecodeAlaw decodes A-law PCM data to 16bit LPCM + +### func [DecodeAlawFrame](/alaw.go#L110) + +`func DecodeAlawFrame(frame uint8) int16` + +DecodeAlawFrame decodes an A-law PCM frame to 16bit LPCM + +### func [DecodeUlaw](/ulaw.go#L103) + +`func DecodeUlaw(pcm []byte) []byte` + +DecodeUlaw decodes u-law PCM data to 16bit LPCM + +### func [DecodeUlawFrame](/ulaw.go#L114) + +`func DecodeUlawFrame(frame uint8) int16` + +DecodeUlawFrame decodes a u-law PCM frame to 16bit LPCM + +### 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(frame int16) uint8` + +EncodeAlawFrame encodes a 16bit LPCM frame to G711 A-law PCM + +### 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(frame int16) uint8` + +EncodeUlawFrame encodes a 16bit LPCM frame to G711 u-law PCM + +### func [Ulaw2Alaw](/ulaw.go#L119) + +`func Ulaw2Alaw(ulaw []byte) []byte` + +Ulaw2Alaw performs direct u-law to A-law data conversion + +### func [Ulaw2AlawFrame](/ulaw.go#L128) + +`func Ulaw2AlawFrame(frame uint8) uint8` + +Ulaw2AlawFrame directly converts a u-law frame to A-law + +--- diff --git a/cmd/g711dec/g711dec.go b/cmd/g711dec/g711dec.go index 0a2ab20..aeb2d38 100644 --- a/cmd/g711dec/g711dec.go +++ b/cmd/g711dec/g711dec.go @@ -63,6 +63,7 @@ func decodeG711(file string) error { err = fmt.Errorf("unrecognised format for file: %s", file) return err } + defer decoder.Close() outName := strings.TrimSuffix(file, filepath.Ext(file)) + ".raw" outFile, err := os.Create(outName) if err != nil { diff --git a/cmd/g711enc/g711enc.go b/cmd/g711enc/g711enc.go index 49d9655..8871fad 100644 --- a/cmd/g711enc/g711enc.go +++ b/cmd/g711enc/g711enc.go @@ -74,6 +74,7 @@ func encodeG711(file, format string) error { return err } } + defer encoder.Close() if extension == ".wav" { input.Seek(wavHeader, 0) // Skip wav header } diff --git a/g711.go b/g711.go index 3d9479c..01b2219 100644 --- a/g711.go +++ b/g711.go @@ -103,6 +103,18 @@ func NewUlawEncoder(writer io.Writer, input int) (*Encoder, error) { return &w, nil } +// Close closes the Decoder, it implements the io.Closer interface. +func (r *Decoder) Close() error { + r = nil + return nil +} + +// Close closes the Encoder, it implements the io.Closer interface. +func (w *Encoder) Close() error { + w = nil + return nil +} + // Reset discards the Decoder state. This permits reusing a Decoder rather than allocating a new one. func (r *Decoder) Reset(reader io.Reader) error { if reader == nil { diff --git a/g711_test.go b/g711_test.go index f41f5ba..6658f70 100644 --- a/g711_test.go +++ b/g711_test.go @@ -61,6 +61,7 @@ func TestEncode(t *testing.T) { t.Errorf("Alaw Encode: expected: %d , actual: %d", tc.expected, i) } } + aenc.Close() uenc, _ := NewUlawEncoder(io.Discard, Lpcm) for _, tc := range EncoderTest { i, _ := uenc.Write(tc.data) @@ -68,6 +69,7 @@ func TestEncode(t *testing.T) { t.Errorf("ulaw Encode: expected: %d , actual: %d", tc.expected, i) } } + uenc.Close() utrans, _ := NewUlawEncoder(io.Discard, Alaw) for _, tc := range TranscoderTest { i, _ := utrans.Write(tc.data) @@ -75,6 +77,7 @@ func TestEncode(t *testing.T) { t.Errorf("ulaw Transcode: expected: %d , actual: %d", tc.expected, i) } } + utrans.Close() } // Test Decoding @@ -94,6 +97,7 @@ func TestDecode(t *testing.T) { t.Errorf("Alaw Decode: expected: %d , actual: %d", tc.expected, i) } } + adec.Close() b.Reset() udec, _ := NewUlawDecoder(b) for _, tc := range DecoderTest { @@ -109,6 +113,7 @@ func TestDecode(t *testing.T) { t.Errorf("ulaw Decode: expected: %d , actual: %d", tc.expected, i) } } + udec.Close() b.Reset() // Edge Case udec, _ = NewUlawDecoder(iotest.TimeoutReader(b)) @@ -125,6 +130,7 @@ func TestDecode(t *testing.T) { t.Errorf("ulaw Decode: expected: %d , actual: %d", tc.expected, i) } } + udec.Close() } // Benchmark Encoding data to Alaw @@ -144,6 +150,7 @@ func BenchmarkAEncode(b *testing.B) { if err != nil { b.Fatalf("Encoding failed: %s\n", err) } + encoder.Close() } } @@ -167,6 +174,7 @@ func BenchmarkUEncode(b *testing.B) { b.Fatalf("Encoding failed: %s\n", err) } + encoder.Close() } } @@ -190,6 +198,7 @@ func BenchmarkTranscode(b *testing.B) { b.Fatalf("Transcoding failed: %s\n", err) } + transcoder.Close() } } @@ -213,6 +222,7 @@ func BenchmarkUDecode(b *testing.B) { b.Fatalf("Decoding failed: %s\n", err) } + decoder.Close() } } @@ -236,5 +246,6 @@ func BenchmarkADecode(b *testing.B) { b.Fatalf("Decoding failed: %s\n", err) } + decoder.Close() } } From eda5276dc64bd81b4d7c24ff0e445634c89e1c31 Mon Sep 17 00:00:00 2001 From: Lefteris Zafiris Date: Tue, 9 Jan 2024 00:53:44 +0200 Subject: [PATCH 3/7] New, simpler API (breaking change) --- Readme.md | 86 +++++---------------- alaw.go | 2 +- cmd/g711/main.go | 86 +++++++++++++++++++++ cmd/g711dec/g711dec.go | 75 ------------------ cmd/g711enc/g711enc.go | 83 -------------------- g711.go | 171 +++++++++++++++-------------------------- g711_test.go | 77 +++++-------------- testing/Readme.md | 1 + testing/piano.raw | Bin 0 -> 100904 bytes ulaw.go | 2 +- 10 files changed, 191 insertions(+), 392 deletions(-) create mode 100644 cmd/g711/main.go delete mode 100644 cmd/g711dec/g711dec.go delete mode 100644 cmd/g711enc/g711enc.go create mode 100644 testing/piano.raw diff --git a/Readme.md b/Readme.md index 8d89367..faa3a51 100644 --- a/Readme.md +++ b/Readme.md @@ -3,99 +3,53 @@ Package g711 implements encoding and decoding of G711 PCM sound data. G.711 is an ITU-T standard for audio companding. -## Usage - -See code examples in `cmd/` folder. +For usage details please see the code snippet in the cmd folder. ## Constants ```golang const ( // Input and output formats - Alaw = iota // Alaw G711 encoded PCM data - Ulaw // Ulaw G711 encoded PCM data - Lpcm // Lpcm 16bit signed linear data + Alaw = iota + 1 // Alaw G711 encoded PCM data + Ulaw // Ulaw G711 encoded PCM data + Lpcm // Lpcm 16bit signed linear data ) ``` -## Types - -### type [Decoder](/g711.go#L30) - -`type Decoder struct { ... }` - -Decoder reads G711 PCM data and decodes it to 16bit 8000Hz LPCM - -#### func [NewAlawDecoder](/g711.go#L46) - -`func NewAlawDecoder(reader io.Reader) (*Decoder, error)` - -NewAlawDecoder returns a pointer to a Decoder that implements an io.Reader. -It takes as input the source data Reader. - -#### func [NewUlawDecoder](/g711.go#L59) - -`func NewUlawDecoder(reader io.Reader) (*Decoder, error)` - -NewUlawDecoder returns a pointer to a Decoder that implements an io.Reader. -It takes as input the source data Reader. - -#### func (*Decoder) [Close](/g711.go#L107) -`func (r *Decoder) Close() error` - -Close closes the Decoder, it implements the io.Closer interface. - -#### func (*Decoder) [Read](/g711.go#L138) - -`func (r *Decoder) Read(p []byte) (i int, err error)` - -Read decodes G711 data. Reads up to len(p) bytes into p, returns the number -of bytes read and any error encountered. - -#### func (*Decoder) [Reset](/g711.go#L119) - -`func (r *Decoder) Reset(reader io.Reader) error` - -Reset discards the Decoder state. This permits reusing a Decoder rather than allocating a new one. +## Types -### type [Encoder](/g711.go#L37) +### type [Coder](/g711.go#L32) -`type Encoder struct { ... }` +`type Coder struct { ... }` -Encoder encodes 16bit 8000Hz LPCM data to G711 PCM or +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 [NewAlawEncoder](/g711.go#L72) - -`func NewAlawEncoder(writer io.Writer, input int) (*Encoder, error)` - -NewAlawEncoder returns a pointer to an Encoder that implements an io.Writer. -It takes as input the destination data Writer and the input encoding format. - -#### func [NewUlawEncoder](/g711.go#L90) +#### func [NewCoder](/g711.go#L40) -`func NewUlawEncoder(writer io.Writer, input int) (*Encoder, error)` +`func NewCoder(writer io.Writer, input, output int) (*Coder, error)` -NewUlawEncoder returns a pointer to an Encoder that implements an io.Writer. -It takes as input the destination data Writer and the input encoding format. +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 (*Encoder) [Close](/g711.go#L113) +#### func (*Coder) [Close](/g711.go#L90) -`func (w *Encoder) Close() error` +`func (w *Coder) Close() error` Close closes the Encoder, it implements the io.Closer interface. -#### func (*Encoder) [Reset](/g711.go#L128) +#### func (*Coder) [Reset](/g711.go#L96) -`func (w *Encoder) Reset(writer io.Writer) error` +`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 (*Encoder) [Write](/g711.go#L152) +#### func (*Coder) [Write](/g711.go#L107) -`func (w *Encoder) Write(p []byte) (i int, err error)` +`func (w *Coder) Write(p []byte) (int, error)` -Write encodes G711 Data. Writes len(p) bytes from p to the underlying data stream, +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. diff --git a/alaw.go b/alaw.go index 5794775..105c881 100644 --- a/alaw.go +++ b/alaw.go @@ -66,7 +66,7 @@ var ( 226, 227, 224, 225, 230, 231, 228, 229, 221, 221, 220, 220, 223, 223, 222, 222, 244, 246, 240, 242, 252, 254, 248, 250, 234, 235, 232, 233, 238, 239, 236, 237, 200, 201, 198, 199, 204, 205, 202, 203, 192, 193, 191, 191, 196, 197, 194, 195, - 214, 215, 212, 213, 218, 219, 216, 217, 207, 207, 206, 206, 210, 211, 208, 0, + 214, 215, 212, 213, 218, 219, 216, 217, 207, 207, 206, 206, 210, 211, 208, 209, } ) diff --git a/cmd/g711/main.go b/cmd/g711/main.go new file mode 100644 index 0000000..142e931 --- /dev/null +++ b/cmd/g711/main.go @@ -0,0 +1,86 @@ +/* + Copyright (C) 2016 - 2024, Lefteris Zafiris + + This program is free software, distributed under the terms of + the BSD 3-Clause License. See the LICENSE file + at the top of the source tree. + + g711 encodes 16bit 8kHz LPCM data to 8bit G711 PCM, + or decodes 8bit G711 PCM to 16bit 8kHz LPCM, + or converts between A-law and u-law formats. + Input can be 16bit 8kHz wav or raw LPCM files, or ulaw/alaw encoded PCM data. + + Usage: g711 -in -out + Valid formats are: alaw, ulaw, lpcm + +*/ + +package main + +import ( + "flag" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/zaf/g711" +) + +const wavHeader = 44 + +var ( + in = flag.String("in", "", "input format: alaw, ulaw, lpcm") + out = flag.String("out", "", "output format: alaw, ulaw, lpcm") +) + +var formats = map[string]int{ + "alaw": g711.Alaw, + "ulaw": g711.Ulaw, + "lpcm": g711.Lpcm, +} + +func main() { + flag.Parse() + var exitCode int + for _, file := range flag.Args() { + err := translate(file) + if err != nil { + fmt.Println("Error while processing", file, err) + exitCode = 1 + } + } + os.Exit(exitCode) +} + +func translate(file string) error { + input, err := os.Open(file) + if err != nil { + return err + } + inExtension := strings.ToLower(filepath.Ext(file)) + + inFormat := formats[strings.ToLower(*in)] + outFormat := formats[strings.ToLower(*out)] + outName := strings.TrimSuffix(file, filepath.Ext(file)) + "." + strings.ToLower(*out) + outFile, err := os.Create(outName) + if err != nil { + return err + } + defer outFile.Close() + // Create a new translator, it implements io.WriteCloser + translator, err := g711.NewCoder(outFile, inFormat, outFormat) + if err != nil { + os.Remove(outName) + return err + } + defer translator.Close() + if inExtension == ".wav" { + input.Seek(wavHeader, 0) // Skip wav header + } + // enc/dec/transcode the input file data and write to the output file + _, err = io.Copy(translator, input) + outFile.Sync() + return err +} diff --git a/cmd/g711dec/g711dec.go b/cmd/g711dec/g711dec.go deleted file mode 100644 index aeb2d38..0000000 --- a/cmd/g711dec/g711dec.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - Copyright (C) 2016 - 2024, Lefteris Zafiris - - This program is free software, distributed under the terms of - the BSD 3-Clause License. See the LICENSE file - at the top of the source tree. - - g711dec decodes 8bit G711 PCM data to 16 Bit signed LPCM raw data - -*/ - -package main - -import ( - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/zaf/g711" -) - -func main() { - if len(os.Args) == 1 || os.Args[1] == "help" || os.Args[1] == "--help" { - fmt.Printf("%s Decodes 8bit G711 PCM data to raw 16 Bit signed LPCM\n", os.Args[0]) - fmt.Println("The program takes as input a list A-law or u-law encoded files") - fmt.Println("decodes them to LPCM and saves the files with a \".raw\" extension.") - fmt.Printf("\nUsage: %s [files]\n", os.Args[0]) - os.Exit(1) - } - var exitCode int - for _, file := range os.Args[1:] { - err := decodeG711(file) - if err != nil { - fmt.Println(err) - exitCode = 1 - } - } - os.Exit(exitCode) -} - -func decodeG711(file string) error { - input, err := os.Open(file) - if err != nil { - return err - } - defer input.Close() - - extension := strings.ToLower(filepath.Ext(file)) - decoder := new(g711.Decoder) - if extension == ".alaw" || extension == ".al" { - decoder, err = g711.NewAlawDecoder(input) - if err != nil { - return err - } - } else if extension == ".ulaw" || extension == ".ul" { - decoder, err = g711.NewUlawDecoder(input) - if err != nil { - return err - } - } else { - err = fmt.Errorf("unrecognised format for file: %s", file) - return err - } - defer decoder.Close() - outName := strings.TrimSuffix(file, filepath.Ext(file)) + ".raw" - outFile, err := os.Create(outName) - if err != nil { - return err - } - defer outFile.Close() - _, err = io.Copy(outFile, decoder) - return err -} diff --git a/cmd/g711enc/g711enc.go b/cmd/g711enc/g711enc.go deleted file mode 100644 index 8871fad..0000000 --- a/cmd/g711enc/g711enc.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright (C) 2016 - 2024, Lefteris Zafiris - - This program is free software, distributed under the terms of - the BSD 3-Clause License. See the LICENSE file - at the top of the source tree. - - g711enc encodes 16bit 8kHz LPCM data to 8bit G711 PCM. - It works with wav or raw files as input. - -*/ - -package main - -import ( - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/zaf/g711" -) - -const wavHeader = 44 - -func main() { - if len(os.Args) < 3 || os.Args[1] == "help" || os.Args[1] == "--help" || (os.Args[1] != "ulaw" && os.Args[1] != "alaw") { - fmt.Printf("%s Encodes 16bit 8kHz LPCM data to 8bit G711 PCM\n", os.Args[0]) - fmt.Println("The program takes as input a list or wav or raw files, encodes them") - fmt.Println("to G711 PCM and saves them with the proper extension.") - fmt.Printf("\nUsage: %s [encoding format] [files]\n", os.Args[0]) - fmt.Println("encoding format can be either alaw or ulaw") - os.Exit(1) - } - var exitCode int - format := os.Args[1] - for _, file := range os.Args[2:] { - err := encodeG711(file, format) - if err != nil { - fmt.Println(err) - exitCode = 1 - } - } - os.Exit(exitCode) -} - -func encodeG711(file, format string) error { - input, err := os.Open(file) - if err != nil { - return err - } - - extension := strings.ToLower(filepath.Ext(file)) - if extension != ".wav" && extension != ".raw" && extension != ".sln" { - err = fmt.Errorf("unrecognised format for input file: %s", file) - return err - } - outName := strings.TrimSuffix(file, filepath.Ext(file)) + "." + format - outFile, err := os.Create(outName) - if err != nil { - return err - } - defer outFile.Close() - encoder := new(g711.Encoder) - if format == "alaw" { - encoder, err = g711.NewAlawEncoder(outFile, g711.Lpcm) - if err != nil { - return err - } - } else { - encoder, err = g711.NewUlawEncoder(outFile, g711.Lpcm) - if err != nil { - return err - } - } - defer encoder.Close() - if extension == ".wav" { - input.Seek(wavHeader, 0) // Skip wav header - } - _, err = io.Copy(encoder, input) - return err -} diff --git a/g711.go b/g711.go index 01b2219..8e3a471 100644 --- a/g711.go +++ b/g711.go @@ -10,7 +10,7 @@ Package g711 implements encoding and decoding of G711 PCM sound data. G.711 is an ITU-T standard for audio companding. -For usage details please see the code snippets in the cmd folder. +For usage details please see the code snippet in the cmd folder. */ package g711 @@ -21,111 +21,79 @@ import ( const ( // Input and output formats - Alaw = iota // Alaw G711 encoded PCM data - Ulaw // Ulaw G711 encoded PCM data - Lpcm // Lpcm 16bit signed linear data + Alaw = iota + 1 // Alaw G711 encoded PCM data + Ulaw // Ulaw G711 encoded PCM data + Lpcm // Lpcm 16bit signed linear data ) -// Decoder reads G711 PCM data and decodes it to 16bit 8000Hz LPCM -type Decoder struct { - decode func([]byte) []byte // decoding function - source io.Reader // source data -} - -// Encoder encodes 16bit 8000Hz LPCM data to G711 PCM or +// 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 -type Encoder struct { - input int // input format - encode func([]byte) []byte // encoding function - transcode func([]byte) []byte // transcoding function +type Coder struct { + translate func([]byte) []byte // enc/decoding function destination io.Writer // output data + multiplier float64 } -// NewAlawDecoder returns a pointer to a Decoder that implements an io.Reader. -// It takes as input the source data Reader. -func NewAlawDecoder(reader io.Reader) (*Decoder, error) { - if reader == nil { - return nil, errors.New("io.Reader is nil") - } - r := Decoder{ - decode: DecodeAlaw, - source: reader, - } - return &r, nil -} - -// NewUlawDecoder returns a pointer to a Decoder that implements an io.Reader. -// It takes as input the source data Reader. -func NewUlawDecoder(reader io.Reader) (*Decoder, error) { - if reader == nil { - return nil, errors.New("io.Reader is nil") - } - r := Decoder{ - decode: DecodeUlaw, - source: reader, - } - return &r, nil -} - -// NewAlawEncoder returns a pointer to an Encoder that implements an io.Writer. -// It takes as input the destination data Writer and the input encoding format. -func NewAlawEncoder(writer io.Writer, input int) (*Encoder, error) { - if writer == nil { - return nil, errors.New("io.Writer is nil") - } - if input != Ulaw && input != Lpcm { - return nil, errors.New("invalid input format") - } - w := Encoder{ - input: input, - encode: EncodeAlaw, - transcode: Ulaw2Alaw, - destination: writer, - } - return &w, nil -} - -// NewUlawEncoder returns a pointer to an Encoder that implements an io.Writer. -// It takes as input the destination data Writer and the input encoding format. -func NewUlawEncoder(writer io.Writer, input int) (*Encoder, 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 NewCoder(writer io.Writer, input, output int) (*Coder, error) { if writer == nil { return nil, errors.New("io.Writer is nil") } - if input != Alaw && input != Lpcm { + var translate func([]byte) []byte + multiplier := 1.0 + switch input { + case Lpcm: + switch output { + case Alaw: + translate = EncodeAlaw + multiplier = 2 + case Ulaw: + translate = EncodeUlaw + multiplier = 2 + default: + return nil, errors.New("invalid output format") + } + case Alaw: + switch output { + case Lpcm: + translate = DecodeAlaw + multiplier = 0.5 + case Ulaw: + translate = Alaw2Ulaw + default: + return nil, errors.New("invalid output format") + } + case Ulaw: + switch output { + case Lpcm: + translate = DecodeUlaw + multiplier = 0.5 + case Alaw: + translate = Ulaw2Alaw + default: + return nil, errors.New("invalid output format") + } + default: return nil, errors.New("invalid input format") } - w := Encoder{ - input: input, - encode: EncodeUlaw, - transcode: Alaw2Ulaw, + w := Coder{ + translate: translate, destination: writer, + multiplier: multiplier, } return &w, nil } -// Close closes the Decoder, it implements the io.Closer interface. -func (r *Decoder) Close() error { - r = nil - return nil -} - // Close closes the Encoder, it implements the io.Closer interface. -func (w *Encoder) Close() error { +func (w *Coder) Close() error { w = nil return nil } -// Reset discards the Decoder state. This permits reusing a Decoder rather than allocating a new one. -func (r *Decoder) Reset(reader io.Reader) error { - if reader == nil { - return errors.New("io.Reader is nil") - } - r.source = reader - return nil -} - // Reset discards the Encoder state. This permits reusing an Encoder rather than allocating a new one. -func (w *Encoder) Reset(writer io.Writer) error { +func (w *Coder) Reset(writer io.Writer) error { if writer == nil { return errors.New("io.Writer is nil") } @@ -133,34 +101,17 @@ func (w *Encoder) Reset(writer io.Writer) error { return nil } -// Read decodes G711 data. Reads up to len(p) bytes into p, returns the number -// of bytes read and any error encountered. -func (r *Decoder) Read(p []byte) (i int, err error) { - if len(p) == 0 { - return - } - b := make([]byte, len(p)/2) - i, err = r.source.Read(b) - copy(p, r.decode(b)) - i *= 2 // Report back the correct number of bytes - return -} - -// Write encodes G711 Data. Writes len(p) bytes from p to the underlying data stream, +// 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. -func (w *Encoder) Write(p []byte) (i int, err error) { +func (w *Coder) Write(p []byte) (int, error) { if len(p) == 0 { - return - } - if w.input == Lpcm { // Encode LPCM data to G711 - i, err = w.destination.Write(w.encode(p)) - if err == nil && len(p)%2 != 0 { - err = errors.New("odd number of LPCM bytes, incomplete frame") - } - i *= 2 // Report back the correct number of bytes written from p - } else { // Trans-code - i, err = w.destination.Write(w.transcode(p)) + return 0, nil } - return + i, err := w.destination.Write(w.translate(p)) + // If we are encoding to g711 we need to multiply the number of bytes written by 2 to avoid reporting short writes + // this happens because 2 bytes of input data are encoded to 1 byte of output data. + // In a similar manner if we are decoding from g711 we need to divide the number of bytes written by 2. + i = int(float64(i) * w.multiplier) + return i, err } diff --git a/g711_test.go b/g711_test.go index 6658f70..d008357 100644 --- a/g711_test.go +++ b/g711_test.go @@ -12,11 +12,9 @@ package g711 import ( - "bytes" "io" "os" "testing" - "testing/iotest" ) var EncoderTest = []struct { @@ -35,10 +33,10 @@ var DecoderTest = []struct { expected int }{ {[]byte{}, 0}, - {[]byte{0x01}, 2}, - {[]byte{0x01, 0x00}, 4}, - {[]byte{0x01, 0x00, 0x7c, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde}, 24}, - {[]byte{0x01, 0x00, 0xdc, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde, 0xd9}, 26}, + {[]byte{0x01}, 1}, + {[]byte{0x01, 0x00}, 2}, + {[]byte{0x01, 0x00, 0x7c, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde}, 12}, + {[]byte{0x01, 0x00, 0xdc, 0x7f, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde, 0xd9}, 13}, } var TranscoderTest = []struct { @@ -54,7 +52,7 @@ var TranscoderTest = []struct { // Test Encoding func TestEncode(t *testing.T) { - aenc, _ := NewAlawEncoder(io.Discard, Lpcm) + aenc, _ := NewCoder(io.Discard, Lpcm, Alaw) for _, tc := range EncoderTest { i, _ := aenc.Write(tc.data) if i != tc.expected { @@ -62,7 +60,7 @@ func TestEncode(t *testing.T) { } } aenc.Close() - uenc, _ := NewUlawEncoder(io.Discard, Lpcm) + uenc, _ := NewCoder(io.Discard, Lpcm, Ulaw) for _, tc := range EncoderTest { i, _ := uenc.Write(tc.data) if i != tc.expected { @@ -70,7 +68,7 @@ func TestEncode(t *testing.T) { } } uenc.Close() - utrans, _ := NewUlawEncoder(io.Discard, Alaw) + utrans, _ := NewCoder(io.Discard, Alaw, Ulaw) for _, tc := range TranscoderTest { i, _ := utrans.Write(tc.data) if i != tc.expected { @@ -82,50 +80,17 @@ func TestEncode(t *testing.T) { // Test Decoding func TestDecode(t *testing.T) { - b := new(bytes.Buffer) - adec, _ := NewAlawDecoder(b) + adec, _ := NewCoder(io.Discard, Alaw, Lpcm) for _, tc := range DecoderTest { - b.Write(tc.data) - p := make([]byte, 16) - var err error - var i, n int - for err == nil { - n, err = adec.Read(p) - i += n - } + i, _ := adec.Write(tc.data) if i != tc.expected { t.Errorf("Alaw Decode: expected: %d , actual: %d", tc.expected, i) } } adec.Close() - b.Reset() - udec, _ := NewUlawDecoder(b) - for _, tc := range DecoderTest { - b.Write(tc.data) - p := make([]byte, 16) - var err error - var i, n int - for err == nil { - n, err = udec.Read(p) - i += n - } - if i != tc.expected { - t.Errorf("ulaw Decode: expected: %d , actual: %d", tc.expected, i) - } - } - udec.Close() - b.Reset() - // Edge Case - udec, _ = NewUlawDecoder(iotest.TimeoutReader(b)) + udec, _ := NewCoder(io.Discard, Ulaw, Lpcm) for _, tc := range DecoderTest { - b.Write(tc.data) - p := make([]byte, 16) - var err error - var i, n int - for err == nil || err.Error() == "timeout" { - n, err = udec.Read(p) - i += n - } + i, _ := udec.Write(tc.data) if i != tc.expected { t.Errorf("ulaw Decode: expected: %d , actual: %d", tc.expected, i) } @@ -142,9 +107,9 @@ func BenchmarkAEncode(b *testing.B) { b.SetBytes(int64(len(rawData))) b.ResetTimer() for i := 0; i < b.N; i++ { - encoder, err := NewAlawEncoder(io.Discard, Lpcm) + encoder, err := NewCoder(io.Discard, Lpcm, Alaw) if err != nil { - b.Fatalf("Failed to create Writer: %s\n", err) + b.Fatalf("Failed to create Encoder: %s\n", err) } _, err = encoder.Write(rawData) if err != nil { @@ -164,9 +129,9 @@ func BenchmarkUEncode(b *testing.B) { b.SetBytes(int64(len(rawData))) b.ResetTimer() for i := 0; i < b.N; i++ { - encoder, err := NewUlawEncoder(io.Discard, Lpcm) + encoder, err := NewCoder(io.Discard, Lpcm, Ulaw) if err != nil { - b.Fatalf("Failed to create Writer: %s\n", err) + b.Fatalf("Failed to create Encoder: %s\n", err) } _, err = encoder.Write(rawData) @@ -188,9 +153,9 @@ func BenchmarkTranscode(b *testing.B) { b.SetBytes(int64(len(alawData))) b.ResetTimer() for i := 0; i < b.N; i++ { - transcoder, err := NewAlawEncoder(io.Discard, Ulaw) + transcoder, err := NewCoder(io.Discard, Ulaw, Alaw) if err != nil { - b.Fatalf("Failed to create Writer: %s\n", err) + b.Fatalf("Failed to create Transcoder: %s\n", err) } _, err = transcoder.Write(alawData) @@ -212,12 +177,12 @@ func BenchmarkUDecode(b *testing.B) { b.SetBytes(int64(len(ulawData))) b.ResetTimer() for i := 0; i < b.N; i++ { - decoder, err := NewUlawDecoder(bytes.NewReader(ulawData)) + decoder, err := NewCoder(io.Discard, Ulaw, Lpcm) if err != nil { b.Fatalf("Failed to create Reader: %s\n", err) } - _, err = io.Copy(io.Discard, decoder) + _, err = decoder.Write(ulawData) if err != nil { b.Fatalf("Decoding failed: %s\n", err) @@ -236,12 +201,12 @@ func BenchmarkADecode(b *testing.B) { b.SetBytes(int64(len(alawData))) b.ResetTimer() for i := 0; i < b.N; i++ { - decoder, err := NewAlawDecoder(bytes.NewReader(alawData)) + decoder, err := NewCoder(io.Discard, Alaw, Lpcm) if err != nil { b.Fatalf("Failed to create Reader: %s\n", err) } - _, err = io.Copy(io.Discard, decoder) + _, err = decoder.Write(alawData) if err != nil { b.Fatalf("Decoding failed: %s\n", err) diff --git a/testing/Readme.md b/testing/Readme.md index 2ca9fcd..0ca2836 100644 --- a/testing/Readme.md +++ b/testing/Readme.md @@ -3,6 +3,7 @@ ## Sound files ``` dtmf-1234.raw : Signed 16 bit Little Endian, Rate 8000 Hz, Mono +piano.raw : Signed 16 bit Little Endian, Rate 8000 Hz, Mono silence-1s.raw : Signed 16 bit Little Endian, Rate 8000 Hz, Mono sine-440Hz-1s.raw : Signed 16 bit Little Endian, Rate 8000 Hz, Mono speech.alaw : A-Law, Rate 8000 Hz, Mono diff --git a/testing/piano.raw b/testing/piano.raw new file mode 100644 index 0000000000000000000000000000000000000000..76b52fb80bca4d268ff4964e59472b8553ccac29 GIT binary patch literal 100904 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 literal 0 HcmV?d00001 diff --git a/ulaw.go b/ulaw.go index aec8955..af6f7e3 100644 --- a/ulaw.go +++ b/ulaw.go @@ -71,7 +71,7 @@ var ( 232, 233, 238, 239, 236, 237, 226, 227, 224, 225, 230, 231, 228, 229, 251, 249, 254, 255, 252, 253, 242, 243, 240, 241, 246, 247, 244, 245, 203, 201, 207, 205, 194, 195, 192, 193, 198, 199, 196, 197, 218, 219, 216, 217, 222, 223, 220, 221, - 210, 210, 211, 211, 208, 208, 209, 209, 214, 214, 215, 215, 212, 212, 213, 0, + 210, 210, 211, 211, 208, 208, 209, 209, 214, 214, 215, 215, 212, 212, 213, 213, } ) From 2725f9be21f2467bde534bea7fe282dcc3efba25 Mon Sep 17 00:00:00 2001 From: Lefteris Zafiris Date: Tue, 9 Jan 2024 19:34:35 +0200 Subject: [PATCH 4/7] Fix ualw to alaw transcoding --- ulaw.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ulaw.go b/ulaw.go index af6f7e3..8752dac 100644 --- a/ulaw.go +++ b/ulaw.go @@ -121,7 +121,7 @@ func Ulaw2Alaw(ulaw []byte) []byte { for i := 0; i < len(alaw); i++ { alaw[i] = ulaw2alaw[ulaw[i]] } - return ulaw + return alaw } // Ulaw2AlawFrame directly converts a u-law frame to A-law From d0afa73f24af2c084b79199e4c66fc4688e460de Mon Sep 17 00:00:00 2001 From: Lefteris Zafiris Date: Wed, 10 Jan 2024 00:26:23 +0200 Subject: [PATCH 5/7] Test updates --- g711.go | 6 ++++++ g711_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/g711.go b/g711.go index 8e3a471..19fc96a 100644 --- a/g711.go +++ b/g711.go @@ -88,12 +88,18 @@ func NewCoder(writer io.Writer, input, output int) (*Coder, error) { // Close closes the Encoder, it implements the io.Closer interface. func (w *Coder) Close() error { + w.destination = nil + w.translate = nil + w.multiplier = 0 w = nil return nil } // Reset discards the Encoder state. This permits reusing an Encoder rather than allocating a new one. func (w *Coder) Reset(writer io.Writer) error { + if w == nil || w.translate == nil || w.destination == nil { + return errors.New("coder is uninitialized or closed") + } if writer == nil { return errors.New("io.Writer is nil") } diff --git a/g711_test.go b/g711_test.go index d008357..127ef39 100644 --- a/g711_test.go +++ b/g711_test.go @@ -12,11 +12,65 @@ package g711 import ( + "bytes" "io" "os" "testing" ) +func TestNewCoder(t *testing.T) { + writer := bytes.NewBuffer([]byte{}) + input := Lpcm + output := Alaw + // Test case: Valid input + coder, err := NewCoder(writer, input, output) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + if coder == nil { + t.Error("Expected coder to not be nil") + } + // Test case: Nil writer + _, err = NewCoder(nil, input, output) + if err == nil { + t.Error("Expected error, got nil") + } + // Test case: Invalid input format + _, err = NewCoder(writer, 999, output) + if err == nil { + t.Error("Expected error, got nil") + } + // Test case: Invalid output format + _, err = NewCoder(writer, input, 999) + if err == nil { + t.Error("Expected error, got nil") + } +} + +func TestReset(t *testing.T) { + // Test case 1: Coder is not nil, writer is not nil + w, _ := NewCoder(io.Discard, Lpcm, Alaw) + writer := bytes.NewBufferString("") + err := w.Reset(writer) + if err != nil { + t.Errorf("Expected no error, but got: %v", err) + } + w.Close() + // Test case 2: Coder is nil + nilCoder, _ := NewCoder(io.Discard, Lpcm, Alaw) + nilCoder.Close() + err = nilCoder.Reset(writer) + if err == nil || err.Error() != "coder is uninitialized or closed" { + t.Errorf("Expected error: 'coder is uninitialized or closed', but got: %v", err) + } + // Test case 3: writer is nil + w, _ = NewCoder(io.Discard, Lpcm, Alaw) + err = w.Reset(nil) + if err == nil || err.Error() != "io.Writer is nil" { + t.Errorf("Expected error: 'io.Writer is nil', but got: %v", err) + } +} + var EncoderTest = []struct { data []byte expected int From 10c9a565fdb7f65a81382fb60341f5f9b7dc79fd Mon Sep 17 00:00:00 2001 From: Lefteris Zafiris Date: Sat, 16 Mar 2024 14:58:17 +0200 Subject: [PATCH 6/7] Small change --- g711.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/g711.go b/g711.go index 19fc96a..ee104fa 100644 --- a/g711.go +++ b/g711.go @@ -32,9 +32,15 @@ const ( type Coder struct { translate func([]byte) []byte // enc/decoding function destination io.Writer // output data - multiplier float64 + written int8 // reported written size } +const ( + // written parameters + double = iota + 1 + half +) + // 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 NewCoder(writer io.Writer, input, output int) (*Coder, error) { @@ -42,16 +48,16 @@ func NewCoder(writer io.Writer, input, output int) (*Coder, error) { return nil, errors.New("io.Writer is nil") } var translate func([]byte) []byte - multiplier := 1.0 + var written int8 switch input { case Lpcm: switch output { case Alaw: translate = EncodeAlaw - multiplier = 2 + written = double case Ulaw: translate = EncodeUlaw - multiplier = 2 + written = double default: return nil, errors.New("invalid output format") } @@ -59,7 +65,7 @@ func NewCoder(writer io.Writer, input, output int) (*Coder, error) { switch output { case Lpcm: translate = DecodeAlaw - multiplier = 0.5 + written = half case Ulaw: translate = Alaw2Ulaw default: @@ -69,7 +75,7 @@ func NewCoder(writer io.Writer, input, output int) (*Coder, error) { switch output { case Lpcm: translate = DecodeUlaw - multiplier = 0.5 + written = half case Alaw: translate = Ulaw2Alaw default: @@ -81,7 +87,7 @@ func NewCoder(writer io.Writer, input, output int) (*Coder, error) { w := Coder{ translate: translate, destination: writer, - multiplier: multiplier, + written: written, } return &w, nil } @@ -90,7 +96,7 @@ func NewCoder(writer io.Writer, input, output int) (*Coder, error) { func (w *Coder) Close() error { w.destination = nil w.translate = nil - w.multiplier = 0 + w.written = 0 w = nil return nil } @@ -118,6 +124,11 @@ func (w *Coder) Write(p []byte) (int, error) { // If we are encoding to g711 we need to multiply the number of bytes written by 2 to avoid reporting short writes // this happens because 2 bytes of input data are encoded to 1 byte of output data. // In a similar manner if we are decoding from g711 we need to divide the number of bytes written by 2. - i = int(float64(i) * w.multiplier) + switch { + case w.written == double: + i <<= 1 + case w.written == half: + i >>= 1 + } return i, err } From 10be6f3187ae45508b009e5ecdd74e829852cae0 Mon Sep 17 00:00:00 2001 From: Lefteris Zafiris Date: Sat, 14 Dec 2024 16:35:59 +0200 Subject: [PATCH 7/7] Added zero allocation functions Fucntions to encode/decode/transcode using user provided buffers. --- Readme.md | 88 ++++++++++++++++++++++++++++++++++++++++------------ alaw.go | 27 ++++++++++++++-- alaw_test.go | 54 ++++++++++++++++++++++++++++++-- g711.go | 3 ++ ulaw.go | 27 ++++++++++++++-- ulaw_test.go | 54 ++++++++++++++++++++++++++++++-- 6 files changed, 221 insertions(+), 32 deletions(-) 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 } }