Implement bitdepth conversion and upsampling
bitdepth package takes F32LE -> S16LE and handles upsampling from 16Khz -> 48Khz
This commit is contained in:
parent
7941dd8c63
commit
733188ab17
11
decoder.go
11
decoder.go
|
@ -3,23 +3,26 @@ package opus
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/opus/internal/bitdepth"
|
||||||
"github.com/pion/opus/internal/silk"
|
"github.com/pion/opus/internal/silk"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoder decodes the Opus bitstream into PCM
|
// Decoder decodes the Opus bitstream into PCM
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
silkDecoder silk.Decoder
|
silkDecoder silk.Decoder
|
||||||
|
silkBuffer []float32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDecoder creates a new Opus Decoder
|
// NewDecoder creates a new Opus Decoder
|
||||||
func NewDecoder() Decoder {
|
func NewDecoder() Decoder {
|
||||||
return Decoder{
|
return Decoder{
|
||||||
silkDecoder: silk.NewDecoder(),
|
silkDecoder: silk.NewDecoder(),
|
||||||
|
silkBuffer: make([]float32, 320),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes the Opus bitstream into PCM
|
// 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 {
|
if len(in) < 1 {
|
||||||
return 0, false, errTooShortForTableOfContentsHeader
|
return 0, false, errTooShortForTableOfContentsHeader
|
||||||
}
|
}
|
||||||
|
@ -40,11 +43,15 @@ func (d *Decoder) Decode(in []byte, out []float32) (bandwidth Bandwidth, isStere
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, encodedFrame := range encodedFrames {
|
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 {
|
if err != nil {
|
||||||
return 0, false, err
|
return 0, false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := bitdepth.ConvertFloat32LittleEndianToSigned16LittleEndian(d.silkBuffer, out, 3); err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
return cfg.bandwidth(), tocHeader.isStereo(), nil
|
return cfg.bandwidth(), tocHeader.isStereo(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ decoder `pwd`/output.ogg `audio-samples.pcm`
|
||||||
Now play the audio with the tool of your choice.
|
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
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -11,12 +10,6 @@ import (
|
||||||
"github.com/pion/opus/pkg/oggreader"
|
"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() {
|
func main() {
|
||||||
if len(os.Args) != 3 {
|
if len(os.Args) != 3 {
|
||||||
panic("Usage: <in-file> <out-file>")
|
panic("Usage: <in-file> <out-file>")
|
||||||
|
@ -32,13 +25,14 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
out := make([]float32, 320)
|
out := make([]byte, 1920)
|
||||||
f, err := os.Create(os.Args[2])
|
f, err := os.Create(os.Args[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder := opus.NewDecoder()
|
decoder := opus.NewDecoder()
|
||||||
|
x := 0
|
||||||
for {
|
for {
|
||||||
segments, _, err := ogg.ParseNextPage()
|
segments, _, err := ogg.ParseNextPage()
|
||||||
|
|
||||||
|
@ -57,7 +51,7 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Write(convertFloatToByteSlice(out))
|
f.Write(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
22
internal/bitdepth/bitdepth.go
Normal file
22
internal/bitdepth/bitdepth.go
Normal 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
|
||||||
|
}
|
16
internal/bitdepth/bitdepth_test.go
Normal file
16
internal/bitdepth/bitdepth_test.go
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
7
internal/bitdepth/errors.go
Normal file
7
internal/bitdepth/errors.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package bitdepth
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
errBufferLengthMismatch = errors.New("length of in and out buffer are not equal")
|
||||||
|
)
|
Loading…
Reference in a new issue