// Plays this sound over the specified VoiceConnection func (s *Sound) Play(vc *discordgo.VoiceConnection) { vc.Speaking(true) defer vc.Speaking(false) for _, buff := range s.buffer { vc.OpusSend <- buff } }
// Play a sound func PlaySound(s *discordgo.Session, play *Play, vc *discordgo.VoiceConnection) (err error) { log.WithFields(log.Fields{ "play": play, }).Info("Playing sound") if vc == nil { vc, err = s.ChannelVoiceJoin(play.GuildID, play.ChannelID, false, false) // vc.Receive = false if err != nil { log.WithFields(log.Fields{ "error": err, }).Error("Failed to play sound") delete(queues, play.GuildID) return err } } // If we need to change channels, do that now if vc.ChannelID != play.ChannelID { vc.ChangeChannel(play.ChannelID, false, false) time.Sleep(time.Millisecond * 125) } // // Track stats for this play in redis // go rdTrackSoundStats(play) // Sleep for a specified amount of time before playing the sound time.Sleep(time.Millisecond * 32) // Play the sound play.Sound.Play(vc) // If this is chained, play the chained sound if play.Next != nil { PlaySound(s, play.Next, vc) } // If there is another song in the queue, recurse and play that if len(queues[play.GuildID]) > 0 { play := <-queues[play.GuildID] PlaySound(s, play, vc) return nil } // If the queue is empty, delete it time.Sleep(time.Millisecond * time.Duration(play.Sound.PartDelay)) delete(queues, play.GuildID) vc.Disconnect() return nil }
// PlayAudioFile will play the given filename to the already connected // Discord voice server/channel. voice websocket and udp socket // must already be setup before this will work. func PlayAudioFile(v *discordgo.VoiceConnection, filename string) { // Create a shell command "object" to run. run = exec.Command("ffmpeg", "-i", filename, "-f", "s16le", "-ar", strconv.Itoa(frameRate), "-ac", strconv.Itoa(channels), "pipe:1") ffmpegout, err := run.StdoutPipe() if err != nil { fmt.Println("StdoutPipe Error:", err) return } ffmpegbuf := bufio.NewReaderSize(ffmpegout, 16384) // Starts the ffmpeg command err = run.Start() if err != nil { fmt.Println("RunStart Error:", err) return } // Send "speaking" packet over the voice websocket v.Speaking(true) // Send not "speaking" packet over the websocket when we finish defer v.Speaking(false) // will actually only spawn one instance, a bit hacky. if send == nil { send = make(chan []int16, 2) } go SendPCM(v, send) for { // read data from ffmpeg stdout audiobuf := make([]int16, frameSize*channels) err = binary.Read(ffmpegbuf, binary.LittleEndian, &audiobuf) if err == io.EOF || err == io.ErrUnexpectedEOF { return } if err != nil { fmt.Println("error reading from ffmpeg stdout :", err) return } // Send received PCM to the sendPCM channel send <- audiobuf } }
// Takes inbound audio and sends it right back out. func Echo(v *discordgo.VoiceConnection) { recv := make(chan *discordgo.Packet, 2) go dgvoice.ReceivePCM(v, recv) send := make(chan []int16, 2) go dgvoice.SendPCM(v, send) v.Speaking(true) defer v.Speaking(false) for { p, ok := <-recv if !ok { return } send <- p.PCM } }