From 733188ab176b34f8f4744d42ef4ad87b08f2284f Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Fri, 30 Sep 2022 21:56:49 -0600 Subject: [PATCH] Implement bitdepth conversion and upsampling bitdepth package takes F32LE -> S16LE and handles upsampling from 16Khz -> 48Khz --- decoder.go | 11 +++++++++-- examples/decode/README.md | 4 ++-- examples/decode/main.go | 12 +++--------- internal/bitdepth/bitdepth.go | 22 ++++++++++++++++++++++ internal/bitdepth/bitdepth_test.go | 16 ++++++++++++++++ internal/bitdepth/errors.go | 7 +++++++ 6 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 internal/bitdepth/bitdepth.go create mode 100644 internal/bitdepth/bitdepth_test.go create mode 100644 internal/bitdepth/errors.go diff --git a/decoder.go b/decoder.go index eaad268..882452b 100644 --- a/decoder.go +++ b/decoder.go @@ -3,23 +3,26 @@ package opus import ( "fmt" + "github.com/pion/opus/internal/bitdepth" "github.com/pion/opus/internal/silk" ) // Decoder decodes the Opus bitstream into PCM type Decoder struct { silkDecoder silk.Decoder + silkBuffer []float32 } // NewDecoder creates a new Opus Decoder func NewDecoder() Decoder { return Decoder{ silkDecoder: silk.NewDecoder(), + silkBuffer: make([]float32, 320), } } // Decode decodes the Opus bitstream into PCM -func (d *Decoder) Decode(in []byte, out []float32) (bandwidth Bandwidth, isStereo bool, err error) { +func (d *Decoder) Decode(in []byte, out []byte) (bandwidth Bandwidth, isStereo bool, err error) { if len(in) < 1 { return 0, false, errTooShortForTableOfContentsHeader } @@ -40,11 +43,15 @@ func (d *Decoder) Decode(in []byte, out []float32) (bandwidth Bandwidth, isStere } for _, encodedFrame := range encodedFrames { - err := d.silkDecoder.Decode(encodedFrame, out, tocHeader.isStereo(), cfg.frameDuration().nanoseconds(), silk.Bandwidth(cfg.bandwidth())) + err := d.silkDecoder.Decode(encodedFrame, d.silkBuffer, tocHeader.isStereo(), cfg.frameDuration().nanoseconds(), silk.Bandwidth(cfg.bandwidth())) if err != nil { return 0, false, err } } + if err := bitdepth.ConvertFloat32LittleEndianToSigned16LittleEndian(d.silkBuffer, out, 3); err != nil { + return 0, false, err + } + return cfg.bandwidth(), tocHeader.isStereo(), nil } diff --git a/examples/decode/README.md b/examples/decode/README.md index bbeef3a..3cfa5ea 100644 --- a/examples/decode/README.md +++ b/examples/decode/README.md @@ -28,9 +28,9 @@ decoder `pwd`/output.ogg `audio-samples.pcm` Now play the audio with the tool of your choice. ``` -gst-launch-1.0 filesrc location=audio-samples.pcm ! audio/x-raw, format=F32LE, rate=16000,channels=1 ! autoaudiosink -v +gst-launch-1.0 filesrc location=audio-samples.pcm ! audio/x-raw, format=S16LE, rate=48000,channels=1 ! autoaudiosink -v ``` ``` -ffplay -f f32le -ar 16000 audio-samples.pcm +ffplay -f s16le -ar 48000 audio-samples.pcm ``` diff --git a/examples/decode/main.go b/examples/decode/main.go index d2e895c..24c42fe 100644 --- a/examples/decode/main.go +++ b/examples/decode/main.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "encoding/binary" "errors" "io" "os" @@ -11,12 +10,6 @@ import ( "github.com/pion/opus/pkg/oggreader" ) -func convertFloatToByteSlice(i []float32) []byte { - buf := new(bytes.Buffer) - binary.Write(buf, binary.LittleEndian, i) - return buf.Bytes() -} - func main() { if len(os.Args) != 3 { panic("Usage: ") @@ -32,13 +25,14 @@ func main() { panic(err) } - out := make([]float32, 320) + out := make([]byte, 1920) f, err := os.Create(os.Args[2]) if err != nil { panic(err) } decoder := opus.NewDecoder() + x := 0 for { segments, _, err := ogg.ParseNextPage() @@ -57,7 +51,7 @@ func main() { panic(err) } - f.Write(convertFloatToByteSlice(out)) + f.Write(out) } } } diff --git a/internal/bitdepth/bitdepth.go b/internal/bitdepth/bitdepth.go new file mode 100644 index 0000000..6fa96fb --- /dev/null +++ b/internal/bitdepth/bitdepth.go @@ -0,0 +1,22 @@ +package bitdepth + +import ( + "math" +) + +func ConvertFloat32LittleEndianToSigned16LittleEndian(in []float32, out []byte, resampleCount int) error { + currIndex := 0 + for i := range in { + res := int16(math.Floor(float64(in[i] * 32767))) + + for j := resampleCount; j > 0; j-- { + out[currIndex] = byte(res & 0b11111111) + currIndex++ + + out[currIndex] = (byte(res >> 8)) + currIndex++ + } + } + + return nil +} diff --git a/internal/bitdepth/bitdepth_test.go b/internal/bitdepth/bitdepth_test.go new file mode 100644 index 0000000..5a95b3c --- /dev/null +++ b/internal/bitdepth/bitdepth_test.go @@ -0,0 +1,16 @@ +package bitdepth + +import ( + "bytes" + "testing" +) + +func TestConvertFloat32LittleEndianToSigned16LittleEndian(t *testing.T) { + in := []float32{0.3, 0, .55, .72, -.05} + out := make([]byte, len(in)*2) + ConvertFloat32LittleEndianToSigned16LittleEndian(in, out, 1) + + if !bytes.Equal([]byte{0x66, 0x26, 0x00, 0x00, 0x65, 0x46, 0x28, 0x5c, 0x99, 0xf9}, out) { + t.Fatal("buffer mismatch") + } +} diff --git a/internal/bitdepth/errors.go b/internal/bitdepth/errors.go new file mode 100644 index 0000000..75e4e3d --- /dev/null +++ b/internal/bitdepth/errors.go @@ -0,0 +1,7 @@ +package bitdepth + +import "errors" + +var ( + errBufferLengthMismatch = errors.New("length of in and out buffer are not equal") +)