Keep OpenAL jitter playback continuous

This commit is contained in:
Brandon McGinty (chatgpt)
2026-08-10 16:07:47 -04:00
committed by Brandon McGinty
parent 5b0879ddaa
commit 99f1f4b9ab
2 changed files with 28 additions and 10 deletions
+16 -10
View File
@@ -51,9 +51,17 @@ type Recorder interface {
const recorderOutgoingSource uint32 = ^uint32(0)
const (
maxBufferSize = 11520 // Max frame size (2880) * bytes per stereo sample (4)
maxBufferSize = 11520 // Max frame size (2880) * bytes per stereo sample (4)
jitterMinPackets = 3
jitterMaxPackets = 10
)
// jitterPlaybackReady holds the initial playout delay only once. Requiring
// the minimum on every packet drains and refills the renderer in bursts.
func jitterPlaybackReady(started bool, buffered int) bool {
return started || buffered >= jitterMinPackets
}
var (
ErrState = errors.New("gumbleopenal: invalid state")
ErrMic = errors.New("gumbleopenal: microphone disconnected or misconfigured")
@@ -451,15 +459,10 @@ func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) {
}
// Jitter buffer: collects incoming packets, reorders by
// sequence number, and releases them in order after a small
// initial delay (3-5 packets / 30-50ms).
const (
jitterMinPackets = 3 // minimum packets before playout starts
jitterMaxPackets = 10 // maximum buffered packets before dropping oldest
)
// sequence number, and releases them after a small initial delay.
var jitterBuf []*gumble.AudioPacket
var jitterNextSeq int64
var jitterInit bool
var jitterInit, jitterStarted bool
// insertSorted inserts a packet into the jitter buffer sorted
// by sequence number.
@@ -509,10 +512,13 @@ func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) {
jitterInit = true
}
// Only start playing when we have enough buffered packets
if len(jitterBuf) < jitterMinPackets {
// Hold only the initial packets. Once playback starts, drain every
// ready packet so the renderer is fed continuously rather than in
// bursts of jitterMinPackets packets.
if !jitterPlaybackReady(jitterStarted, len(jitterBuf)) {
continue
}
jitterStarted = true
// Drain all packets that are ready (in sequence order)
for {
@@ -48,6 +48,18 @@ func TestStopSourceWaitsForWorker(t *testing.T) {
<-returned
}
func TestJitterPlaybackDelayAppliesOnlyAtStartup(t *testing.T) {
if jitterPlaybackReady(false, jitterMinPackets-1) {
t.Fatal("jitter playback started before initial buffer filled")
}
if !jitterPlaybackReady(false, jitterMinPackets) {
t.Fatal("jitter playback did not start after initial buffer filled")
}
if !jitterPlaybackReady(true, 1) {
t.Fatal("jitter playback paused while refilling after startup")
}
}
func TestRenderRejectsWorkAfterShutdown(t *testing.T) {
s := &Stream{renderClosed: true}
called := false