Use libopusenc to encode stream, WiP: tests
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
DataHoarder 2022-02-27 18:56:08 +01:00
parent c9b07c6bec
commit 6cc027f24d
6 changed files with 263 additions and 726 deletions

View file

@ -9,15 +9,28 @@
// plays nice with the CGo rules and avoids any confusion. // plays nice with the CGo rules and avoids any confusion.
#include <opusfile.h> #include <opusfile.h>
#include <opusenc.h>
#include <stdint.h> #include <stdint.h>
// Defined in Go. Uses the same signature as Go, no need for proxy function. // Defined in Go. Uses the same signature as Go, no need for proxy function.
int go_readcallback(void *p, unsigned char *buf, int nbytes); int go_readcallback(void *p, unsigned char *buf, int nbytes);
int go_writecallback(void *p, const unsigned char *buf, opus_int32 len);
int closecallback(void *p) {
return 0;
}
static struct OpusFileCallbacks callbacks = { static struct OpusFileCallbacks callbacks = {
.read = go_readcallback, .read = go_readcallback,
}; };
static OpusEncCallbacks encodeCallbacks = {
.write = go_writecallback,
.close = closecallback,
};
static OggOpusComments* emptyComment = NULL;
// Proxy function for op_open_callbacks, because it takes a void * context but // Proxy function for op_open_callbacks, because it takes a void * context but
// we want to pass it non-pointer data, namely an arbitrary uintptr_t // we want to pass it non-pointer data, namely an arbitrary uintptr_t
// value. This is legal C, but go test -race (-d=checkptr) complains anyway. So // value. This is legal C, but go test -race (-d=checkptr) complains anyway. So
@ -28,3 +41,99 @@ my_open_callbacks(uintptr_t p, int *error)
{ {
return op_open_callbacks((void *)p, &callbacks, NULL, 0, error); return op_open_callbacks((void *)p, &callbacks, NULL, 0, error);
} }
OpusEncCallbacks *
my_write_callbacks()
{
return &encodeCallbacks;
}
OggOpusComments*
my_empty_comment()
{
if (emptyComment == NULL) {
emptyComment = ope_comments_create();
}
return emptyComment;
}
int
bridge_encoder_set_dtx(OggOpusEnc *st, opus_int32 use_dtx)
{
return ope_encoder_ctl(st, OPUS_SET_DTX(use_dtx));
}
int
bridge_encoder_get_dtx(OggOpusEnc *st, opus_int32 *dtx)
{
return ope_encoder_ctl(st, OPUS_GET_DTX(dtx));
}
int
bridge_encoder_get_sample_rate(OggOpusEnc *st, opus_int32 *sample_rate)
{
return ope_encoder_ctl(st, OPUS_GET_SAMPLE_RATE(sample_rate));
}
int
bridge_encoder_set_bitrate(OggOpusEnc *st, opus_int32 bitrate)
{
return ope_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
}
int
bridge_encoder_get_bitrate(OggOpusEnc *st, opus_int32 *bitrate)
{
return ope_encoder_ctl(st, OPUS_GET_BITRATE(bitrate));
}
int
bridge_encoder_set_complexity(OggOpusEnc *st, opus_int32 complexity)
{
return ope_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
}
int
bridge_encoder_get_complexity(OggOpusEnc *st, opus_int32 *complexity)
{
return ope_encoder_ctl(st, OPUS_GET_COMPLEXITY(complexity));
}
int
bridge_encoder_set_max_bandwidth(OggOpusEnc *st, opus_int32 max_bw)
{
return ope_encoder_ctl(st, OPUS_SET_MAX_BANDWIDTH(max_bw));
}
int
bridge_encoder_get_max_bandwidth(OggOpusEnc *st, opus_int32 *max_bw)
{
return ope_encoder_ctl(st, OPUS_GET_MAX_BANDWIDTH(max_bw));
}
int
bridge_encoder_set_inband_fec(OggOpusEnc *st, opus_int32 fec)
{
return ope_encoder_ctl(st, OPUS_SET_INBAND_FEC(fec));
}
int
bridge_encoder_get_inband_fec(OggOpusEnc *st, opus_int32 *fec)
{
return ope_encoder_ctl(st, OPUS_GET_INBAND_FEC(fec));
}
int
bridge_encoder_set_packet_loss_perc(OggOpusEnc *st, opus_int32 loss_perc)
{
return ope_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(loss_perc));
}
int
bridge_encoder_get_packet_loss_perc(OggOpusEnc *st, opus_int32 *loss_perc)
{
return ope_encoder_ctl(st, OPUS_GET_PACKET_LOSS_PERC(loss_perc));
}

View file

@ -92,7 +92,7 @@ func (dec *Decoder) Decode(data []byte, pcm []int16) (int, error) {
return n, nil return n, nil
} }
// Decode encoded Opus data into the supplied buffer. On success, returns the // DecodeFloat32 encoded Opus data into the supplied buffer. On success, returns the
// number of samples correctly written to the target buffer. // number of samples correctly written to the target buffer.
func (dec *Decoder) DecodeFloat32(data []byte, pcm []float32) (int, error) { func (dec *Decoder) DecodeFloat32(data []byte, pcm []float32) (int, error) {
if dec.p == nil { if dec.p == nil {

View file

@ -30,39 +30,3 @@ func TestDecoderUnitialized(t *testing.T) {
t.Errorf("Expected \"unitialized decoder\" error: %v", err) t.Errorf("Expected \"unitialized decoder\" error: %v", err)
} }
} }
func TestDecoder_GetLastPacketDuration(t *testing.T) {
const G4 = 391.995
const SAMPLE_RATE = 48000
const FRAME_SIZE_MS = 60
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
pcm := make([]int16, FRAME_SIZE)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
if err != nil || enc == nil {
t.Fatalf("Error creating new encoder: %v", err)
}
addSine(pcm, SAMPLE_RATE, G4)
data := make([]byte, 1000)
n, err := enc.Encode(pcm, data)
if err != nil {
t.Fatalf("Couldn't encode data: %v", err)
}
data = data[:n]
dec, err := NewDecoder(SAMPLE_RATE, 1)
if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err)
}
n, err = dec.Decode(data, pcm)
if err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
samples, err := dec.LastPacketDuration()
if err != nil {
t.Fatalf("Couldn't get last packet duration: %v", err)
}
if samples != n {
t.Fatalf("Wrong duration length. Expected %d. Got %d", n, samples)
}
}

View file

@ -6,91 +6,60 @@ package opus
import ( import (
"fmt" "fmt"
"io"
"runtime/cgo"
"unsafe" "unsafe"
) )
/* /*
#cgo pkg-config: opus #cgo pkg-config: opus libopusenc
#include <opus.h> #include <opus.h>
#include <opusenc.h>
int int
bridge_encoder_set_dtx(OpusEncoder *st, opus_int32 use_dtx) bridge_encoder_set_dtx(OggOpusEnc *st, opus_int32 use_dtx);
{
return opus_encoder_ctl(st, OPUS_SET_DTX(use_dtx));
}
int int
bridge_encoder_get_dtx(OpusEncoder *st, opus_int32 *dtx) bridge_encoder_get_dtx(OggOpusEnc *st, opus_int32 *dtx);
{
return opus_encoder_ctl(st, OPUS_GET_DTX(dtx));
}
int int
bridge_encoder_get_sample_rate(OpusEncoder *st, opus_int32 *sample_rate) bridge_encoder_get_sample_rate(OggOpusEnc *st, opus_int32 *sample_rate);
{
return opus_encoder_ctl(st, OPUS_GET_SAMPLE_RATE(sample_rate));
}
int int
bridge_encoder_set_bitrate(OpusEncoder *st, opus_int32 bitrate) bridge_encoder_set_bitrate(OggOpusEnc *st, opus_int32 bitrate);
{
return opus_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
}
int int
bridge_encoder_get_bitrate(OpusEncoder *st, opus_int32 *bitrate) bridge_encoder_get_bitrate(OggOpusEnc *st, opus_int32 *bitrate);
{
return opus_encoder_ctl(st, OPUS_GET_BITRATE(bitrate));
}
int int
bridge_encoder_set_complexity(OpusEncoder *st, opus_int32 complexity) bridge_encoder_set_complexity(OggOpusEnc *st, opus_int32 complexity);
{
return opus_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
}
int int
bridge_encoder_get_complexity(OpusEncoder *st, opus_int32 *complexity) bridge_encoder_get_complexity(OggOpusEnc *st, opus_int32 *complexity);
{
return opus_encoder_ctl(st, OPUS_GET_COMPLEXITY(complexity));
}
int int
bridge_encoder_set_max_bandwidth(OpusEncoder *st, opus_int32 max_bw) bridge_encoder_set_max_bandwidth(OggOpusEnc *st, opus_int32 max_bw);
{
return opus_encoder_ctl(st, OPUS_SET_MAX_BANDWIDTH(max_bw));
}
int int
bridge_encoder_get_max_bandwidth(OpusEncoder *st, opus_int32 *max_bw) bridge_encoder_get_max_bandwidth(OggOpusEnc *st, opus_int32 *max_bw);
{
return opus_encoder_ctl(st, OPUS_GET_MAX_BANDWIDTH(max_bw));
}
int int
bridge_encoder_set_inband_fec(OpusEncoder *st, opus_int32 fec) bridge_encoder_set_inband_fec(OggOpusEnc *st, opus_int32 fec);
{
return opus_encoder_ctl(st, OPUS_SET_INBAND_FEC(fec));
}
int int
bridge_encoder_get_inband_fec(OpusEncoder *st, opus_int32 *fec) bridge_encoder_get_inband_fec(OggOpusEnc *st, opus_int32 *fec);
{
return opus_encoder_ctl(st, OPUS_GET_INBAND_FEC(fec));
}
int int
bridge_encoder_set_packet_loss_perc(OpusEncoder *st, opus_int32 loss_perc) bridge_encoder_set_packet_loss_perc(OggOpusEnc *st, opus_int32 loss_perc);
{
return opus_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(loss_perc));
}
int int
bridge_encoder_get_packet_loss_perc(OpusEncoder *st, opus_int32 *loss_perc) bridge_encoder_get_packet_loss_perc(OggOpusEnc *st, opus_int32 *loss_perc);
{
return opus_encoder_ctl(st, OPUS_GET_PACKET_LOSS_PERC(loss_perc));
} OpusEncCallbacks * my_write_callbacks();
OggOpusComments* my_empty_comment();
*/ */
import "C" import "C"
@ -114,18 +83,32 @@ var errEncUninitialized = fmt.Errorf("opus encoder uninitialized")
// Encoder contains the state of an Opus encoder for libopus. // Encoder contains the state of an Opus encoder for libopus.
type Encoder struct { type Encoder struct {
p *C.struct_OpusEncoder p *C.struct_OggOpusEnc
channels int channels int
// Memory for the encoder struct allocated on the Go heap to allow Go GC to handle cgo.Handle
// manage it (and obviate need to free()) writer io.Writer
mem []byte
} }
// NewEncoder allocates a new Opus encoder and initializes it with the //export go_writecallback
// appropriate parameters. All related memory is managed by the Go GC. func go_writecallback(p unsafe.Pointer, cbuf *C.uchar, length C.int) C.int {
func NewEncoder(sample_rate int, channels int, application Application) (*Encoder, error) { enc := ((*cgo.Handle)(p)).Value().(*Encoder)
if enc == nil {
// This is bad
return 1
}
_, err := enc.writer.Write(unsafe.Slice((*byte)(cbuf), int(length)))
if err != nil {
return 1
}
return 0
}
// NewEncoder allocates a new Opus encoder and initializes it with the appropriate parameters.
func NewEncoder(sampleRate int, channels int, application Application, writer io.Writer) (*Encoder, error) {
var enc Encoder var enc Encoder
err := enc.Init(sample_rate, channels, application) err := enc.Init(sampleRate, channels, application, writer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -135,84 +118,94 @@ func NewEncoder(sample_rate int, channels int, application Application) (*Encode
// Init initializes a pre-allocated opus encoder. Unless the encoder has been // Init initializes a pre-allocated opus encoder. Unless the encoder has been
// created using NewEncoder, this method must be called exactly once in the // created using NewEncoder, this method must be called exactly once in the
// life-time of this object, before calling any other methods. // life-time of this object, before calling any other methods.
func (enc *Encoder) Init(sample_rate int, channels int, application Application) error { func (enc *Encoder) Init(sampleRate int, channels int, application Application, writer io.Writer) error {
if enc.p != nil { if enc.p != nil {
return fmt.Errorf("opus encoder already initialized") return fmt.Errorf("opus encoder already initialized")
} }
if channels != 1 && channels != 2 { if channels != 1 && channels != 2 {
return fmt.Errorf("Number of channels must be 1 or 2: %d", channels) 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.channels = channels
enc.mem = make([]byte, size) enc.writer = writer
enc.p = (*C.OpusEncoder)(unsafe.Pointer(&enc.mem[0])) enc.handle = cgo.NewHandle(enc)
errno := int(C.opus_encoder_init(
enc.p, var errno C.int
C.opus_int32(sample_rate), if channels > 2 {
C.int(channels), enc.p = C.ope_encoder_create_callbacks(C.my_write_callbacks(), unsafe.Pointer(&enc.handle), C.my_empty_comment(), C.opus_int32(sampleRate), C.int(channels), C.int(1), &errno)
C.int(application))) } else {
enc.p = C.ope_encoder_create_callbacks(C.my_write_callbacks(), unsafe.Pointer(&enc.handle), C.my_empty_comment(), C.opus_int32(sampleRate), C.int(channels), C.int(0), &errno)
}
if errno != 0 { if errno != 0 {
return Error(int(errno)) return Error(int(errno))
} }
return nil return nil
} }
// Encode raw PCM data and store the result in the supplied buffer. On success, // Encode raw PCM data
// returns the number of bytes used up by the encoded data. func (enc *Encoder) Encode(pcm []int16) error {
func (enc *Encoder) Encode(pcm []int16, data []byte) (int, error) {
if enc.p == nil { if enc.p == nil {
return 0, errEncUninitialized return errEncUninitialized
} }
if len(pcm) == 0 { if len(pcm) == 0 {
return 0, fmt.Errorf("opus: no data supplied") return fmt.Errorf("opus: no data supplied")
} }
if len(data) == 0 {
return 0, fmt.Errorf("opus: no target buffer")
}
// libopus talks about samples as 1 sample containing multiple channels. So
// e.g. 20 samples of 2-channel data is actually 40 raw data points.
if len(pcm)%enc.channels != 0 { if len(pcm)%enc.channels != 0 {
return 0, fmt.Errorf("opus: input buffer length must be multiple of channels") return fmt.Errorf("opus: input buffer length must be multiple of channels")
} }
samples := len(pcm) / enc.channels samples := len(pcm) / enc.channels
n := int(C.opus_encode(
enc.p, n := int(C.ope_encoder_write(enc.p, (*C.opus_int16)(&pcm[0]), C.int(samples)))
(*C.opus_int16)(&pcm[0]), if n != 0 {
C.int(samples), return Error(n)
(*C.uchar)(&data[0]),
C.opus_int32(cap(data))))
if n < 0 {
return 0, Error(n)
} }
return n, nil return nil
} }
// Encode raw PCM data and store the result in the supplied buffer. On success, // FlushHeaders Write out the header now rather than wait for audio to begin.
// returns the number of bytes used up by the encoded data. func (enc *Encoder) FlushHeaders() error {
func (enc *Encoder) EncodeFloat32(pcm []float32, data []byte) (int, error) { res := C.ope_encoder_flush_header(enc.p)
if res != C.OPUS_OK {
return Error(res)
}
return nil
}
// Close finishes stream, then frees allocated resources
func (enc *Encoder) Close() error {
if enc.p != nil {
res := C.ope_encoder_drain(enc.p)
if res != C.OPUS_OK {
return Error(res)
}
C.ope_encoder_destroy(enc.p)
enc.p = nil
enc.handle.Delete()
}
return nil
}
// EncodeFloat32 raw PCM data
func (enc *Encoder) EncodeFloat32(pcm []float32) error {
if enc.p == nil { if enc.p == nil {
return 0, errEncUninitialized return errEncUninitialized
} }
if len(pcm) == 0 { if len(pcm) == 0 {
return 0, fmt.Errorf("opus: no data supplied") return fmt.Errorf("opus: no data supplied")
}
if len(data) == 0 {
return 0, fmt.Errorf("opus: no target buffer")
} }
if len(pcm)%enc.channels != 0 { if len(pcm)%enc.channels != 0 {
return 0, fmt.Errorf("opus: input buffer length must be multiple of channels") return fmt.Errorf("opus: input buffer length must be multiple of channels")
} }
samples := len(pcm) / enc.channels samples := len(pcm) / enc.channels
n := int(C.opus_encode_float(
enc.p, n := int(C.ope_encoder_write_float(enc.p, (*C.float)(&pcm[0]), C.int(samples)))
(*C.float)(&pcm[0]), if n != 0 {
C.int(samples), return Error(n)
(*C.uchar)(&data[0]),
C.opus_int32(cap(data))))
if n < 0 {
return 0, Error(n)
} }
return n, nil return nil
} }
// SetDTX configures the encoder's use of discontinuous transmission (DTX). // SetDTX configures the encoder's use of discontinuous transmission (DTX).

View file

@ -4,14 +4,17 @@
package opus package opus
import "testing" import (
"bytes"
"testing"
)
func TestEncoderNew(t *testing.T) { func TestEncoderNew(t *testing.T) {
enc, err := NewEncoder(48000, 1, AppVoIP) enc, err := NewEncoder(48000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
enc, err = NewEncoder(12345, 1, AppVoIP) enc, err = NewEncoder(12345, 1, AppVoIP, new(bytes.Buffer))
if err == nil || enc != nil { if err == nil || enc != nil {
t.Errorf("Expected error for illegal samplerate 12345") t.Errorf("Expected error for illegal samplerate 12345")
} }
@ -19,18 +22,18 @@ func TestEncoderNew(t *testing.T) {
func TestEncoderUnitialized(t *testing.T) { func TestEncoderUnitialized(t *testing.T) {
var enc Encoder var enc Encoder
_, err := enc.Encode(nil, nil) err := enc.Encode(nil)
if err != errEncUninitialized { if err != errEncUninitialized {
t.Errorf("Expected \"unitialized encoder\" error: %v", err) t.Errorf("Expected \"unitialized encoder\" error: %v", err)
} }
_, err = enc.EncodeFloat32(nil, nil) err = enc.EncodeFloat32(nil)
if err != errEncUninitialized { if err != errEncUninitialized {
t.Errorf("Expected \"unitialized encoder\" error: %v", err) t.Errorf("Expected \"unitialized encoder\" error: %v", err)
} }
} }
func TestEncoderDTX(t *testing.T) { func TestEncoderDTX(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -51,9 +54,9 @@ func TestEncoderDTX(t *testing.T) {
} }
func TestEncoderSampleRate(t *testing.T) { func TestEncoderSampleRate(t *testing.T) {
sample_rates := []int{8000, 12000, 16000, 24000, 48000} sampleRates := []int{8000, 12000, 16000, 24000, 48000}
for _, f := range sample_rates { for _, f := range sampleRates {
enc, err := NewEncoder(f, 1, AppVoIP) enc, err := NewEncoder(f, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Fatalf("Error creating new encoder with sample_rate %d Hz: %v", f, err) t.Fatalf("Error creating new encoder with sample_rate %d Hz: %v", f, err)
} }
@ -68,7 +71,7 @@ func TestEncoderSampleRate(t *testing.T) {
} }
func TestEncoder_SetGetBitrate(t *testing.T) { func TestEncoder_SetGetBitrate(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -78,6 +81,7 @@ func TestEncoder_SetGetBitrate(t *testing.T) {
if err != nil { if err != nil {
t.Error("Error set bitrate:", err) t.Error("Error set bitrate:", err)
} }
enc.FlushHeaders()
br, err := enc.Bitrate() br, err := enc.Bitrate()
if err != nil { if err != nil {
t.Error("Error getting bitrate", err) t.Error("Error getting bitrate", err)
@ -89,7 +93,7 @@ func TestEncoder_SetGetBitrate(t *testing.T) {
} }
func TestEncoder_SetBitrateToAuto(t *testing.T) { func TestEncoder_SetBitrateToAuto(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -125,7 +129,7 @@ func TestEncoder_SetBitrateToAuto(t *testing.T) {
} }
func TestEncoder_SetBitrateToMax(t *testing.T) { func TestEncoder_SetBitrateToMax(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -161,7 +165,7 @@ func TestEncoder_SetBitrateToMax(t *testing.T) {
} }
func TestEncoder_SetGetInvalidBitrate(t *testing.T) { func TestEncoder_SetGetInvalidBitrate(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -183,7 +187,7 @@ func TestEncoder_SetGetInvalidBitrate(t *testing.T) {
} }
func TestEncoder_SetGetComplexity(t *testing.T) { func TestEncoder_SetGetComplexity(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -205,7 +209,7 @@ func TestEncoder_SetGetComplexity(t *testing.T) {
} }
func TestEncoder_SetGetInvalidComplexity(t *testing.T) { func TestEncoder_SetGetInvalidComplexity(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -233,7 +237,7 @@ func TestEncoder_SetGetInvalidComplexity(t *testing.T) {
} }
func TestEncoder_SetGetMaxBandwidth(t *testing.T) { func TestEncoder_SetGetMaxBandwidth(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -261,7 +265,7 @@ func TestEncoder_SetGetMaxBandwidth(t *testing.T) {
} }
func TestEncoder_SetGetInBandFEC(t *testing.T) { func TestEncoder_SetGetInBandFEC(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -292,7 +296,7 @@ func TestEncoder_SetGetInBandFEC(t *testing.T) {
} }
func TestEncoder_SetGetPacketLossPerc(t *testing.T) { func TestEncoder_SetGetPacketLossPerc(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }
@ -314,7 +318,7 @@ func TestEncoder_SetGetPacketLossPerc(t *testing.T) {
} }
func TestEncoder_SetGetInvalidPacketLossPerc(t *testing.T) { func TestEncoder_SetGetInvalidPacketLossPerc(t *testing.T) {
enc, err := NewEncoder(8000, 1, AppVoIP) enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
if err != nil || enc == nil { if err != nil || enc == nil {
t.Errorf("Error creating new encoder: %v", err) t.Errorf("Error creating new encoder: %v", err)
} }

View file

@ -5,6 +5,8 @@
package opus package opus
import ( import (
"bytes"
"io"
"strings" "strings"
"testing" "testing"
) )
@ -32,27 +34,38 @@ func TestCodec(t *testing.T) {
const FRAME_SIZE_MS = 60 const FRAME_SIZE_MS = 60
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000 const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
pcm := make([]int16, FRAME_SIZE) pcm := make([]int16, FRAME_SIZE)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
data := new(bytes.Buffer)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP, data)
if err != nil || enc == nil { if err != nil || enc == nil {
t.Fatalf("Error creating new encoder: %v", err) t.Fatalf("Error creating new encoder: %v", err)
} }
addSine(pcm, SAMPLE_RATE, G4) addSine(pcm, SAMPLE_RATE, G4)
data := make([]byte, 1000)
n, err := enc.Encode(pcm, data) err = enc.Encode(pcm)
if err != nil { if err != nil {
t.Fatalf("Couldn't encode data: %v", err) t.Fatalf("Couldn't encode data: %v", err)
} }
data = data[:n] enc.Close()
dec, err := NewDecoder(SAMPLE_RATE, 1)
dec, err := NewStream(bytes.NewReader(data.Bytes()))
if err != nil || dec == nil { if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err) t.Fatalf("Error creating new decoder: %v", err)
} }
n, err = dec.Decode(data, pcm)
if err != nil { toRead := len(pcm)
t.Fatalf("Couldn't decode data: %v", err) for toRead != 0 {
n, err := dec.Read(pcm)
if err == io.EOF {
break
} else if err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
toRead -= n
} }
if len(pcm) != n {
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n) if toRead != 0 {
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), len(pcm)-toRead)
} }
// Checking the output programmatically is seriously not easy. I checked it // Checking the output programmatically is seriously not easy. I checked it
// by hand and by ear, it looks fine. I'll just assume the API faithfully // by hand and by ear, it looks fine. I'll just assume the API faithfully
@ -66,23 +79,23 @@ func TestCodecFloat32(t *testing.T) {
const FRAME_SIZE_MS = 60 const FRAME_SIZE_MS = 60
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000 const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
pcm := make([]float32, FRAME_SIZE) pcm := make([]float32, FRAME_SIZE)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP) data := new(bytes.Buffer)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP, data)
if err != nil || enc == nil { if err != nil || enc == nil {
t.Fatalf("Error creating new encoder: %v", err) t.Fatalf("Error creating new encoder: %v", err)
} }
addSineFloat32(pcm, SAMPLE_RATE, G4) addSineFloat32(pcm, SAMPLE_RATE, G4)
data := make([]byte, 1000) err = enc.EncodeFloat32(pcm)
n, err := enc.EncodeFloat32(pcm, data)
if err != nil { if err != nil {
t.Fatalf("Couldn't encode data: %v", err) t.Fatalf("Couldn't encode data: %v", err)
} }
dec, err := NewDecoder(SAMPLE_RATE, 1) dec, err := NewStream(bytes.NewReader(data.Bytes()))
if err != nil || dec == nil { if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err) t.Fatalf("Error creating new decoder: %v", err)
} }
// TODO: Uh-oh.. it looks like I forgot to put a data = data[:n] here, yet // TODO: Uh-oh.. it looks like I forgot to put a data = data[:n] here, yet
// the test is not failing. Why? // the test is not failing. Why?
n, err = dec.DecodeFloat32(data, pcm) n, err := dec.ReadFloat32(pcm)
if err != nil { if err != nil {
t.Fatalf("Couldn't decode data: %v", err) t.Fatalf("Couldn't decode data: %v", err)
} }
@ -90,549 +103,3 @@ func TestCodecFloat32(t *testing.T) {
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n) t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n)
} }
} }
func TestCodecFEC(t *testing.T) {
// Create bogus input sound
const G4 = 391.995
const SAMPLE_RATE = 48000
const FRAME_SIZE_MS = 10
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
const NUMBER_OF_FRAMES = 6
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(true)
dec, err := NewDecoder(SAMPLE_RATE, 1)
if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err)
}
mono := make([]int16, FRAME_SIZE*NUMBER_OF_FRAMES)
addSine(mono, SAMPLE_RATE, G4)
encodedData := make([][]byte, NUMBER_OF_FRAMES)
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
data := make([]byte, 1000)
n, err := enc.Encode(mono[i:i+FRAME_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, NUMBER_OF_FRAMES*FRAME_SIZE)
n, err := dec.Decode(encodedData[i], pcmBuffer)
if err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
pcmBuffer = pcmBuffer[:n]
if n != FRAME_SIZE {
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, 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))
}
// Commented out for the same reason as in TestStereo
/*
fmt.Printf("in,out\n")
for i := range mono {
fmt.Printf("%d,%d\n", mono[i], pcm[i])
}
*/
}
func TestCodecFECFloat32(t *testing.T) {
// Create bogus input sound
const G4 = 391.995
const SAMPLE_RATE = 48000
const FRAME_SIZE_MS = 10
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
const NUMBER_OF_FRAMES = 6
pcm := make([]float32, 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(true)
dec, err := NewDecoder(SAMPLE_RATE, 1)
if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err)
}
mono := make([]float32, FRAME_SIZE*NUMBER_OF_FRAMES)
addSineFloat32(mono, SAMPLE_RATE, G4)
encodedData := make([][]byte, NUMBER_OF_FRAMES)
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
data := make([]byte, 1000)
n, err := enc.EncodeFloat32(mono[i:i+FRAME_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([]float32, samples)
if err = dec.DecodeFECFloat32(encodedData[i], pcmBuffer); err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
pcm = append(pcm, pcmBuffer...)
lost = false
}
pcmBuffer := make([]float32, NUMBER_OF_FRAMES*FRAME_SIZE)
n, err := dec.DecodeFloat32(encodedData[i], pcmBuffer)
if err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
pcmBuffer = pcmBuffer[:n]
if n != FRAME_SIZE {
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, 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))
}
// Commented out for the same reason as in TestStereo
/*
fmt.Printf("in,out\n")
for i := 0; i < len(mono); i++ {
fmt.Printf("%f,%f\n", mono[i], pcm[i])
}
*/
}
func TestCodecPLC(t *testing.T) {
// Create bogus input sound
const G4 = 391.995
const SAMPLE_RATE = 48000
const FRAME_SIZE_MS = 10
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
const NUMBER_OF_FRAMES = 6
pcm := make([]int16, 0)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
if err != nil || enc == nil {
t.Fatalf("Error creating new encoder: %v", err)
}
dec, err := NewDecoder(SAMPLE_RATE, 1)
if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err)
}
mono := make([]int16, FRAME_SIZE*NUMBER_OF_FRAMES)
addSine(mono, SAMPLE_RATE, G4)
encodedData := make([][]byte, NUMBER_OF_FRAMES)
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
data := make([]byte, 1000)
n, err := enc.Encode(mono[i:i+FRAME_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.DecodePLC(pcmBuffer); err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
nonZero := 0
for n := range pcmBuffer {
if pcmBuffer[n] != 0 {
nonZero++
}
}
if nonZero == 0 {
t.Fatalf("DecodePLC produced only zero samples")
}
pcm = append(pcm, pcmBuffer...)
lost = false
}
pcmBuffer := make([]int16, NUMBER_OF_FRAMES*FRAME_SIZE)
n, err := dec.Decode(encodedData[i], pcmBuffer)
if err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
pcmBuffer = pcmBuffer[:n]
if n != FRAME_SIZE {
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, 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))
}
// Commented out for the same reason as in TestStereo
/*
fmt.Printf("in,out\n")
for i := range mono {
fmt.Printf("%d,%d\n", mono[i], pcm[i])
}
*/
}
func TestCodecPLCFloat32(t *testing.T) {
// Create bogus input sound
const G4 = 391.995
const SAMPLE_RATE = 48000
const FRAME_SIZE_MS = 10
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
const NUMBER_OF_FRAMES = 6
pcm := make([]float32, 0)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
if err != nil || enc == nil {
t.Fatalf("Error creating new encoder: %v", err)
}
dec, err := NewDecoder(SAMPLE_RATE, 1)
if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err)
}
mono := make([]float32, FRAME_SIZE*NUMBER_OF_FRAMES)
addSineFloat32(mono, SAMPLE_RATE, G4)
encodedData := make([][]byte, NUMBER_OF_FRAMES)
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
data := make([]byte, 1000)
n, err := enc.EncodeFloat32(mono[i:i+FRAME_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([]float32, samples)
if err = dec.DecodePLCFloat32(pcmBuffer); err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
nonZero := 0
for n := range pcmBuffer {
if pcmBuffer[n] != 0.0 {
nonZero++
}
}
if nonZero == 0 {
t.Fatalf("DecodePLC produced only zero samples")
}
pcm = append(pcm, pcmBuffer...)
lost = false
}
pcmBuffer := make([]float32, NUMBER_OF_FRAMES*FRAME_SIZE)
n, err := dec.DecodeFloat32(encodedData[i], pcmBuffer)
if err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
pcmBuffer = pcmBuffer[:n]
if n != FRAME_SIZE {
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, 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))
}
// Commented out for the same reason as in TestStereo
/*
fmt.Printf("in,out\n")
for i := 0; i < len(mono); i++ {
fmt.Printf("%f,%f\n", mono[i], pcm[i])
}
*/
}
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, AppVoIP)
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.Fatalf("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])
}
*/
}
func TestBufferSize(t *testing.T) {
const G4 = 391.995
const SAMPLE_RATE = 48000
const FRAME_SIZE_MS = 60
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
const GUARD_SIZE = 100
checkGuardInt16 := func(t *testing.T, s []int16) {
for n := range s {
if s[n] != 0 {
t.Fatal("Memory corruption detected")
}
}
}
checkGuardFloat32 := func(t *testing.T, s []float32) {
for n := range s {
if s[n] != 0 {
t.Fatal("Memory corruption detected")
}
}
}
checkResult := func(t *testing.T, n int, err error, expectOK bool) {
if expectOK {
if err != nil {
t.Fatalf("Couldn't decode data: %v", err)
}
if n != FRAME_SIZE {
t.Fatalf("Length mismatch: %d samples in, %d out", FRAME_SIZE, n)
}
} else {
if err == nil {
t.Fatalf("Expected decode failure, but it succeeded")
}
}
}
encodeFrame := func(t *testing.T) []byte {
pcm := make([]int16, FRAME_SIZE)
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
if err != nil || enc == nil {
t.Fatalf("Error creating new encoder: %v", err)
}
addSine(pcm, SAMPLE_RATE, G4)
data := make([]byte, 1000)
n, err := enc.Encode(pcm, data)
if err != nil {
t.Fatalf("Couldn't encode data: %v", err)
}
return data[:n]
}
createDecoder := func(t *testing.T) *Decoder {
dec, err := NewDecoder(SAMPLE_RATE, 1)
if err != nil || dec == nil {
t.Fatalf("Error creating new decoder: %v", err)
}
return dec
}
decodeInt16 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
dec := createDecoder(t)
decodedMem := make([]int16, decodeSize+GUARD_SIZE*2)
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
n, err := dec.Decode(data, decodedRef)
checkGuardInt16(t, decodedMem[:GUARD_SIZE])
checkGuardInt16(t, decodedMem[decodeSize+GUARD_SIZE:])
checkResult(t, n, err, expectOK)
}
decodeFloat32 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
dec := createDecoder(t)
decodedMem := make([]float32, decodeSize+GUARD_SIZE*2)
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
n, err := dec.DecodeFloat32(data, decodedRef)
checkGuardFloat32(t, decodedMem[:GUARD_SIZE])
checkGuardFloat32(t, decodedMem[decodeSize+GUARD_SIZE:])
checkResult(t, n, err, expectOK)
}
decodeFecInt16 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
dec := createDecoder(t)
decodedMem := make([]int16, decodeSize+GUARD_SIZE*2)
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
err := dec.DecodeFEC(data, decodedRef)
checkGuardInt16(t, decodedMem[:GUARD_SIZE])
checkGuardInt16(t, decodedMem[decodeSize+GUARD_SIZE:])
checkResult(t, FRAME_SIZE, err, expectOK)
}
decodeFecFloat32 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
dec := createDecoder(t)
decodedMem := make([]float32, decodeSize+GUARD_SIZE*2)
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
err := dec.DecodeFECFloat32(data, decodedRef)
checkGuardFloat32(t, decodedMem[:GUARD_SIZE])
checkGuardFloat32(t, decodedMem[decodeSize+GUARD_SIZE:])
checkResult(t, FRAME_SIZE, err, expectOK)
}
t.Run("smaller-buffer-int16", func(t *testing.T) {
decodeInt16(t, encodeFrame(t), FRAME_SIZE-1, false)
})
t.Run("smaller-buffer-float32", func(t *testing.T) {
decodeFloat32(t, encodeFrame(t), FRAME_SIZE-1, false)
})
t.Run("smaller-buffer-int16-fec", func(t *testing.T) {
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE-1, false)
})
t.Run("smaller-buffer-float32-fec", func(t *testing.T) {
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE-1, false)
})
t.Run("exact-buffer-int16", func(t *testing.T) {
decodeInt16(t, encodeFrame(t), FRAME_SIZE, true)
})
t.Run("exact-buffer-float32", func(t *testing.T) {
decodeFloat32(t, encodeFrame(t), FRAME_SIZE, true)
})
t.Run("exact-buffer-int16-fec", func(t *testing.T) {
decodeFecInt16(t, encodeFrame(t), FRAME_SIZE, true)
})
t.Run("exact-buffer-float32-fec", func(t *testing.T) {
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE, true)
})
t.Run("larger-buffer-int16", func(t *testing.T) {
decodeInt16(t, encodeFrame(t), FRAME_SIZE+1, true)
})
t.Run("larger-buffer-float32", func(t *testing.T) {
decodeFloat32(t, encodeFrame(t), FRAME_SIZE+1, true)
})
t.Run("larger-buffer-int16-fec", func(t *testing.T) {
decodeFecInt16(t, encodeFrame(t), FRAME_SIZE+1, false)
})
t.Run("larger-buffer-float32-fec", func(t *testing.T) {
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE+1, false)
})
}