Skip to content

Suspend idle AudioContext to release the 1 ms platform timer - #106

Open
daiverd wants to merge 1 commit into
masterfrom
agent/issue-99-audio-idle-suspend
Open

Suspend idle AudioContext to release the 1 ms platform timer#106
daiverd wants to merge 1 commit into
masterfrom
agent/issue-99-audio-idle-suspend

Conversation

@daiverd

@daiverd daiverd commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Fixes #99

The AudioContext ran from MudClient construction for the life of the tab, holding a 1 ms Windows platform timer request (a machine-wide effect: raised interrupt rate, blocked deep idle states) and an open OS audio stream even after days of silence. Confirmed live on a long-running session: context.state === "running" with zero sounds cached.

Changes

  • MediaService suspends the context via cacophony.pause() after 5 minutes with no audio activity.
  • ensureAwake() — memoized single-flight resume — is called at the top of every inbound audio path (load, play, update, setChain, automate, music resume), so concurrent GMCP messages during a wake share one resume().
  • Ramps and fades extend a busyUntil deadline so scheduled automation is never frozen mid-ramp (AudioContext.currentTime stops while suspended). Units verified as milliseconds throughout.
  • Wake-hold API (acquireWakeHold/releaseWakeHold) pins the context for activity the sound registry can't see; voice chat (audioChat.tsx) holds it for the room's lifetime since LiveKit routes through the shared cacophony context.
  • Construction stays eager (per the issue): lazy creation would leave a server-sent sound with no unlocked context before the first gesture.

Verification

  • 9 new tests (MediaService.idleSuspend.test.ts): suspend after idle, no suspend while playing / held / ramping, single shared resume, wake-before-play ordering, shutdown clears the timer. Full suite 1070/1070 green; tsc --noEmit clean.
  • Live in Chrome: suspendIfIdle()suspended, holds block it; programmatic pause()resume() round-trip verified on client.mongoose.world with no fresh gesture (origin has autoplay rights; on zero-engagement origins the resume correctly stays pending until first gesture, same audible behavior as before via cacophony's auto-unlock).

Note: the MIDI virtual synth (JZZ/Tiny) creates its own AudioContext, so with MIDI enabled a second timer-holder exists that this PR does not cover — noted on #99.

🤖 Generated with Claude Code

The AudioContext was created at MudClient construction and never
suspended for the life of the tab, holding a 1 ms Windows platform
timer request and an open OS audio stream even after days of silence.

MediaService now suspends the context after 5 minutes with no audio
activity and wakes it programmatically via a memoized ensureAwake()
at the top of every inbound audio path (load, play, update, setChain,
automate, music resume). Pending ramps and fades defer suspension via
a busyUntil deadline so scheduled automation is never frozen, and a
wake-hold API keeps the context alive for activity the sound registry
cannot see; voice chat holds it for the room's lifetime.

Construction stays eager so cacophony's auto-unlock still has the
longest possible window before the first server-sent sound.

Fixes #99

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audio: suspend idle AudioContext to release 1 ms platform timer and OS audio stream

1 participant