From 5ba421ac739bfc037bdad415edf61a7644c0229a Mon Sep 17 00:00:00 2001
From: elinor <elinor.alp@gmail.com>
Date: Mon, 26 Mar 2018 10:33:31 +0300
Subject: [PATCH] Added decode with fec + test

---
 decoder.go   | 28 +++++++++++++++++-
 opus_test.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/decoder.go b/decoder.go
index 99b8e61..9b5b117 100644
--- a/decoder.go
+++ b/decoder.go
@@ -112,8 +112,34 @@ func (dec *Decoder) DecodeFloat32(data []byte, pcm []float32) (int, error) {
 	return n, nil
 }
 
+// Decode encoded Opus data into the supplied buffer with forward error
+// correction. The supplied buffer will be entirely filled.
+func (dec *Decoder) DecodeFEC(data []byte, pcm []int16) error {
+	if dec.p == nil {
+		return errDecUninitialized
+	}
+	if len(data) == 0 {
+		return fmt.Errorf("opus: no data supplied")
+	}
+	if len(pcm) == 0 {
+		return fmt.Errorf("opus: target buffer empty")
+	}
+
+	n := int(C.opus_decode(
+		dec.p,
+		(*C.uchar)(&data[0]),
+		C.opus_int32(len(data)),
+		(*C.opus_int16)(&pcm[0]),
+		C.int(cap(pcm)),
+		1))
+	if n < 0 {
+		return Error(n)
+	}
+	return nil
+}
+
 // Gets the duration (in samples) of the last packet successfully decoded or concealed.
-func (dec *Decoder) LastPacketDuration() (int,error){
+func (dec *Decoder) LastPacketDuration() (int, error) {
 	var samples C.opus_int32
 	res := C.bridge_decoder_get_last_packet_duration(dec.p, &samples)
 	if res != C.OPUS_OK {
diff --git a/opus_test.go b/opus_test.go
index 77ea94f..f894d5b 100644
--- a/opus_test.go
+++ b/opus_test.go
@@ -59,6 +59,90 @@ func TestCodec(t *testing.T) {
 	// passes error codes through, and that's that.
 }
 
+func TestCodecFEC(t *testing.T) {
+	// Create bogus input sound
+	const G4 = 391.995
+	const SAMPLE_RATE = 48000
+	const FRAME_SIZE_MS = 60
+	const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
+	const PACKET_SIZE = 480 // 10ms packet
+	pcm := make([]int16, 0)
+
+	enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
+	if err != nil || enc == nil {
+		t.Fatalf("Error creating new encoder: %v", err)
+	}
+	enc.SetPacketLossPerc(30)
+	enc.SetInBandFEC(1)
+
+	dec, err := NewDecoder(SAMPLE_RATE, 1)
+	if err != nil || dec == nil {
+		t.Fatalf("Error creating new decoder: %v", err)
+	}
+
+	mono := make([]int16, FRAME_SIZE)
+	addSine(mono, SAMPLE_RATE, G4)
+
+	encodedData := make([][]byte, FRAME_SIZE/PACKET_SIZE)
+	for i, idx := 0, 0; i < len(mono); i += PACKET_SIZE {
+		data := make([]byte, 1000)
+		n, err := enc.Encode(mono[i:i+PACKET_SIZE], data)
+		if err != nil {
+			t.Fatalf("Couldn't encode data: %v", err)
+		}
+		data = data[:n]
+		encodedData[idx] = data
+		idx++
+	}
+
+	lost := false
+	for i := 0; i < len(encodedData); i++ {
+		// "Dropping" packets 2 and 4
+		if i == 2 || i == 4 {
+			lost = true
+			continue
+		}
+		if lost {
+			samples, err := dec.LastPacketDuration()
+			if err != nil {
+				t.Fatalf("Couldn't get last packet duration: %v", err)
+			}
+
+			pcmBuffer := make([]int16, samples)
+			if err = dec.DecodeFEC(encodedData[i], pcmBuffer); err != nil {
+				t.Fatalf("Couldn't decode data: %v", err)
+			}
+			pcm = append(pcm, pcmBuffer...)
+			lost = false
+		}
+
+		pcmBuffer := make([]int16, FRAME_SIZE)
+		n, err := dec.Decode(encodedData[i], pcmBuffer)
+		if err != nil {
+			t.Fatalf("Couldn't decode data: %v", err)
+		}
+		pcmBuffer = pcmBuffer[:n]
+		if len(pcmBuffer) != n {
+			t.Fatalf("Length mismatch: %d samples in, %d out", len(pcmBuffer), n)
+		}
+		pcm = append(pcm, pcmBuffer...)
+	}
+
+	if len(mono) != len(pcm) {
+		t.Fatalf("Input/Output length mismatch: %d samples in, %d out", len(mono), len(pcm))
+	}
+
+	// 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.
+
+	//fmt.Printf("in,out\n")
+	//for i := range mono {
+	//	fmt.Printf("%d,%d\n", mono[i], pcm[i])
+	//}
+}
+
 func TestCodecFloat32(t *testing.T) {
 	// Create bogus input sound
 	const G4 = 391.995
-- 
2.51.0