65 lines
1.5 KiB
Go
65 lines
1.5 KiB
Go
// ABOUTME: Opus audio encoder
|
|
// ABOUTME: Encodes int32 samples to Opus bytes
|
|
package encode
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/Sendspin/sendspin-go/pkg/audio"
|
|
"gopkg.in/hraban/opus.v2"
|
|
)
|
|
|
|
type OpusEncoder struct {
|
|
encoder *opus.Encoder
|
|
sampleRate int
|
|
channels int
|
|
frameSize int
|
|
pcmBuf []int16 // reusable conversion buffer
|
|
outBuf []byte // reusable encode output buffer
|
|
}
|
|
|
|
func NewOpus(format audio.Format) (Encoder, error) {
|
|
if format.Codec != "opus" {
|
|
return nil, fmt.Errorf("invalid codec for Opus encoder: %s", format.Codec)
|
|
}
|
|
|
|
encoder, err := opus.NewEncoder(format.SampleRate, format.Channels, opus.AppAudio)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create opus encoder: %w", err)
|
|
}
|
|
|
|
// Opus frame size depends on sample rate
|
|
frameSize := format.SampleRate / 50 // 20ms frame
|
|
|
|
return &OpusEncoder{
|
|
encoder: encoder,
|
|
sampleRate: format.SampleRate,
|
|
channels: format.Channels,
|
|
frameSize: frameSize,
|
|
pcmBuf: make([]int16, frameSize*format.Channels),
|
|
outBuf: make([]byte, 4000),
|
|
}, nil
|
|
}
|
|
|
|
func (e *OpusEncoder) Encode(samples []int32) ([]byte, error) {
|
|
if len(samples) > len(e.pcmBuf) {
|
|
e.pcmBuf = make([]int16, len(samples))
|
|
}
|
|
|
|
pcm := e.pcmBuf[:len(samples)]
|
|
for i, sample := range samples {
|
|
pcm[i] = audio.SampleToInt16(sample)
|
|
}
|
|
|
|
n, err := e.encoder.Encode(pcm, e.outBuf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("opus encode error: %w", err)
|
|
}
|
|
|
|
return append([]byte(nil), e.outBuf[:n]...), nil
|
|
}
|
|
|
|
func (e *OpusEncoder) Close() error {
|
|
return nil
|
|
}
|