]> Sergey Matveev's repositories - go-opus.git/commitdiff
Unit test for stereo sound
authorHraban Luyat <hraban@0brg.net>
Mon, 10 Oct 2016 15:18:03 +0000 (16:18 +0100)
committerHraban Luyat <hraban@0brg.net>
Mon, 10 Oct 2016 15:18:03 +0000 (16:18 +0100)
README.md
encoder.go
opus_test.go
stream_test.go
utils_test.go

index 7580a421388bb8e15a1190ed1112ead15efbb1f4..4f72eee997c75f2b425ff9a7fce2db716d214bf4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -55,6 +55,14 @@ if err != nil {
 data = data[:n] // only the first N bytes are opus data. Just like io.Reader.
 ```
 
+Note that you must choose a target buffer size, and this buffer size will affect
+the encoding process:
+
+> Size of the allocated memory for the output payload. This may be used to
+> impose an upper limit on the instant bitrate, but should not be used as the
+> only bitrate control. Use `OPUS_SET_BITRATE` to control the bitrate.
+
+- https://opus-codec.org/docs/opus_api-1.1.3/group__opus__encoder.html
 
 ### Decoding
 
index bd50405633684c7ce30ff9e5d68561a50e9bbbda..5821d287f3cae77bbafb78c616aa523994ea234b 100644 (file)
@@ -41,7 +41,8 @@ var errEncUninitialized = fmt.Errorf("opus encoder uninitialized")
 
 // Encoder contains the state of an Opus encoder for libopus.
 type Encoder struct {
-       p *C.struct_OpusEncoder
+       p        *C.struct_OpusEncoder
+       channels int
        // Memory for the encoder struct allocated on the Go heap to allow Go GC to
        // manage it (and obviate need to free())
        mem []byte
@@ -69,6 +70,7 @@ func (enc *Encoder) Init(sample_rate int, channels int, application Application)
                return fmt.Errorf("Number of channels must be 1 or 2: %d", channels)
        }
        size := C.opus_encoder_get_size(C.int(channels))
+       enc.channels = channels
        enc.mem = make([]byte, size)
        enc.p = (*C.OpusEncoder)(unsafe.Pointer(&enc.mem[0]))
        errno := int(C.opus_encoder_init(
@@ -94,10 +96,14 @@ func (enc *Encoder) Encode(pcm []int16, data []byte) (int, error) {
        if len(data) == 0 {
                return 0, fmt.Errorf("opus: no target buffer")
        }
+       if len(pcm)%enc.channels != 0 {
+               return 0, fmt.Errorf("opus: input buffer length must be multiple of channels")
+       }
+       samples := len(pcm) / enc.channels
        n := int(C.opus_encode(
                enc.p,
                (*C.opus_int16)(&pcm[0]),
-               C.int(len(pcm)),
+               C.int(samples),
                (*C.uchar)(&data[0]),
                C.opus_int32(cap(data))))
        if n < 0 {
@@ -118,10 +124,14 @@ func (enc *Encoder) EncodeFloat32(pcm []float32, data []byte) (int, error) {
        if len(data) == 0 {
                return 0, fmt.Errorf("opus: no target buffer")
        }
+       if len(pcm)%enc.channels != 0 {
+               return 0, fmt.Errorf("opus: input buffer length must be multiple of channels")
+       }
+       samples := len(pcm) / enc.channels
        n := int(C.opus_encode_float(
                enc.p,
                (*C.float)(&pcm[0]),
-               C.int(len(pcm)),
+               C.int(samples),
                (*C.uchar)(&data[0]),
                C.opus_int32(cap(data))))
        if n < 0 {
index 09966fe12a163978d691eee4171607b6a089a0df..87fe1104eb54b5a614745466add6453a712d9924 100644 (file)
@@ -80,6 +80,8 @@ func TestCodecFloat32(t *testing.T) {
        if err != nil || dec == nil {
                t.Fatalf("Error creating new decoder: %v", err)
        }
+       // TODO: Uh-oh.. it looks like I forgot to put a data = data[:n] here, yet
+       // the test is not failing. Why?
        n, err = dec.DecodeFloat32(data, pcm)
        if err != nil {
                t.Fatalf("Couldn't decode data: %v", err)
@@ -88,3 +90,55 @@ func TestCodecFloat32(t *testing.T) {
                t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n)
        }
 }
+
+func TestStereo(t *testing.T) {
+       // Create bogus input sound
+       const G4 = 391.995
+       const E3 = 164.814
+       const SAMPLE_RATE = 48000
+       const FRAME_SIZE_MS = 60
+       const CHANNELS = 2
+       const FRAME_SIZE_MONO = SAMPLE_RATE * FRAME_SIZE_MS / 1000
+
+       enc, err := NewEncoder(SAMPLE_RATE, CHANNELS, APPLICATION_VOIP)
+       if err != nil || enc == nil {
+               t.Fatalf("Error creating new encoder: %v", err)
+       }
+       dec, err := NewDecoder(SAMPLE_RATE, CHANNELS)
+       if err != nil || dec == nil {
+               t.Fatalf("Error creating new decoder: %v", err)
+       }
+
+       // Source signal (left G4, right E3)
+       left := make([]int16, FRAME_SIZE_MONO)
+       right := make([]int16, FRAME_SIZE_MONO)
+       addSine(left, SAMPLE_RATE, G4)
+       addSine(right, SAMPLE_RATE, E3)
+       pcm := interleave(left, right)
+
+       data := make([]byte, 1000)
+       n, err := enc.Encode(pcm, data)
+       if err != nil {
+               t.Fatalf("Couldn't encode data: %v", err)
+       }
+       data = data[:n]
+       n, err = dec.Decode(data, pcm)
+       if err != nil {
+               t.Fatal("Couldn't decode data: %v", err)
+       }
+       if n*CHANNELS != len(pcm) {
+               t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n*CHANNELS)
+       }
+
+       // This is hard to check programatically, but I plotted the graphs in a
+       // spreadsheet and it looked great. The encoded waves both start out with a
+       // string of zero samples before they pick up, and the G4 is phase shifted
+       // by half a period, but that's all fine for lossy audio encoding.
+       /*
+               leftdec, rightdec := split(pcm)
+               fmt.Printf("left_in,left_out,right_in,right_out\n")
+               for i := range left {
+                       fmt.Printf("%d,%d,%d,%d\n", left[i], leftdec[i], right[i], rightdec[i])
+               }
+       */
+}
index 95bebfb2646bd95ae40a16cb19cb2146873b0535..f9fdff29353b0c8379d3717ae50c15e1addcb2f2 100644 (file)
@@ -8,7 +8,6 @@ import (
        "fmt"
        "io"
        "io/ioutil"
-       "math"
        "os"
        "reflect"
        "strings"
@@ -87,23 +86,6 @@ func extractWavPcm(t *testing.T, fname string) []int16 {
        return samples
 }
 
-func maxDiff(a []int16, b []int16) int32 {
-       if len(a) != len(b) {
-               return math.MaxInt16
-       }
-       var max int32 = 0
-       for i := range a {
-               d := int32(a[i]) - int32(b[i])
-               if d < 0 {
-                       d = -d
-               }
-               if d > max {
-                       max = d
-               }
-       }
-       return max
-}
-
 func TestStream(t *testing.T) {
        opuspcm := opus2pcm(t, "testdata/speech_8.opus", 10000)
        wavpcm := extractWavPcm(t, "testdata/speech_8.wav")
index 77985323e1141392422ca0d71c4f338a7edfe1be..dbd37ea28cee06e36e6c67275530e980f14b4995 100644 (file)
@@ -20,6 +20,48 @@ func addSineFloat32(buf []float32, sampleRate int, freq float64) {
 func addSine(buf []int16, sampleRate int, freq float64) {
        factor := 2 * math.Pi * freq / float64(sampleRate)
        for i := range buf {
-               buf[i] += int16(math.Sin(float64(i)*factor) * math.MaxInt16)
+               buf[i] += int16(math.Sin(float64(i)*factor) * (math.MaxInt16 - 1))
        }
 }
+
+func maxDiff(a []int16, b []int16) int32 {
+       if len(a) != len(b) {
+               return math.MaxInt16
+       }
+       var max int32 = 0
+       for i := range a {
+               d := int32(a[i]) - int32(b[i])
+               if d < 0 {
+                       d = -d
+               }
+               if d > max {
+                       max = d
+               }
+       }
+       return max
+}
+
+func interleave(a []int16, b []int16) []int16 {
+       if len(a) != len(b) {
+               panic("interleave: buffers must have equal length")
+       }
+       result := make([]int16, 2*len(a))
+       for i := range a {
+               result[2*i] = a[i]
+               result[2*i+1] = b[i]
+       }
+       return result
+}
+
+func split(interleaved []int16) ([]int16, []int16) {
+       if len(interleaved)%2 != 0 {
+               panic("split: interleaved buffer must have even number of samples")
+       }
+       left := make([]int16, len(interleaved)/2)
+       right := make([]int16, len(interleaved)/2)
+       for i := range left {
+               left[i] = interleaved[2*i]
+               right[i] = interleaved[2*i+1]
+       }
+       return left, right
+}