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