package main import ( "fmt" "os" "time" "github.com/bwmarrin/discordgo" "layeh.com/gopus" "layeh.com/gumble/gumble" _ "layeh.com/gumble/opus" ) // OnError gets called by dgvoice when an error is encountered. // By default logs to STDERR var OnError = func(str string, err error) { prefix := "dgVoice: " + str if err != nil { os.Stderr.WriteString(prefix + ": " + err.Error()) } else { os.Stderr.WriteString(prefix) } } // SendPCM will receive on the provied channel encode // received PCM data into Opus then send that to Discordgo func discordSendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16) { const channels int = 1 const frameRate int = 48000 // audio sampling rate const frameSize int = 960 // uint16 size of each audio frame const maxBytes int = (frameSize * 2) * 2 // max size of opus data streaming := false opusEncoder, err := gopus.NewEncoder(frameRate, channels, gopus.Audio) if err != nil { OnError("NewEncoder Error", err) return } ticker := time.NewTicker(20 * time.Millisecond) for { <-ticker.C if len(pcm) > 1 { if !streaming { v.Speaking(true) streaming = true } r1 := <-pcm r2 := <-pcm // try encoding pcm frame with Opus opus, err := opusEncoder.Encode(append(r1, r2...), frameSize, maxBytes) if err != nil { OnError("Encoding Error", err) return } if v.Ready == false || v.OpusSend == nil { OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", v.Ready, v.OpusSend), nil) return } v.OpusSend <- opus } else { if streaming { v.Speaking(false) streaming = false } } } } // ReceivePCM will receive on the the Discordgo OpusRecv channel and decode // the opus audio into PCM then send it on the provided channel. func discordReceivePCM(v *discordgo.VoiceConnection, toMumble chan gumble.AudioBuffer) { var err error speakers := make(map[uint32]*gopus.Decoder) for { if v.Ready == false || v.OpusRecv == nil { OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", v.Ready, v.OpusSend), nil) return } p, ok := <-v.OpusRecv if !ok { fmt.Println("Opus not ok") return } _, ok = speakers[p.SSRC] if !ok { speakers[p.SSRC], err = gopus.NewDecoder(48000, 1) if err != nil { OnError("error creating opus decoder", err) continue } } p.PCM, err = speakers[p.SSRC].Decode(p.Opus, 960, false) if err != nil { OnError("Error decoding opus data", err) continue } toMumble <- p.PCM[0:480] toMumble <- p.PCM[480:960] } }