diff --git a/Makefile b/Makefile index e4a2448..5d30e51 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,12 @@ dev-race: $(GOFILES) dev-profile: $(GOFILES) goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge -cpuprofile cpu.prof +test-chart: SHELL:=/bin/bash +test-chart: + go test & + until pidof mumble-discord-bridge.test; do continue; done; + psrecord --plot test-cpu-memory.png $$(pidof mumble-discord-bridge.test) + docker-latest: docker build -t stieneee/mumble-discord-bridge:latest . diff --git a/discord.go b/discord.go index 7d2f79f..272345b 100644 --- a/discord.go +++ b/discord.go @@ -61,7 +61,11 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup, opusSilence = append(opusSilence, 0x00) } - ticker := NewTickerCT(20 * time.Millisecond) + // ticker := NewTickerCT(20 * time.Millisecond) + sleepTick := SleepCT{ + d: 20 * time.Millisecond, + t: time.Now(), + } lastReady := true var readyTimeout *time.Timer @@ -97,7 +101,10 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup, return default: } - <-ticker.C + + // <-ticker.C + sleepTick.SleepNextTarget() + if (len(pcm) > 1 && streaming) || (len(pcm) > dd.Bridge.BridgeConfig.DiscordStartStreamingCount && !streaming) { if !streaming { log.Println("Debug: Discord start speaking") @@ -134,7 +141,8 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup, // We want to do this after alerting the user of possible short speaking cycles for i := 0; i < 5; i++ { internalSend(opusSilence) - <-ticker.C + // <-ticker.C + sleepTick.SleepNextTarget() } dd.Bridge.DiscordVoice.Speaking(false) @@ -235,7 +243,10 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro } func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGroup, toMumble chan<- gumble.AudioBuffer) { - ticker := NewTickerCT(10 * time.Millisecond) + sleepTick := SleepCT{ + d: 10 * time.Millisecond, + t: time.Now(), + } sendAudio := false wg.Add(1) @@ -244,9 +255,11 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou case <-ctx.Done(): wg.Done() return - case <-ticker.C: + default: } + sleepTick.SleepNextTarget() + dd.discordMutex.Lock() sendAudio = false diff --git a/docs/test-cpu-memory.png b/docs/test-cpu-memory.png new file mode 100644 index 0000000..90a9914 Binary files /dev/null and b/docs/test-cpu-memory.png differ diff --git a/mumble.go b/mumble.go index 1f60548..c248b57 100644 --- a/mumble.go +++ b/mumble.go @@ -44,7 +44,10 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) { } func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, toDiscord chan []int16) { - ticker := NewTickerCT(10 * time.Millisecond) + sleepTick := SleepCT{ + d: 10 * time.Millisecond, + t: time.Now(), + } sendAudio := false bufferWarning := false @@ -58,7 +61,7 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t default: } - <-ticker.C + sleepTick.SleepNextTarget() mutex.Lock() @@ -86,27 +89,28 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t mutex.Unlock() - outBuf := make([]int16, 480) - - for i := 0; i < len(outBuf); i++ { - for j := 0; j < len(internalMixerArr); j++ { - outBuf[i] += (internalMixerArr[j])[i] - } - } - - if len(toDiscord) > 20 { - if !bufferWarning { - log.Println("Warning: toDiscord buffer size") - bufferWarning = true - } - } else { - if bufferWarning { - log.Println("Resolved: toDiscord buffer size") - bufferWarning = false - } - } - if sendAudio { + + outBuf := make([]int16, 480) + + for i := 0; i < len(outBuf); i++ { + for j := 0; j < len(internalMixerArr); j++ { + outBuf[i] += (internalMixerArr[j])[i] + } + } + + if len(toDiscord) > 20 { + if !bufferWarning { + log.Println("Warning: toDiscord buffer size") + bufferWarning = true + } + } else { + if bufferWarning { + log.Println("Resolved: toDiscord buffer size") + bufferWarning = false + } + } + select { case toDiscord <- outBuf: default: diff --git a/sleepct.go b/sleepct.go new file mode 100644 index 0000000..f5277c7 --- /dev/null +++ b/sleepct.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "sync" + "time" +) + +// SleepCT - Sleep constant time step crates a sleep based ticker +// designed maintain a sleep/tick interval +type SleepCT struct { + sync.Mutex + d time.Duration // duration + t time.Time // last time target +} + +func (s *SleepCT) SleepNextTarget() { + s.Lock() + + now := time.Now() + + var last time.Time + if s.t.IsZero() { + fmt.Println("SleepCT reset") + last = now.Add(-s.d) + } else { + last = s.t + } + + // Next Target + s.t = last.Add(s.d) + + d := s.t.Sub(now) + + time.Sleep(d) + + // delta := now.Sub(s.t) + // fmt.Println("delta", delta, d, time.Since(s.t)) + + s.Unlock() +} diff --git a/test-cpu-memory.png b/test-cpu-memory.png new file mode 100644 index 0000000..50c4db5 Binary files /dev/null and b/test-cpu-memory.png differ diff --git a/tickerct.go b/tickerct.go index 392d89a..7ada854 100644 --- a/tickerct.go +++ b/tickerct.go @@ -7,7 +7,7 @@ import ( "time" ) -// A Ticker holds a channel that delivers ``ticks'' of a clock +// A Ticker holds a channel that delivers ``ticks'' of a clockinter // at intervals. type TickerCT struct { sync.Mutex diff --git a/tickerct_test.go b/timing_test.go similarity index 71% rename from tickerct_test.go rename to timing_test.go index fd1c318..f0935cc 100644 --- a/tickerct_test.go +++ b/timing_test.go @@ -70,3 +70,33 @@ func TestTickerCT(t *testing.T) { wg.Wait() } + +func testSleepCT(wg *sync.WaitGroup) { + wg.Add(1) + go func(interval time.Duration) { + now := time.Now() + start := now + // start the ticker + s := SleepCT{ + d: interval, + t: time.Now(), + } + var i int64 + for i = 0; i < testCount; i++ { + if i+1 < testCount { + time.Sleep(time.Duration(float64(maxSleepInterval) * rand.Float64())) + } + s.SleepNextTarget() + } + fmt.Println("SleepCT after", testDuration, "drifts", time.Since(start)-testDuration) + wg.Done() + }(tickerInterval) +} + +func TestSleepCT(t *testing.T) { + wg := sync.WaitGroup{} + + testSleepCT(&wg) + + wg.Wait() +}