Improve voice system. Fix long-standing and recent bugs (stutter, loss of speech) - #5239
Merged
Merged
Conversation
Fix voice stuttering and intermittent loss of speech. Various other improvements as well (see PR desc)
FileEX
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Voice: fixes for the DoS-hardening regressions, plus the long-standing voice bugs
This is a follow-up for 9e28567, to fix voice stuttering and intermittent loss of speech. The hardening was good, but it introduced some user-visible problems, and while fixing those I also made fixes for bugs that have been in the voice code for years. The hardening's protections stay intact - where they dropped things, we now queue them instead.
Part 1: improvements over the DoS hardening commit (9e28567)
No more dropped audio from the decode cap. The hardening limits the client to 6 Speex decodes per rendered frame so a packet flood can't eat your CPU. The problem: packets past that limit were just thrown away, which made relayed bursts sound choppy. This change queues the overflow (bounded to 64 frames, oldest recycled if it ever fills) and drains it on later frames, so the CPU stays bounded and the audio isn't lost.
No more fake stop/start events mid-sentence. The hardening's dropped packets starve the playback buffer, which fires a BASS "stall" and the client immediately reported onClientPlayerVoiceStop - then a start again as soon as packets resumed. Scripts saw constant flicker. The stop is now only reported after 250ms of both audio silence and packet silence, and only once every queued frame has been decoded. (To be fair: the immediate-stop-on-stall behavior itself is old - 2018 - but the cap turned a rare glitch into a constant one, and this fix handles both.)
Reworked the hardening's own additions so they cooperate with the queue: the packet timestamp is stamped before the uniform-byte skip (so silent-but-held push-to-talk still counts as "talking"), and the per-frame decode counter became a shared budget between packet intake and the queue drain, with the drain getting first pick.
Part 2: "Long-standing voice bugs" (General improvements)
Sender side (the player talking):
Listener side (the player hearing):
Volume bug from 2016. The user's volume scale was multiplied into m_fVolume at creation and again at play time, so setting volume back to 1.0 never restored full volume. The base value now stays neutral and the scale is applied only at play time.
Playback speed set to zero. m_fDefaultFrequency was never initialized (it sat at zero since 2016), so SetPlaybackSpeed multiplied against 0. It's now seeded from the sample rate and read back from BASS as a fallback.
Canceled start events didn't actually mute. If a script canceled onClientPlayerVoiceStart, the client just returned for that one packet and re-asked for the next one, so a muted player kept triggering the event and kept being fed into BASS. Canceling now silences the whole burst and drops the frames already queued.
BASS queue capped. The push stream now has a one-second limit, so a stalled or flooded stream can't grow memory without bound.
Safe teardown. DeInit is guarded so a half-initialized voice object can't crash on cleanup.
Server side:
Independent files:
Testing