Fix speaker audio desync by removing server-side pacing and adding sample-offset sync#2412
Fix speaker audio desync by removing server-side pacing and adding sample-offset sync#2412joviah wants to merge 4 commits intocc-tweaked:mc-1.20.xfrom
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Send audio eagerly on next tick instead of estimating client playback. Track cumulative sample offset per stream for client-side sync. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Track consumed and buffered samples against server-provided offsets. Hard reset when drift exceeds 500ms, drop stale packets. Synchronized push()/read() for thread safety. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Ah, I had not realised you were planning to use Claude to write this. CC:T does not accept LLM-generated code — I have updated the contributing documentation to more accurately reflect this.
I think tagging each audio packet with the expected time it will play, and then dropping/pad the audio buffer as needed makes sense — I've debated doing something similar with global timer in an attempt to fix #2412, but that's a bit trickier than using a per-stream timer. However, I think any approach here needs to use the time (or at least the sound engine's notion of time), rather than just the sample count. If the sound engine itself is not reading the the audio stream (which is the underlying problem of #2108 or #1313), then this fix does nothing. I think the server has to remain the source of truth here — the implementation here allows you the server to send an infinite amount of audio data to the client, potentially OOMing them — it's just the way |
|
No worries! Thanks! |
Summary
Fixes #1874 and #2120.
DfpwmState. Audio is now sent to the client on the next server tick after Lua pushes it. The existing single-slot buffer design provides natural flow control.EncodedAudiopackets. The server tracks cumulative samples sent per stream.DfpwmStream. The client tracks consumed samples and compares against incoming offsets. Small drift self-corrects; drift beyond 500ms triggers a hard reset (queue clear + resume from latest packet).Why not improve the estimation?
The server fundamentally cannot model client playback accurately — network latency, sound engine scheduling, volume=0, and the 8-speaker Minecraft limit all cause the estimate to drift. Rather than building a better estimate, this PR removes the estimation entirely and lets the client self-correct using real data.
Edge cases covered
consumedSamplesstops advancing, drift grows, and a hard reset fires when playback resumes — discarding stale audio.Test plan
EncodedAudiosampleOffset serialization roundtripDfpwmStatesampleOffset tracking andisPlaying()timeoutDfpwmStreamdrift detection: normal flow, hard reset, stale packet drop, small drift, read stall, stream restartFiles changed
EncodedAudio.java— addedsampleOffsetfield + VarLong serialization +withSampleOffset()DfpwmState.java— removedclientEndTime/CLIENT_BUFFER, addedsampleOffsetcounter, simplifiedshouldSendPending(), updatedisPlaying()to use timeoutSpeakerPeripheral.java— updatedshouldSendPending()call siteDfpwmStream.java— added sync state (consumedSamples,bufferedSamples,streamBaseOffset), drift detection inpush(), sample tracking inread(), synchronized access, debug logging