Implement bitdepth conversion and upsampling

bitdepth package takes F32LE -> S16LE and handles upsampling
from 16Khz -> 48Khz
This commit is contained in:
Sean DuBois 2022-09-30 21:56:49 -06:00
parent 7941dd8c63
commit 733188ab17
6 changed files with 59 additions and 13 deletions

View file

@ -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
}

View file

@ -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
```

View file

@ -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: <in-file> <out-file>")
@ -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)
}
}
}

View file

@ -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
}

View file

@ -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")
}
}

View file

@ -0,0 +1,7 @@
package bitdepth
import "errors"
var (
errBufferLengthMismatch = errors.New("length of in and out buffer are not equal")
)