mirror of
https://github.com/stryan/mumble-discord-bridge.git
synced 2024-11-23 05:45:41 -05:00
refactor global variables into duplex structs to fix issue on bridge restart
disable timer pausing in discord send untill better notification method can be developed
This commit is contained in:
parent
8ca66fb500
commit
c94362581a
@ -141,7 +141,7 @@ func (b *BridgeState) StartBridge() {
|
|||||||
|
|
||||||
// MUMBLE Connect
|
// MUMBLE Connect
|
||||||
|
|
||||||
b.MumbleStream = &MumbleDuplex{}
|
b.MumbleStream = NewMumbleDuplex()
|
||||||
det := b.BridgeConfig.MumbleConfig.AudioListeners.Attach(b.MumbleStream)
|
det := b.BridgeConfig.MumbleConfig.AudioListeners.Attach(b.MumbleStream)
|
||||||
defer det.Detach()
|
defer det.Detach()
|
||||||
|
|
||||||
@ -180,6 +180,9 @@ func (b *BridgeState) StartBridge() {
|
|||||||
defer close(toDiscord)
|
defer close(toDiscord)
|
||||||
defer close(toMumble)
|
defer close(toMumble)
|
||||||
|
|
||||||
|
// From Discord
|
||||||
|
b.DiscordStream = NewDiscordDuplex(b)
|
||||||
|
|
||||||
// Start Passing Between
|
// Start Passing Between
|
||||||
|
|
||||||
// From Mumble
|
// From Mumble
|
||||||
@ -189,12 +192,6 @@ func (b *BridgeState) StartBridge() {
|
|||||||
b.MumbleStream.fromMumbleMixer(ctx, cancel, toDiscord)
|
b.MumbleStream.fromMumbleMixer(ctx, cancel, toDiscord)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// From Discord
|
|
||||||
b.DiscordStream = &DiscordDuplex{
|
|
||||||
Bridge: b,
|
|
||||||
fromDiscordMap: make(map[uint32]fromDiscord),
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
@ -28,10 +28,18 @@ type DiscordDuplex struct {
|
|||||||
|
|
||||||
discordMutex sync.Mutex
|
discordMutex sync.Mutex
|
||||||
fromDiscordMap map[uint32]fromDiscord
|
fromDiscordMap map[uint32]fromDiscord
|
||||||
|
discordSendSleepTick sleepct.SleepCT
|
||||||
|
discordReceiveSleepTick sleepct.SleepCT
|
||||||
}
|
}
|
||||||
|
|
||||||
var discrodSendSleepTick sleepct.SleepCT = sleepct.SleepCT{}
|
func NewDiscordDuplex(b *BridgeState) *DiscordDuplex {
|
||||||
var discrodReceiveSleepTick sleepct.SleepCT = sleepct.SleepCT{}
|
return &DiscordDuplex{
|
||||||
|
Bridge: b,
|
||||||
|
fromDiscordMap: make(map[uint32]fromDiscord),
|
||||||
|
discordSendSleepTick: sleepct.SleepCT{},
|
||||||
|
discordReceiveSleepTick: sleepct.SleepCT{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OnError gets called by dgvoice when an error is encountered.
|
// OnError gets called by dgvoice when an error is encountered.
|
||||||
// By default logs to STDERR
|
// By default logs to STDERR
|
||||||
@ -46,7 +54,7 @@ var OnError = func(str string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SendPCM will receive on the provied channel encode
|
// SendPCM will receive on the provied channel encode
|
||||||
// received PCM data into Opus then send that to Discordgo
|
// received PCM data with Opus then send that to Discordgo
|
||||||
func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, cancel context.CancelFunc, pcm <-chan []int16) {
|
func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, cancel context.CancelFunc, pcm <-chan []int16) {
|
||||||
const channels int = 1
|
const channels int = 1
|
||||||
const frameRate int = 48000 // audio sampling rate
|
const frameRate int = 48000 // audio sampling rate
|
||||||
@ -64,12 +72,28 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, cancel context.Canc
|
|||||||
// Generate Opus Silence Frame
|
// Generate Opus Silence Frame
|
||||||
opusSilence := []byte{0xf8, 0xff, 0xfe}
|
opusSilence := []byte{0xf8, 0xff, 0xfe}
|
||||||
|
|
||||||
discrodSendSleepTick.Start(20 * time.Millisecond)
|
dd.discordSendSleepTick.Start(20 * time.Millisecond)
|
||||||
|
|
||||||
lastReady := true
|
lastReady := true
|
||||||
var readyTimeout *time.Timer
|
var readyTimeout *time.Timer
|
||||||
var speakingStart time.Time
|
var speakingStart time.Time
|
||||||
|
|
||||||
|
// Spy on the PCM channel to notify
|
||||||
|
// TODO determine a method to notify a paused sleepct
|
||||||
|
// pcm := make(chan []int16, 10)
|
||||||
|
// go func() {
|
||||||
|
// for {
|
||||||
|
// t, ok := <-pcmIn
|
||||||
|
// if !ok {
|
||||||
|
// close(pcm)
|
||||||
|
// return
|
||||||
|
// } else {
|
||||||
|
// dd.discordSendSleepTick.Notify()
|
||||||
|
// pcm <- t
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }()
|
||||||
|
|
||||||
internalSend := func(opus []byte) {
|
internalSend := func(opus []byte) {
|
||||||
dd.Bridge.DiscordVoice.RWMutex.RLock()
|
dd.Bridge.DiscordVoice.RWMutex.RLock()
|
||||||
if !dd.Bridge.DiscordVoice.Ready || dd.Bridge.DiscordVoice.OpusSend == nil {
|
if !dd.Bridge.DiscordVoice.Ready || dd.Bridge.DiscordVoice.OpusSend == nil {
|
||||||
@ -101,7 +125,8 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, cancel context.Canc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we are not streaming try to pause
|
// if we are not streaming try to pause
|
||||||
promTimerDiscordSend.Observe(float64(discrodSendSleepTick.SleepNextTarget(ctx, !streaming)))
|
// promTimerDiscordSend.Observe(float64(dd.discordSendSleepTick.SleepNextTarget(ctx, !streaming)))
|
||||||
|
promTimerDiscordSend.Observe(float64(dd.discordSendSleepTick.SleepNextTarget(ctx, false)))
|
||||||
|
|
||||||
if (len(pcm) > 1 && streaming) || (len(pcm) > dd.Bridge.BridgeConfig.DiscordStartStreamingCount && !streaming) {
|
if (len(pcm) > 1 && streaming) || (len(pcm) > dd.Bridge.BridgeConfig.DiscordStartStreamingCount && !streaming) {
|
||||||
if !streaming {
|
if !streaming {
|
||||||
@ -128,7 +153,7 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, cancel context.Canc
|
|||||||
// It is possible that short speaking cycle is the result of a short input to mumble (Not a problem). ie a quick tap of push to talk button.
|
// It is possible that short speaking cycle is the result of a short input to mumble (Not a problem). ie a quick tap of push to talk button.
|
||||||
// Or when timing delays are introduced via network, hardware or kernel delays (Problem).
|
// Or when timing delays are introduced via network, hardware or kernel delays (Problem).
|
||||||
// The problem delays result in choppy or stuttering sounds, especially when the silence frames are introduced into the opus frames below.
|
// The problem delays result in choppy or stuttering sounds, especially when the silence frames are introduced into the opus frames below.
|
||||||
// Multiple short cycle delays can result in a Discrod rate limiter being trigger due to of multiple JSON speaking/not-speaking state changes
|
// Multiple short cycle delays can result in a discord rate limiter being trigger due to of multiple JSON speaking/not-speaking state changes
|
||||||
if time.Since(speakingStart).Milliseconds() < 50 {
|
if time.Since(speakingStart).Milliseconds() < 50 {
|
||||||
log.Println("Warning: Short Mumble to Discord speaking cycle. Consider increaseing the size of the to Discord jitter buffer.")
|
log.Println("Warning: Short Mumble to Discord speaking cycle. Consider increaseing the size of the to Discord jitter buffer.")
|
||||||
}
|
}
|
||||||
@ -137,7 +162,9 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, cancel context.Canc
|
|||||||
// We want to do this after alerting the user of possible short speaking cycles
|
// We want to do this after alerting the user of possible short speaking cycles
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
internalSend(opusSilence)
|
internalSend(opusSilence)
|
||||||
promTimerDiscordSend.Observe(float64(discrodSendSleepTick.SleepNextTarget(ctx, true)))
|
// promTimerDiscordSend.Observe(float64(dd.discordSendSleepTick.SleepNextTarget(ctx, true)))
|
||||||
|
promTimerDiscordSend.Observe(float64(dd.discordSendSleepTick.SleepNextTarget(ctx, false)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dd.Bridge.DiscordVoice.Speaking(false)
|
dd.Bridge.DiscordVoice.Speaking(false)
|
||||||
@ -260,7 +287,7 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, cancel context.C
|
|||||||
}
|
}
|
||||||
dd.discordMutex.Unlock()
|
dd.discordMutex.Unlock()
|
||||||
|
|
||||||
discrodReceiveSleepTick.Notify()
|
dd.discordReceiveSleepTick.Notify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +298,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, toMumble chan<- g
|
|||||||
}
|
}
|
||||||
var speakingStart time.Time
|
var speakingStart time.Time
|
||||||
|
|
||||||
discrodReceiveSleepTick.Start(10 * time.Millisecond)
|
dd.discordReceiveSleepTick.Start(10 * time.Millisecond)
|
||||||
|
|
||||||
sendAudio := false
|
sendAudio := false
|
||||||
toMumbleStreaming := false
|
toMumbleStreaming := false
|
||||||
@ -285,7 +312,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, toMumble chan<- g
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if didn't send audio try to pause
|
// if didn't send audio try to pause
|
||||||
promTimerDiscordMixer.Observe(float64(discrodReceiveSleepTick.SleepNextTarget(ctx, !sendAudio)))
|
promTimerDiscordMixer.Observe(float64(dd.discordReceiveSleepTick.SleepNextTarget(ctx, !sendAudio)))
|
||||||
|
|
||||||
dd.discordMutex.Lock()
|
dd.discordMutex.Lock()
|
||||||
|
|
||||||
@ -364,7 +391,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, toMumble chan<- g
|
|||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
mumbleTimeoutSend(mumbleSilence)
|
mumbleTimeoutSend(mumbleSilence)
|
||||||
promTimerDiscordMixer.Observe(float64(discrodReceiveSleepTick.SleepNextTarget(ctx, false)))
|
promTimerDiscordMixer.Observe(float64(dd.discordReceiveSleepTick.SleepNextTarget(ctx, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
toMumbleStreaming = false
|
toMumbleStreaming = false
|
||||||
|
@ -12,27 +12,34 @@ import (
|
|||||||
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
|
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mutex sync.Mutex
|
// MumbleDuplex - listener and outgoing
|
||||||
var fromMumbleArr []chan gumble.AudioBuffer
|
type MumbleDuplex struct {
|
||||||
var mumbleStreamingArr []bool
|
mutex sync.Mutex
|
||||||
|
fromMumbleArr []chan gumble.AudioBuffer
|
||||||
|
mumbleStreamingArr []bool
|
||||||
|
mumbleSleepTick sleepct.SleepCT
|
||||||
|
}
|
||||||
|
|
||||||
// MumbleDuplex - listenera and outgoing
|
func NewMumbleDuplex() *MumbleDuplex {
|
||||||
type MumbleDuplex struct{}
|
return &MumbleDuplex{
|
||||||
|
fromMumbleArr: make([]chan gumble.AudioBuffer, 0),
|
||||||
var mumbleSleepTick sleepct.SleepCT = sleepct.SleepCT{}
|
mumbleStreamingArr: make([]bool, 0),
|
||||||
|
mumbleSleepTick: sleepct.SleepCT{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OnAudioStream - Spawn routines to handle incoming packets
|
// OnAudioStream - Spawn routines to handle incoming packets
|
||||||
func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) {
|
func (m *MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) {
|
||||||
|
|
||||||
// hold a reference ot the channel in the closure
|
// hold a reference ot the channel in the closure
|
||||||
localMumbleArray := make(chan gumble.AudioBuffer, 100)
|
streamChan := make(chan gumble.AudioBuffer, 100)
|
||||||
|
|
||||||
mutex.Lock()
|
m.mutex.Lock()
|
||||||
fromMumbleArr = append(fromMumbleArr, localMumbleArray)
|
m.fromMumbleArr = append(m.fromMumbleArr, streamChan)
|
||||||
mumbleStreamingArr = append(mumbleStreamingArr, false)
|
m.mumbleStreamingArr = append(m.mumbleStreamingArr, false)
|
||||||
mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
|
|
||||||
promMumbleArraySize.Set(float64(len(fromMumbleArr)))
|
promMumbleArraySize.Set(float64(len(m.fromMumbleArr)))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
name := e.User.Name
|
name := e.User.Name
|
||||||
@ -42,17 +49,17 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) {
|
|||||||
|
|
||||||
// 480 per 10ms
|
// 480 per 10ms
|
||||||
for i := 0; i < len(p.AudioBuffer)/480; i++ {
|
for i := 0; i < len(p.AudioBuffer)/480; i++ {
|
||||||
localMumbleArray <- p.AudioBuffer[480*i : 480*(i+1)]
|
streamChan <- p.AudioBuffer[480*i : 480*(i+1)]
|
||||||
}
|
}
|
||||||
promReceivedMumblePackets.Inc()
|
promReceivedMumblePackets.Inc()
|
||||||
mumbleSleepTick.Notify()
|
m.mumbleSleepTick.Notify()
|
||||||
}
|
}
|
||||||
log.Println("Mumble audio stream ended", name)
|
log.Println("Mumble audio stream ended", name)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, cancel context.CancelFunc, toDiscord chan []int16) {
|
func (m *MumbleDuplex) fromMumbleMixer(ctx context.Context, cancel context.CancelFunc, toDiscord chan []int16) {
|
||||||
mumbleSleepTick.Start(10 * time.Millisecond)
|
m.mumbleSleepTick.Start(10 * time.Millisecond)
|
||||||
|
|
||||||
sendAudio := false
|
sendAudio := false
|
||||||
bufferWarning := false
|
bufferWarning := false
|
||||||
@ -68,36 +75,35 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, cancel context.Cancel
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we sent audio on the last pass attempt to pause
|
promTimerMumbleMixer.Observe(float64(m.mumbleSleepTick.SleepNextTarget(ctx, false)))
|
||||||
promTimerMumbleMixer.Observe(float64(mumbleSleepTick.SleepNextTarget(ctx, !sendAudio)))
|
|
||||||
|
|
||||||
mutex.Lock()
|
m.mutex.Lock()
|
||||||
|
|
||||||
sendAudio = false
|
sendAudio = false
|
||||||
internalMixerArr := make([]gumble.AudioBuffer, 0)
|
internalMixerArr := make([]gumble.AudioBuffer, 0)
|
||||||
streamingCount := 0
|
streamingCount := 0
|
||||||
|
|
||||||
// Work through each channel
|
// Work through each channel
|
||||||
for i := 0; i < len(fromMumbleArr); i++ {
|
for i := 0; i < len(m.fromMumbleArr); i++ {
|
||||||
if len(fromMumbleArr[i]) > 0 {
|
if len(m.fromMumbleArr[i]) > 0 {
|
||||||
sendAudio = true
|
sendAudio = true
|
||||||
if !mumbleStreamingArr[i] {
|
if !m.mumbleStreamingArr[i] {
|
||||||
mumbleStreamingArr[i] = true
|
m.mumbleStreamingArr[i] = true
|
||||||
streamingCount++
|
streamingCount++
|
||||||
// log.Println("Mumble starting", i)
|
// log.Println("Mumble starting", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
x1 := (<-fromMumbleArr[i])
|
x1 := (<-m.fromMumbleArr[i])
|
||||||
internalMixerArr = append(internalMixerArr, x1)
|
internalMixerArr = append(internalMixerArr, x1)
|
||||||
} else {
|
} else {
|
||||||
if mumbleStreamingArr[i] {
|
if m.mumbleStreamingArr[i] {
|
||||||
mumbleStreamingArr[i] = false
|
m.mumbleStreamingArr[i] = false
|
||||||
// log.Println("Mumble stopping", i)
|
// log.Println("Mumble stopping", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
|
|
||||||
promMumbleStreaming.Set(float64(streamingCount))
|
promMumbleStreaming.Set(float64(streamingCount))
|
||||||
|
|
||||||
@ -145,8 +151,6 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, cancel context.Cancel
|
|||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
discrodSendSleepTick.Notify()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user