-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: remove github.com/bwmarrin/dgvoice, pull code in
- Loading branch information
Showing
5 changed files
with
194 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"encoding/binary" | ||
"io" | ||
"os/exec" | ||
"strconv" | ||
|
||
"github.com/bwmarrin/discordgo" | ||
"layeh.com/gopus" | ||
) | ||
|
||
// NOTE: This API is not final and these are likely to change. | ||
|
||
// Technically the below settings can be adjusted however that poses | ||
// a lot of other problems that are not handled well at this time. | ||
// These below values seem to provide the best overall performance | ||
const ( | ||
channels int = 2 // 1 for mono, 2 for stereo | ||
frameRate int = 48000 // audio sampling rate | ||
frameSize int = 960 // uint16 size of each audio frame | ||
maxBytes int = (frameSize * 2) * 2 // max size of opus data | ||
) | ||
|
||
// SendPCM will receive on the provied channel encode | ||
// received PCM data into Opus then send that to Discordgo | ||
func SendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16) { | ||
if pcm == nil { | ||
return | ||
} | ||
|
||
var err error | ||
|
||
opusEncoder, err := gopus.NewEncoder(frameRate, channels, gopus.Audio) | ||
|
||
if err != nil { | ||
logger.Sugar().Errorf("NewEncoder Error: %s", err) | ||
return | ||
} | ||
|
||
for { | ||
|
||
// read pcm from chan, exit if channel is closed. | ||
recv, ok := <-pcm | ||
if !ok { | ||
logger.Sugar().Errorf("PCM Channel closed", nil) | ||
return | ||
} | ||
|
||
// try encoding pcm frame with Opus | ||
opus, err := opusEncoder.Encode(recv, frameSize, maxBytes) | ||
if err != nil { | ||
logger.Sugar().Errorf("Encoding Error: %s", err) | ||
return | ||
} | ||
|
||
if !v.Ready || v.OpusSend == nil { | ||
// OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", v.Ready, v.OpusSend), nil) | ||
// Sending errors here might not be suited | ||
return | ||
} | ||
// send encoded opus data to the sendOpus channel | ||
v.OpusSend <- opus | ||
} | ||
} | ||
|
||
// 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, stop <-chan bool) { | ||
|
||
// 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 { | ||
logger.Sugar().Errorf("StdoutPipe Error", err) | ||
return | ||
} | ||
|
||
ffmpegbuf := bufio.NewReaderSize(ffmpegout, 16384) | ||
|
||
// Starts the ffmpeg command | ||
err = run.Start() | ||
if err != nil { | ||
logger.Sugar().Errorf("RunStart Error", err) | ||
return | ||
} | ||
|
||
// prevent memory leak from residual ffmpeg streams | ||
defer run.Process.Wait() | ||
// defer run.Process.Kill() | ||
// Stop goroutine when audio is done playing | ||
done := make(chan struct{}) | ||
defer close(done) | ||
|
||
//when stop is sent, kill ffmpeg | ||
go func() { | ||
select { | ||
case <-stop: | ||
_, err = run.Process.Wait() | ||
case <-done: | ||
} | ||
}() | ||
|
||
// Send "speaking" packet over the voice websocket | ||
err = v.Speaking(true) | ||
if err != nil { | ||
logger.Sugar().Errorf("Couldn't set speaking", err) | ||
} | ||
|
||
// Send not "speaking" packet over the websocket when we finish | ||
defer func() { | ||
err := v.Speaking(false) | ||
if err != nil { | ||
logger.Sugar().Errorf("Couldn't stop speaking", err) | ||
} | ||
}() | ||
|
||
send := make(chan []int16, 2) | ||
defer close(send) | ||
|
||
close := make(chan bool) | ||
go func() { | ||
SendPCM(v, send) | ||
close <- true | ||
}() | ||
|
||
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 { | ||
logger.Sugar().Errorf("error reading from ffmpeg stdout", err) | ||
return | ||
} | ||
|
||
// Send received PCM to the sendPCM channel | ||
select { | ||
case send <- audiobuf: | ||
case <-close: | ||
return | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters