Jitter buffer v2, per-minute rx logging, websocket keepalive

The cohost sounded jittery on the 2026-11-03 show. v1 skipped ahead on an
underrun and kept playing from an empty buffer, so after the first hiccup
every later wobble was another glitch. It also never corrected a slow guest
clock: ~12 glitches/min in simulation at 30 ppm slow.

- Underrun: hold the sequence position and refill to a larger target
- Drift correction both ways (drop/insert a frame in quiet moments),
  quiet judged relative to the guest's own noise floor
- 80 ms start, 300 ms max, relaxes after 2 calm minutes
- Log 'rx 1m:' stats, the selected route, and call totals
- Ping the guest websocket every 25 s (the proxy closed it every 90 s)
- Simulation kept in test/jb_sim_test.go.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
namandClaude Opus 5.5 committed 2026-11-04 11:21:50 -06:00
1 parent b0e3b805b1
commit de7ed62d96
5 files changed
+346 -64

No files matched your search

+21
View File
@@ -297,6 +297,26 @@ func guestWS(w http.ResponseWriter, r *http.Request) {
}
c.SetReadLimit(64 << 10)
me := &wsConn{c: c}
// Keepalive: ping every 25 s. Without it the proxy closed the idle socket every 90 s
// and the guest saw "reconnecting..." all show. Signalling only - audio is unaffected.
c.SetReadDeadline(time.Now().Add(70 * time.Second))
c.SetPongHandler(func(string) error { c.SetReadDeadline(time.Now().Add(70 * time.Second)); return nil })
pingDone := make(chan struct{})
defer close(pingDone)
go func() {
t := time.NewTicker(25 * time.Second)
defer t.Stop()
for {
select {
case <-pingDone:
return
case <-t.C:
if err := c.WriteControl(websocket.PingMessage, nil, time.Now().Add(5*time.Second)); err != nil {
return
}
}
}
}()
hub.mu.Lock()
old := hub.guest
@@ -339,6 +359,7 @@ func guestWS(w http.ResponseWriter, r *http.Request) {
if err := c.ReadJSON(&m); err != nil {
return
}
c.SetReadDeadline(time.Now().Add(70 * time.Second))
hub.mu.Lock()
current := hub.guest == me && hub.token == tok
hub.mu.Unlock()