diff --git a/encoder.go b/encoder.go index a9ced73..04a0d54 100644 --- a/encoder.go +++ b/encoder.go @@ -34,6 +34,56 @@ bridge_encoder_get_sample_rate(OpusEncoder *st) opus_encoder_ctl(st, OPUS_GET_SAMPLE_RATE(&sample_rate)); return sample_rate; } + +opus_int32 +set_bitrate(OpusEncoder *st, opus_int32 bitrate) +{ + opus_int32 res; + res = opus_encoder_ctl(st, OPUS_SET_BITRATE(bitrate)); + return res; +} + +opus_int32 +get_bitrate(OpusEncoder *st, opus_int32 *bitrate) +{ + int res; + res = opus_encoder_ctl(st, OPUS_GET_BITRATE(bitrate)); + return res; +} + +opus_int32 +set_complexity(OpusEncoder *st, opus_int32 complexity) +{ + opus_int32 res; + res = opus_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity)); + return res; +} + +opus_int32 +get_complexity(OpusEncoder *st, opus_int32 *complexity) +{ + int res; + res = opus_encoder_ctl(st, OPUS_GET_COMPLEXITY(complexity)); + return res; +} + +opus_int32 +set_max_bandwidth(OpusEncoder *st, opus_int32 max_bw) +{ + opus_int32 res; + res = opus_encoder_ctl(st, OPUS_SET_MAX_BANDWIDTH(max_bw)); + return res; +} + +opus_int32 +get_max_bandwidth(OpusEncoder *st, opus_int32 *max_bw) +{ + int res; + res = opus_encoder_ctl(st, OPUS_GET_MAX_BANDWIDTH(max_bw)); + return res; +} + + */ import "C" @@ -50,7 +100,7 @@ type Encoder struct { // NewEncoder allocates a new Opus encoder and initializes it with the // appropriate parameters. All related memory is managed by the Go GC. -func NewEncoder(sample_rate int, channels int, application Application) (*Encoder, error) { +func NewEncoder(sample_rate int, channels int, application int) (*Encoder, error) { var enc Encoder err := enc.Init(sample_rate, channels, application) if err != nil { @@ -62,7 +112,7 @@ func NewEncoder(sample_rate int, channels int, application Application) (*Encode // Init initializes a pre-allocated opus encoder. Unless the encoder has been // created using NewEncoder, this method must be called exactly once in the // 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(sample_rate int, channels int, application int) error { if enc.p != nil { return fmt.Errorf("opus encoder already initialized") } @@ -162,3 +212,61 @@ func (enc *Encoder) DTX() bool { func (enc *Encoder) SampleRate() int { return int(C.bridge_encoder_get_sample_rate(enc.p)) } + +// SetBitrate sets the bitrate of the Encoder +func (enc *Encoder) SetBitrate(bitrate int) error { + res := int(C.set_bitrate(enc.p, C.opus_int32(bitrate))) + if res < 0 { + return opusError(res) + } + return nil +} + +// Bitrate returns the bitrate of the Encoder +func (enc *Encoder) Bitrate() (int, error) { + var bitrate C.opus_int32 + res := int(C.get_bitrate(enc.p, &bitrate)) + if res < 0 { + return 0, opusError(res) + } + return int(bitrate), nil +} + +// SetComplexity sets the encoder's computational complexity +func (enc *Encoder) SetComplexity(complexity int) error { + res := int(C.set_complexity(enc.p, C.opus_int32(complexity))) + if res < 0 { + return opusError(res) + } + return nil +} + +// Complexity returns the bitrate of the Encoder +func (enc *Encoder) Complexity() (int, error) { + var complexity C.opus_int32 + res := int(C.get_complexity(enc.p, &complexity)) + if res < 0 { + return 0, opusError(res) + } + return int(complexity), nil +} + +// SetMaxBandwidth configures the maximum bandpass that the encoder will select +// automatically +func (enc *Encoder) SetMaxBandwidth(maxBw int) error { + res := int(C.set_max_bandwidth(enc.p, C.opus_int32(maxBw))) + if res < 0 { + return opusError(res) + } + return nil +} + +// MaxBandwidth gets the encoder's configured maximum allowed bandpass. +func (enc *Encoder) MaxBandwidth() (int, error) { + var maxBw C.opus_int32 + res := int(C.get_max_bandwidth(enc.p, &maxBw)) + if res < 0 { + return 0, opusError(res) + } + return int(maxBw), nil +} diff --git a/encoder_test.go b/encoder_test.go index 527a5e3..785001e 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -1,10 +1,8 @@ -// Copyright © 2015, 2016 Authors (see AUTHORS file) -// -// License for use of this code is detailed in the LICENSE file - package opus -import "testing" +import ( + "testing" +) func TestEncoderNew(t *testing.T) { enc, err := NewEncoder(48000, 1, APPLICATION_VOIP) @@ -57,3 +55,118 @@ func TestEncoderSampleRate(t *testing.T) { } } } + +func TestEncoder_SetGetBitrate(t *testing.T) { + enc, err := NewEncoder(8000, 1, APPLICATION_AUDIO) + if err != nil || enc == nil { + t.Errorf("Error creating new encoder: %v", err) + } + vals := []int{500, 100000, 300000} + for _, bitrate := range vals { + err := enc.SetBitrate(bitrate) + if err != nil { + t.Error("Error set bitrate:", err) + } + br, err := enc.Bitrate() + if err != nil { + t.Error("Error getting bitrate", err) + } + if br != bitrate { + t.Errorf("Unexpected bitrate. Got %d, but expected %d", br, bitrate) + } + } +} + +func TestEncoder_SetGetInvalidBitrate(t *testing.T) { + enc, err := NewEncoder(8000, 1, APPLICATION_AUDIO) + if err != nil || enc == nil { + t.Errorf("Error creating new encoder: %v", err) + } + invalidVals := []int{-20, 0} + for _, bitrate := range invalidVals { + err := enc.SetBitrate(bitrate) + if err == nil { + t.Errorf("Expected Error invalid bitrate: %d", bitrate) + } + br, err := enc.Bitrate() + if err != nil { + t.Error("Error getting bitrate", err) + } + // default bitrate is 32 kbit/s + if br != 32000 { + t.Errorf("Unexpected bitrate. Got %d, but expected %d", br, bitrate) + } + } +} + +func TestEncoder_SetGetComplexity(t *testing.T) { + enc, err := NewEncoder(8000, 1, APPLICATION_AUDIO) + if err != nil || enc == nil { + t.Errorf("Error creating new encoder: %v", err) + } + vals := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + for _, complexity := range vals { + err := enc.SetComplexity(complexity) + if err != nil { + t.Error("Error setting complexity value:", err) + } + cpx, err := enc.Complexity() + if err != nil { + t.Error("Error getting complexity value", err) + } + if cpx != complexity { + t.Errorf("Unexpected encoder complexity value. Got %d, but expected %d", + cpx, complexity) + } + } +} + +func TestEncoder_SetGetInvalidComplexity(t *testing.T) { + enc, err := NewEncoder(8000, 1, APPLICATION_AUDIO) + if err != nil || enc == nil { + t.Errorf("Error creating new encoder: %v", err) + } + invalidVals := []int{-20, 11, 1000} + for _, complexity := range invalidVals { + err := enc.SetComplexity(complexity) + if err == nil { + t.Errorf("Expected Error invalid complexity value: %d", complexity) + } + cpx, err := enc.Complexity() + if err != nil { + t.Error("Error getting complexity value", err) + } + // default complexity value is 9 + if cpx != 9 { + t.Errorf("Unexpected complexity value. Got %d, but expected %d", + cpx, complexity) + } + } +} + +func TestEncoder_SetGetMaxBandwidth(t *testing.T) { + enc, err := NewEncoder(8000, 1, APPLICATION_AUDIO) + if err != nil || enc == nil { + t.Errorf("Error creating new encoder: %v", err) + } + vals := []int{OPUS_BANDWIDTH_NARROWBAND, + OPUS_BANDWIDTH_MEDIUMBAND, + OPUS_BANDWIDTH_WIDEBAND, + OPUS_BANDWIDTH_SUPERWIDEBAND, + OPUS_BANDWIDTH_FULLBAND, + } + for _, maxBw := range vals { + err := enc.SetMaxBandwidth(maxBw) + if err != nil { + t.Error("Error setting max Bandwidth:", err) + } + maxBwRead, err := enc.MaxBandwidth() + if err != nil { + t.Error("Error getting max Bandwidth", err) + } + if maxBwRead != maxBw { + t.Errorf("Unexpected max Bandwidth value. Got %d, but expected %d", + maxBwRead, maxBw) + } + } +} diff --git a/opus.go b/opus.go index 7bba634..b4835df 100644 --- a/opus.go +++ b/opus.go @@ -16,17 +16,27 @@ const int CONST_APPLICATION_RESTRICTED_LOWDELAY = OPUS_APPLICATION_RESTRICTED_LO */ import "C" -type Application int - -// These variables should be constants, but for interoperability with CGO -// they're var. Don't change them, though! -var ( +const ( // Optimize encoding for VOIP - APPLICATION_VOIP = Application(C.CONST_APPLICATION_VOIP) + APPLICATION_VOIP = C.OPUS_APPLICATION_VOIP // Optimize encoding for non-voice signals like music - APPLICATION_AUDIO = Application(C.CONST_APPLICATION_AUDIO) + APPLICATION_AUDIO = C.OPUS_APPLICATION_AUDIO // Optimize encoding for low latency applications - APPLICATION_RESTRICTED_LOWDELAY = Application(C.CONST_APPLICATION_RESTRICTED_LOWDELAY) + APPLICATION_RESTRICTED_LOWDELAY = C.OPUS_APPLICATION_RESTRICTED_LOWDELAY + // Auto bitrate + OPUS_AUTO = C.OPUS_AUTO + // Maximum bitrate + OPUS_BITRATE_MAX = C.OPUS_BITRATE_MAX + //4 kHz passband + OPUS_BANDWIDTH_NARROWBAND = C.OPUS_BANDWIDTH_NARROWBAND + // 6 kHz passband + OPUS_BANDWIDTH_MEDIUMBAND = C.OPUS_BANDWIDTH_MEDIUMBAND + // 8 kHz passband + OPUS_BANDWIDTH_WIDEBAND = C.OPUS_BANDWIDTH_WIDEBAND + // 12 kHz passband + OPUS_BANDWIDTH_SUPERWIDEBAND = C.OPUS_BANDWIDTH_SUPERWIDEBAND + // 20 kHz passband + OPUS_BANDWIDTH_FULLBAND = C.OPUS_BANDWIDTH_FULLBAND ) const (