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 {
// 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