// Copyright © Go Opus Authors (see AUTHORS file) // // License for use of this code is detailed in the LICENSE file package opus import ( "errors" "fmt" "io" "runtime/cgo" "time" "unsafe" ) /* #cgo pkg-config: opus libopusenc #include #include int bridge_encoder_set_dtx(OggOpusEnc *st, opus_int32 use_dtx); int bridge_encoder_get_dtx(OggOpusEnc *st, opus_int32 *dtx); int bridge_encoder_get_sample_rate(OggOpusEnc *st, opus_int32 *sample_rate); int bridge_encoder_set_muxing_delay(OggOpusEnc *st, opus_int32 delay); int bridge_encoder_get_muxing_delay(OggOpusEnc *st, opus_int32 *delay); int bridge_encoder_set_bitrate(OggOpusEnc *st, opus_int32 bitrate); int bridge_encoder_get_bitrate(OggOpusEnc *st, opus_int32 *bitrate); int bridge_encoder_set_complexity(OggOpusEnc *st, opus_int32 complexity); int bridge_encoder_get_complexity(OggOpusEnc *st, opus_int32 *complexity); int bridge_encoder_set_max_bandwidth(OggOpusEnc *st, opus_int32 max_bw); int bridge_encoder_get_max_bandwidth(OggOpusEnc *st, opus_int32 *max_bw); int bridge_encoder_set_inband_fec(OggOpusEnc *st, opus_int32 fec); int bridge_encoder_get_inband_fec(OggOpusEnc *st, opus_int32 *fec); int bridge_encoder_set_packet_loss_perc(OggOpusEnc *st, opus_int32 loss_perc); int bridge_encoder_get_packet_loss_perc(OggOpusEnc *st, opus_int32 *loss_perc); int bridge_encoder_set_application(OggOpusEnc *st, opus_int32 application); OpusEncCallbacks * my_write_callbacks(); OggOpusComments* my_empty_comment(); */ import "C" type Bandwidth int const ( // 4 kHz passband Narrowband = Bandwidth(C.OPUS_BANDWIDTH_NARROWBAND) // 6 kHz passband Mediumband = Bandwidth(C.OPUS_BANDWIDTH_MEDIUMBAND) // 8 kHz passband Wideband = Bandwidth(C.OPUS_BANDWIDTH_WIDEBAND) // 12 kHz passband SuperWideband = Bandwidth(C.OPUS_BANDWIDTH_SUPERWIDEBAND) // 20 kHz passband Fullband = Bandwidth(C.OPUS_BANDWIDTH_FULLBAND) ) var errEncUninitialized = fmt.Errorf("opus encoder uninitialized") // Encoder contains the state of an Opus encoder for libopus. type Encoder struct { p *C.struct_OggOpusEnc channels int handle cgo.Handle writer io.Writer } //export go_writecallback func go_writecallback(p unsafe.Pointer, cbuf *C.uchar, length C.int) C.int { 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 err := enc.Init(sampleRate, channels, application, writer) if err != nil { return nil, err } return &enc, nil } // 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(sampleRate int, channels int, application Application, writer io.Writer) error { if enc.p != nil { return fmt.Errorf("opus encoder already initialized") } if channels != 1 && channels != 2 { return fmt.Errorf("number of channels must be 1 or 2: %d", channels) } enc.channels = channels enc.writer = writer enc.handle = cgo.NewHandle(enc) var errno C.int if channels > 8 { 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(255), &errno) } else if channels > 2 { 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) } 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 { return Error(int(errno)) } C.bridge_encoder_set_application(enc.p, C.int(application)) return nil } // Encode raw PCM data func (enc *Encoder) Encode(pcm []int16) error { if enc.p == nil { return errEncUninitialized } if len(pcm) == 0 { return fmt.Errorf("opus: no data supplied") } if len(pcm)%enc.channels != 0 { return fmt.Errorf("opus: input buffer length must be multiple of channels") } samples := len(pcm) / enc.channels n := int(C.ope_encoder_write(enc.p, (*C.opus_int16)(&pcm[0]), C.int(samples))) if n != 0 { return Error(n) } return nil } // FlushHeaders Write out the header now rather than wait for audio to begin. func (enc *Encoder) FlushHeaders() 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 { return errEncUninitialized } if len(pcm) == 0 { return fmt.Errorf("opus: no data supplied") } if len(pcm)%enc.channels != 0 { return fmt.Errorf("opus: input buffer length must be multiple of channels") } samples := len(pcm) / enc.channels n := int(C.ope_encoder_write_float(enc.p, (*C.float)(&pcm[0]), C.int(samples))) if n != 0 { return Error(n) } return nil } // SetDTX configures the encoder's use of discontinuous transmission (DTX). func (enc *Encoder) SetDTX(dtx bool) error { i := 0 if dtx { i = 1 } res := C.bridge_encoder_set_dtx(enc.p, C.opus_int32(i)) if res != C.OPUS_OK { return Error(res) } return nil } // DTX reports whether this encoder is configured to use discontinuous // transmission (DTX). func (enc *Encoder) DTX() (bool, error) { var dtx C.opus_int32 res := C.bridge_encoder_get_dtx(enc.p, &dtx) if res != C.OPUS_OK { return false, Error(res) } return dtx != 0, nil } // SampleRate returns the encoder sample rate in Hz. func (enc *Encoder) SampleRate() (int, error) { var sr C.opus_int32 res := C.bridge_encoder_get_sample_rate(enc.p, &sr) if res != C.OPUS_OK { return 0, Error(res) } return int(sr), nil } // SetBitrate sets the bitrate of the Encoder func (enc *Encoder) SetBitrate(bitrate int) error { res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(bitrate)) if res != C.OPUS_OK { return Error(res) } return nil } // SetBitrateToAuto will allow the encoder to automatically set the bitrate func (enc *Encoder) SetBitrateToAuto() error { res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(C.OPUS_AUTO)) if res != C.OPUS_OK { return Error(res) } return nil } // SetBitrateToMax causes the encoder to use as much rate as it can. This can be // useful for controlling the rate by adjusting the output buffer size. func (enc *Encoder) SetBitrateToMax() error { res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(C.OPUS_BITRATE_MAX)) if res != C.OPUS_OK { return Error(res) } return nil } // Bitrate returns the bitrate of the Encoder func (enc *Encoder) Bitrate() (int, error) { var bitrate C.opus_int32 res := C.bridge_encoder_get_bitrate(enc.p, &bitrate) if res != C.OPUS_OK { return 0, Error(res) } return int(bitrate), nil } // SetMuxingDelay sets maximum container delay func (enc *Encoder) SetMuxingDelay(delay time.Duration) error { if delay < 0 || delay > time.Millisecond*1000 { return errors.New("value must be between 0 to 1000 milliseconds") } res := C.bridge_encoder_set_muxing_delay(enc.p, C.opus_int32(delay.Milliseconds()*48)) if res != C.OPUS_OK { return Error(res) } return nil } // MuxingDelay returns the maximum container delay func (enc *Encoder) MuxingDelay() (time.Duration, error) { var delay C.opus_int32 res := C.bridge_encoder_get_muxing_delay(enc.p, &delay) if res != C.OPUS_OK { return 0, Error(res) } return time.Millisecond * time.Duration(int(delay)/48), nil } // SetComplexity sets the encoder's computational complexity func (enc *Encoder) SetComplexity(complexity int) error { res := C.bridge_encoder_set_complexity(enc.p, C.opus_int32(complexity)) if res != C.OPUS_OK { return Error(res) } return nil } // Complexity returns the computational complexity used by the encoder func (enc *Encoder) Complexity() (int, error) { var complexity C.opus_int32 res := C.bridge_encoder_get_complexity(enc.p, &complexity) if res != C.OPUS_OK { return 0, Error(res) } return int(complexity), nil } // SetMaxBandwidth configures the maximum bandpass that the encoder will select // automatically func (enc *Encoder) SetMaxBandwidth(maxBw Bandwidth) error { res := C.bridge_encoder_set_max_bandwidth(enc.p, C.opus_int32(maxBw)) if res != C.OPUS_OK { return Error(res) } return nil } // MaxBandwidth gets the encoder's configured maximum allowed bandpass. func (enc *Encoder) MaxBandwidth() (Bandwidth, error) { var maxBw C.opus_int32 res := C.bridge_encoder_get_max_bandwidth(enc.p, &maxBw) if res != C.OPUS_OK { return 0, Error(res) } return Bandwidth(maxBw), nil } // SetInBandFEC configures the encoder's use of inband forward error // correction (FEC) func (enc *Encoder) SetInBandFEC(fec bool) error { i := 0 if fec { i = 1 } res := C.bridge_encoder_set_inband_fec(enc.p, C.opus_int32(i)) if res != C.OPUS_OK { return Error(res) } return nil } // InBandFEC gets the encoder's configured inband forward error correction (FEC) func (enc *Encoder) InBandFEC() (bool, error) { var fec C.opus_int32 res := C.bridge_encoder_get_inband_fec(enc.p, &fec) if res != C.OPUS_OK { return false, Error(res) } return fec != 0, nil } // SetPacketLossPerc configures the encoder's expected packet loss percentage. func (enc *Encoder) SetPacketLossPerc(lossPerc int) error { res := C.bridge_encoder_set_packet_loss_perc(enc.p, C.opus_int32(lossPerc)) if res != C.OPUS_OK { return Error(res) } return nil } // PacketLossPerc gets the encoder's configured packet loss percentage. func (enc *Encoder) PacketLossPerc() (int, error) { var lossPerc C.opus_int32 res := C.bridge_encoder_get_packet_loss_perc(enc.p, &lossPerc) if res != C.OPUS_OK { return 0, Error(res) } return int(lossPerc), nil }