diff --git a/ffmpeg_linux b/ffmpeg_linux deleted file mode 100755 index 327fe21..0000000 Binary files a/ffmpeg_linux and /dev/null differ diff --git a/ffmpeg_mac b/ffmpeg_mac deleted file mode 100755 index 16e6629..0000000 Binary files a/ffmpeg_mac and /dev/null differ diff --git a/ffmpeg_windows.exe b/ffmpeg_windows.exe deleted file mode 100755 index 0e2cd9e..0000000 Binary files a/ffmpeg_windows.exe and /dev/null differ diff --git a/server/bothandlers/sounds.go b/server/bothandlers/sounds.go index 8aa26f5..87c8027 100644 --- a/server/bothandlers/sounds.go +++ b/server/bothandlers/sounds.go @@ -8,8 +8,8 @@ import ( "io" "io/ioutil" "log" + "os" "os/exec" - "runtime" "strconv" "strings" "sync" @@ -18,6 +18,7 @@ import ( "layeh.com/gopus" "github.com/bwmarrin/discordgo" + "github.com/cryptix/wav" "github.com/mgerb/go-discord-bot/server/config" ) @@ -27,6 +28,10 @@ const ( frameSize int = 960 // uint16 size of each audio frame maxBytes int = (frameSize * 2) * 2 // max size of opus data maxSoundQueue int = 10 // max amount of sounds that can be queued at one time + + // TODO: figure out why api isn't returning bitrate + bitRate int = 64000 + sampleRate int = 96000 ) // store our connection objects in a map tied to a guild id @@ -38,6 +43,7 @@ type audioConnection struct { sounds map[string]*audioClip soundQueue chan string voiceConnection *discordgo.VoiceConnection + currentChannel *discordgo.Channel soundPlayingLock bool mutex *sync.Mutex // mutex for single audio connection } @@ -103,6 +109,9 @@ func (conn *audioConnection) handleMessage(m *discordgo.MessageCreate) { case "dismiss": conn.dismiss() + case "clip": + conn.clipAudio() + default: conn.playAudio(command, m) } @@ -145,6 +154,12 @@ func (conn *audioConnection) summon(m *discordgo.MessageCreate) { log.Println(err) } + // set the current channel + conn.currentChannel = c + + // start listening to audio after joining channel + go conn.startAudioListener() + return } } @@ -202,19 +217,8 @@ func (conn *audioConnection) loadFile(fileName string) error { fmt.Println("Loading file: " + fname + fextension) - var ffmpegExecutable string - - switch runtime.GOOS { - case "darwin": - ffmpegExecutable = "./ffmpeg_mac" - case "linux": - ffmpegExecutable = "./ffmpeg_linux" - case "windows": - ffmpegExecutable = "ffmpeg_windows.exe" - } - // use ffmpeg to convert file into a format we can use - cmd := exec.Command(ffmpegExecutable, "-i", config.Config.SoundsPath+fname+fextension, "-f", "s16le", "-ar", strconv.Itoa(frameRate), "-ac", strconv.Itoa(channels), "pipe:1") + cmd := exec.Command("ffmpeg", "-i", config.Config.SoundsPath+fname+fextension, "-f", "s16le", "-ar", strconv.Itoa(frameRate), "-ac", strconv.Itoa(channels), "pipe:1") ffmpegout, err := cmd.StdoutPipe() @@ -266,6 +270,120 @@ func (conn *audioConnection) loadFile(fileName string) error { } +func (conn *audioConnection) clipAudio() { + +} + +func (conn *audioConnection) startAudioListener() { + + speakers := make(map[uint32]*gopus.Decoder) + voicePackets := []*discordgo.Packet{} + var err error + +loop: + for { + + select { + case opusChannel, ok := <-conn.voiceConnection.OpusRecv: + if !ok { + continue + } + + _, ok = speakers[opusChannel.SSRC] + + if !ok { + speakers[opusChannel.SSRC], err = gopus.NewDecoder(frameRate, 1) + if err != nil { + log.Println("error creating opus decoder", err) + continue + } + } + + opusChannel.PCM, err = speakers[opusChannel.SSRC].Decode(opusChannel.Opus, frameSize, false) + if err != nil { + log.Println("Error decoding opus data", err) + continue + } + + voicePackets = append(voicePackets, opusChannel) + + // TODO: + fmt.Println(len(voicePackets)) + fmt.Println(int(opusChannel.Timestamp) / bitRate) + if len(voicePackets) > bitRate*5 { + voicePackets = voicePackets[1:] + } + + default: + if !conn.voiceConnection.Ready { + break loop + } + + // cleanOldPackets(voicePackets) + } + + } + + writeWavFile(voicePackets) + +} + +// // remove packets that are more than 1 minute old +// func cleanOldPackets(packets []*discordgo.Packet) { + +// if len(packets) < 1 { +// return +// } + +// // latest timestamp divided by bitrate +// currentTime := int(packets[len(packets)-1].Timestamp) / bitRate + +// index := 0 +// // grab the index of the first object less than a minute ago +// for i, p := range packets { +// timestamp := int(p.Timestamp) / bitRate +// if timestamp < currentTime-5 { +// index = i +// break +// } else if timestamp > currentTime-5 { +// break +// } +// } + +// if index != 0 { + +// log.Println("cleaning") +// log.Println(index) +// } + +// packets = packets[index:] + +// } + +func writeWavFile(packets []*discordgo.Packet) { + + wavOut, err := os.Create("test.wav") + checkErr(err) + defer wavOut.Close() + + meta := wav.File{ + Channels: 1, + SampleRate: uint32(sampleRate), + SignificantBits: 16, + } + + writer, err := meta.NewWriter(wavOut) + checkErr(err) + defer writer.Close() + + for _, p := range packets { + for _, pcm := range p.PCM { + err := writer.WriteInt32(int32(pcm)) + checkErr(err) + } + } +} + // playSounds - plays the current buffer to the provided channel. func (conn *audioConnection) playSounds() (err error) { @@ -302,3 +420,9 @@ func (conn *audioConnection) toggleSoundPlayingLock(playing bool) { conn.soundPlayingLock = playing conn.mutex.Unlock() } + +func checkErr(err error) { + if err != nil { + log.Println(err) + } +}