1
0
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:
Tyler Stiene 2021-11-19 00:57:20 -05:00
parent 8ca66fb500
commit c94362581a
3 changed files with 79 additions and 51 deletions

View File

@ -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()

View File

@ -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

View File

@ -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()
} }
} }
} }