//go:build android package teamspeak /* #cgo CFLAGS: -I${SRCDIR}/../../android/app/src/main/cpp/third_party/opus/include #cgo android,arm64 LDFLAGS: -L${SRCDIR}/.opus/lib/arm64-v8a -l:libopus.a -lm #cgo android,arm LDFLAGS: -L${SRCDIR}/.opus/lib/armeabi-v7a -l:libopus.a -lm #cgo android,386 LDFLAGS: -L${SRCDIR}/.opus/lib/x86 -l:libopus.a -lm #cgo android,amd64 LDFLAGS: -L${SRCDIR}/.opus/lib/x86_64 -l:libopus.a -lm #include "opus.h" #include */ import "C" import ( "errors" "fmt" "runtime" "sync" "unsafe" ) var errOpusDecoderReleased = errors.New("opus decoder has been released") type opusVoiceDecoderFactory struct{} type opusVoiceDecoder struct { mu sync.Mutex decoder *C.OpusDecoder channels int } func newPlatformVoiceDecoderFactory() voiceDecoderFactory { return opusVoiceDecoderFactory{} } func (opusVoiceDecoderFactory) NewvoiceDecoder(_ int, codec int) (voiceDecoder, error) { if codec != 4 && codec != 5 { return nil, fmt.Errorf("unsupported TeamSpeak voice codec %d", codec) } channels := voiceCodecChannels(codec) var opusErr C.int decoder := C.opus_decoder_create(C.opus_int32(receiveSampleRate), C.int(channels), &opusErr) if decoder == nil || opusErr != C.OPUS_OK { return nil, fmt.Errorf("opus_decoder_create: %d", int(opusErr)) } return &opusVoiceDecoder{decoder: decoder, channels: channels}, nil } func (d *opusVoiceDecoder) Decode(packet []byte) ([]int16, error) { if len(packet) == 0 { return nil, errors.New("empty Opus packet") } d.mu.Lock() defer d.mu.Unlock() if d.decoder == nil { return nil, errOpusDecoderReleased } pcm := make([]int16, receiveMaxDecodeSamplesPerChannel*d.channels) decoded := C.opus_decode( d.decoder, (*C.uchar)(unsafe.Pointer(&packet[0])), C.opus_int32(len(packet)), (*C.opus_int16)(unsafe.Pointer(&pcm[0])), C.int(receiveMaxDecodeSamplesPerChannel), 0, ) runtime.KeepAlive(packet) return decodedPCM(pcm, decoded, d.channels) } func (d *opusVoiceDecoder) DecodeLost() ([]int16, error) { d.mu.Lock() defer d.mu.Unlock() if d.decoder == nil { return nil, errOpusDecoderReleased } pcm := make([]int16, receiveMaxDecodeSamplesPerChannel*d.channels) decoded := C.opus_decode( d.decoder, nil, 0, (*C.opus_int16)(unsafe.Pointer(&pcm[0])), C.int(receiveMaxDecodeSamplesPerChannel), 0, ) return decodedPCM(pcm, decoded, d.channels) } func (d *opusVoiceDecoder) Release() { d.mu.Lock() decoder := d.decoder d.decoder = nil d.mu.Unlock() if decoder != nil { C.opus_decoder_destroy(decoder) } } func decodedPCM(pcm []int16, decoded C.int, channels int) ([]int16, error) { if decoded < 0 { return nil, fmt.Errorf("opus_decode: %d", int(decoded)) } perChannel := int(decoded) decodedSamples := perChannel * channels if perChannel > receiveMaxDecodeSamplesPerChannel || decodedSamples > len(pcm) { return nil, fmt.Errorf("opus_decode returned %d samples/channel, buffer has %d samples for %d channels", perChannel, len(pcm), channels) } return normalizeDecodedPCM(pcm[:decodedSamples], channels), nil }