package discordspeaker import ( "context" "io" "log" "sync" "time" "github.com/diamondburned/arikawa/v3/voice" "github.com/diamondburned/arikawa/v3/voice/voicegateway" "github.com/gopxl/beep" "github.com/pkg/errors" "golang.org/x/time/rate" "gopkg.in/hraban/opus.v2" ) var ( mu sync.Mutex mixer beep.Mixer samples [][2]float64 done chan struct{} encoder *opus.Encoder frameSize int = 960 channels int = 2 sampleRate int = 48000 maxBytes int = (frameSize * 2) * 2 buf []byte session *voice.Session pw *io.PipeWriter pr *io.PipeReader spklimit = rate.NewLimiter(rate.Every(5*time.Second), 1) spk = true ) var start time.Time var Silence = [2]float64{} var dmutex = sync.Mutex{} func Init(dgv *voice.Session, bitrate int) error { var err error mu.Lock() defer mu.Unlock() Close() pr, pw = io.Pipe() buf = make([]byte, maxBytes) mixer = beep.Mixer{} samples = make([][2]float64, frameSize) session = dgv encoder, err = opus.NewEncoder(sampleRate, channels, opus.AppAudio) encoder.SetBitrate(bitrate) if err != nil { return errors.Wrap(err, "failed to initialize speaker") } go func() { for { select { default: update() case <-done: return } } }() go func() { dmutex.Lock() _, err := io.Copy(session, pr) dmutex.Unlock() if err != nil { log.Println(err) return } }() return nil } func Close() { } func Lock() { mu.Lock() } // Unlock unlocks the speaker. Call after modifying any currently playing Streamer. func Unlock() { mu.Unlock() } func Play(s ...beep.Streamer) { mu.Lock() mixer.Add(s...) mu.Unlock() } func Clear() { mu.Lock() mixer.Clear() mu.Unlock() } func Speak(s bool) { switch s { case true: case false: } } func CheckSilence(samples [][2]float64) { if IsSilent(samples) { if spk && spklimit.Allow() { log.Println("Notspeaking") session.Speaking(context.Background(), voicegateway.NotSpeaking) spk = false } } else { if !spk { log.Println("Speaking") session.Speaking(context.Background(), voicegateway.Microphone) spk = true spklimit.Reserve() } } } func update() { start = time.Now() mu.Lock() mixer.Stream(samples) mu.Unlock() go CheckSilence(samples) if !spk { return } f32 := make([]float32, len(samples)*2) var idx int for i := 0; i < len(samples); i++ { f32[idx] = float32(samples[i][0]) idx++ f32[idx] = float32(samples[i][1]) idx++ } n, err := encoder.EncodeFloat32(f32, buf) if err != nil { log.Println(err) return } _, err = pw.Write(buf[:n]) if err != nil { log.Println(err) return } //fmt.Println(time.Since(start), len(samples), n) } func IsSilent(in [][2]float64) bool { for _, v := range in { if v != Silence { return false } } return true }