package discordspeaker import ( "context" "log" "sync" "time" "github.com/diamondburned/arikawa/v3/voice" "github.com/diamondburned/arikawa/v3/voice/voicegateway" "github.com/faiface/beep" "github.com/pkg/errors" "gopkg.in/hraban/opus.v2" ) var ( mu sync.Mutex mixer beep.Mixer samples [][2]float64 done chan struct{} encoder *opus.Encoder //voice *discordgo.VoiceConnection frameSize int = 960 channels int = 2 sampleRate int = 48000 maxBytes int = (frameSize * 2) * 2 buf []byte session *voice.Session spk bool ) func Init(dgv *voice.Session) error { var err error mu.Lock() defer mu.Unlock() Close() mixer = beep.Mixer{} buf = make([]byte, maxBytes) samples = make([][2]float64, frameSize) session = dgv encoder, err = opus.NewEncoder(sampleRate, channels, opus.AppVoIP) encoder.SetBitrateToMax() if err != nil { return errors.Wrap(err, "failed to initialize speaker") } go func() { for { select { default: update() case <-done: 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 update() { mu.Lock() mixer.Stream(samples) mu.Unlock() var f32 []float32 for _, sample := range samples { for _, val := range sample { f32 = append(f32, float32(val)) } } if Silence(f32) { if spk { log.Println("Notspeaking") session.Speaking(context.Background(), voicegateway.NotSpeaking) spk = false } time.Sleep(100 * time.Millisecond) return } if !spk { log.Println("Speaking") session.Speaking(context.Background(), voicegateway.Microphone) spk = true } n, err := encoder.EncodeFloat32(f32, buf) if err != nil { log.Println(err) time.Sleep(100 * time.Millisecond) return } _, err = session.Write(buf[:n]) if err != nil { log.Println(err) time.Sleep(100 * time.Millisecond) return } } func Silence(in []float32) bool { for _, v := range in { if v != 0 { return false } } return true }