Skip to content

Optimizing the sound system when someone is speaking#323

Closed
Vitroze wants to merge 1 commit into
StyledStrike:mainfrom
Vitroze:volume_voice
Closed

Optimizing the sound system when someone is speaking#323
Vitroze wants to merge 1 commit into
StyledStrike:mainfrom
Vitroze:volume_voice

Conversation

@Vitroze

@Vitroze Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Hello,
I’ve globally optimized the voice system for when a player speaks (removing the Tick hook). The slight "animation" during the audio switch is negligible—meaning it’s imperceptible to the ear. I decided to leave it in, though I recommend removing it.

hook.Add( "Tick", "Glide.CheckVoiceActivity", function()
local isAnyoneTalking = false
hook.Add( "PlayerStartVoice", "Glide.PlayerStartVoice", function( ply )
if ply == LocalPlayer() then return end

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ignores players using voice_loopback 1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I tested it in both cases (the speaker and the listener), and the hook still executes.

image

@StyledStrike StyledStrike Jul 5, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the hook does execute, but then if ply == LocalPlayer() then return end ignores the local player, making the volume reduction not apply when the local player is speaking

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't applying yet, because of my tests.

local tPlayersTalking = {}

local function AniamtionVoice( bSpeak )
timer.Create( "Glide.VoiceChatAnimation", 0.001, 0, function()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure that using a timer with a value as low as 0.001 is the same as running every tick on a Tick hook

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not exactly the same. I don't recommend using it—especially since you can't hear the difference between having the animation and not having it. I left it in because it's part of the base code, but I'm actually going to remove it in favor of a Tick hook (I can achieve the same result using Tick).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not exactly the same

Actually for 0.001 it is. Even in the wiki for timer.Simple says

The delay interval in seconds. If the delay is too small, the timer will fire on the next Tick.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do 0.01 if necessary. It remains to be seen whether you need to keep that.

glideVolume = Approach(
glideVolume,
bSpeak and Config.vcVolume or 1,
FrameTime() * ( bSpeak and 10 or 4 )

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a timer breaks the correct usage for FrameTime(), although it was my mistake for using it inside a Tick hook, which is also incorrect

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to completely disable the volume up/down animation? Because honestly, I don't see any difference.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The slight "animation" during the audio switch is negligible

The issue I presented here might've been the cause

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The slight "animation" during the audio switch is negligible

The issue I presented here might've been the cause

Right, and yet it didn't bother anyone? If it had been glaringly obvious, I would have understood, but this was just adding a bit of realism for no reason, even though the issue has been resolved.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, and yet it didn't bother anyone?

It depends on how much FPS you have, it does bother me which is how I noticed

@Vitroze Vitroze Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, based on the tests, it drops from 1 to 0.6 very, very quickly. It goes something like 1, 0.76...... (and decimal numbers), 0.6 in under 0.7 seconds, I’d say. I’m running at around 140 fps, so that might be why,it would be interesting to see how it behaves at lower frame rates.

@StyledStrike

StyledStrike commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Relying on tracking player entities with the start/stop voice hooks also introduces new issues when it comes to invalid players (out-of-PVS or players who disconnect while talking), possibly making the volume reduction get stuck, which is why it was using player.Iterator.

I also don't know how/if that could break when the "one player stuck speaking" gmod bug happens, or what happens if the player tries to workaround that bug by using record fix; stop in the console

@Vitroze

Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Relying on tracking player entities with the start/stop voice hooks also introduces new issues when it comes to invalid players (out-of-PVS or players who disconnect while talking), possibly making the volume reduction get stuck, which is why it was using player.Iterator.

It can be done via SteamID32. I'll have that changed. This will prevent PVS or disconnection issues.

@StyledStrike

Copy link
Copy Markdown
Owner

We can do 0.01 if necessary.

Thats still about as fast as Tick on a 66 tickrate server.

It remains to be seen whether you need to keep that.

I was doing some tests just now, and I remembered that smoothing "animation" exists not only to prevent audio popping, but also to deal with Player:VoiceVolume returning 0 or Player:IsVoiceAudible returning false for a few frames, even while the player was still seemingly speaking continuously.

@StyledStrike

StyledStrike commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Oh another big thing: Some servers/addons return true on PlayerStartVoice to create custom voice HUDs, which means these hooks may not even run for Glide

@Vitroze

Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Yeah, but we can't just leave a Tick hook running for that; if that really is the issue, we can overwrite hook.Run and hook.Call so it executes anyway.

It kills performance for no reason.
Regarding the PVS issue, I tested it and my method works.

Vitroze@44f3ee5

@StyledStrike

Copy link
Copy Markdown
Owner

we can overwrite hook.Run and hook.Call so it executes anyway.

That's kinda hacky and prone to compatibility issues

Regarding the PVS issue, I tested it and my method works.

Doesn't matter if we can't rely on PlayerStartVoice

@StyledStrike

Copy link
Copy Markdown
Owner

It kills performance for no reason.

For just a IsVoiceAudible, VoiceVolume and math.Approach call every tick? It's not even every frame, which means that at 140 FPS you still be calling those functions 66 times per second.
And even that could be optimized by localizing IsVoiceAudible, VoiceVolume from the player metatable

@Vitroze

Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

That's kinda hacky and prone to compatibility issues

Do we really have a choice? Or should we just leave that inefficient piece of code as is? I was mainly referring to the fact that when PlayerStartVoice is called, the rest of the code executes, followed by our hook, and only ours (via the identifier).

Doesn't matter if we can't rely on PlayerStartVoice

Otherwise, we could rely on PlayerCanHearPlayersVoice, but that hook is resource-intensive, and frankly, we'd have to cobble something together.

@Vitroze

Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

For just a IsVoiceAudible, VoiceVolume and math.Approach call every tick? It's not even every frame, which means that at 140 FPS you still be calling those functions 66 times per second.
And even that could be optimized by localizing IsVoiceAudible, VoiceVolume from the player metatable

True, but we're still fetching all the players 140 times just to check if someone is speaking, so technically, we don't even need to do that.

Actually, for sandbox, it makes no difference; you won't notice it. But on servers, or for people playing solo with a large number of addons, the difference will be noticeable.

@StyledStrike

StyledStrike commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Otherwise, we could rely on PlayerCanHearPlayersVoice, but that hook is resource-intensive

That's not even a client-side hook

True, but we're still fetching all the players 140 times just to check if someone is speaking,

Please pay attention, the Tick hook does not run every frame, it's capped by the tickrate. It's not "fetching all the players 140 times"

@Vitroze

Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

That's not even a client-side hook

Yes, and it works with a net system, but it's still a bit of a makeshift setup.

Please pay attention, the Tick hook does not run every frame, it's capped by the tickrate. It's not "fetching all the players 140 times"

Oh, sorry excuse me, I got mixed up between 144 fps and 66-tick servers. Even so, I still find that "huge" for so little.

@Vitroze

Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

87291e4 That optimizes things a bit, but I remain convinced that the voice system can be detected outside of a Think hook.

@StyledStrike

Copy link
Copy Markdown
Owner

87291e4 capped the voice checks to at most 30 times per second. I'm now experimenting with checking one player at a time per frame instead of all players in one go.

@Vitroze

Vitroze commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

I tried overwriting hook.Run/hook.Call, but that won't work.

The only idea that actually works is overwriting hook.Add to remove all PlayerStartVoice hooks and move them to a different hook, but that defeats the purpose of Glide and amounts to a hacky workaround.

Below is a quick example I put together to get this working and make the hook reliable.

local voice_loop = GetConVar( "voice_loopback" )

-- Example HUD voice
hook.Add( "PlayerStartVoice", "TestAA", function()
    return true
end )

-- Overwrite
for sNameHook, fcHook in pairs( hook.GetTable()["PlayerStartVoice"] ) do
    hook.Remove( "PlayerStartVoice", sNameHook )
    hook.Add( "PlayerBeforeStartVoice", sNameHook, fcHook )
end

VitrozeFCHookAdd = VitrozeFCHookAdd or hook.Add
function hook.Add( sHook, sName, fcFunc )
    if sHook == "PlayerStartVoice" and sName ~= "Glide.ManageVoice" then
        sHook = "PlayerBeforeStartVoice"
    end

    VitrozeFCHookAdd( sHook, sName, fcFunc )
end

hook.Add( "PlayerStartVoice", "Glide.ManageVoice", function( ply, plyIndex )
    if ply ~= LocalPlayer() or voice_loop:GetBool() then
        tPlayersTalking[ply:SteamID()] = true

        AniamtionVoice( true )
    end

    local bShowHUD = hook.Run( "PlayerBeforeStartVoice", ply, plyIndex )
    return bShowHUD
end )

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.

2 participants