From 041bc2d9591a289e0a5c7478d853e216ec0d01ff Mon Sep 17 00:00:00 2001 From: stpM64 Date: Thu, 10 Aug 2023 21:48:28 +0300 Subject: [PATCH 01/27] Advanced Microphone and Speaker work --- lua/entities/gmod_wire_adv_microphone.lua | 82 +++++++++++++++++++++++ lua/entities/gmod_wire_adv_speaker.lua | 63 +++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 lua/entities/gmod_wire_adv_microphone.lua create mode 100644 lua/entities/gmod_wire_adv_speaker.lua diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua new file mode 100644 index 0000000000..3b45d5c60e --- /dev/null +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -0,0 +1,82 @@ +AddCSLuaFile() + +ENT.Type = "anim" +ENT.Base = "base_wire_entity" +ENT.Author = "stpM64" +ENT.PrintName = "Wire Advanced Microphone" +ENT.Purpose = "Listens to sounds, soundscapes and player voices" +-- Named 'advanced' because 'gmod_wire_microphone' exists in Wire Extras +ENT.WireDebugName = "Advanced Microphone" + +-- Note: we listen both serverside and clientside, +-- because some sounds are played clientside only + +-- array(Entity(gmod_wire_adv_microphone)) +-- Array instead of lookup table because sounds are emitted more often than microphones switched on or off, +-- so iteration is more frequent than insertion/removal. +-- Microphone is live when it is active and at least one active speaker connected to it. +_WireLiveMicrophones = _WireLiveMicrophones or {} +local LiveMics = _WireLiveMicrophones + +function ENT:SetupDataTables() + self:NetworkVar("Bool", 0, "Active") + self:NetworkVarNotify("Active",self.OnActiveChanged) +end + +function ENT:Initialize() + if SERVER then + self:PhysicsInit( SOLID_VPHYSICS ) + self:SetMoveType( MOVETYPE_VPHYSICS ) + self:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = WireLib.CreateInputs(self, { + "Active" + }) + end + + -- table(Entity(gmod_wire_adv_speaker), true) + self._activeSpeakers = {} + + -- Callback not called if 'Active' changes right after creation. + self:OnActiveChanged(nil, nil, self:GetActive()) +end + +function ENT:SetLive(isLive) + if self:GetLive() == isLive then return end + + if isLive then + table.insert(LiveMics, self) + else + table.RemoveByValue(LiveMics, self) + end +end + +function ENT:GetLive() + return self:GetActive() and not table.IsEmpty(self._activeSpeakers) +end + +function ENT:OnActiveChanged(_,_,active) + if table.IsEmpty(self._activeSpeakers) then return end + + self:SetLive(active) +end + +function ENT:TriggerInput( name, value ) + if name == "Active" then + self:SetActive(value ~= 0) + end +end + +function ENT:SpeakerActivated(speaker) + if not IsValid(speaker) then return end + + self._activeSpeakers[speaker] = true +end + +function ENT:SpeakerDeactivated(speaker) + self._activeSpeakers[speaker] = nil +end + +hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) + +end) \ No newline at end of file diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua new file mode 100644 index 0000000000..65ed097910 --- /dev/null +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -0,0 +1,63 @@ +AddCSLuaFile() + +ENT.Type = "anim" +ENT.Base = "base_wire_entity" +ENT.Author = "stpM64" +ENT.PrintName = "Wire Advanced Speaker" +ENT.Purpose = "Reproduces sounds, soundscapes and player voices listened by Advanced Microphone" +ENT.WireDebugName = "Advanced Speaker" + +-- TODO: notify of microphone connection when loading from duplication + +function ENT:SetupDataTables() + self:NetworkVar("Bool", 0, "Active") + self:NetworkVar("Entity", 0, "Microphone") + self:NetworkVarNotify("Microphone",self.OnMicrophoneChanged) +end + +if SERVER then + function ENT:Initialize() + self:PhysicsInit( SOLID_VPHYSICS ) + self:SetMoveType( MOVETYPE_VPHYSICS ) + self:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = WireLib.CreateInputs(self, { + "Active", + "Microphone (Must be Wire Advanced Microphone to work) [ENTITY]" + }) + end +end + +function ENT:TriggerInput( name, value ) + if name == "Active" then + self:SetActive(value ~= 0) + elseif name == "Microphone" then + if not (IsValid(value) and value:GetType() == "gmod_wire_adv_microphone") then + value = nil + end + + self:SetMicrophone(value) + end +end + +function ENT:OnMicrophoneChanged(_, oldmic, newmic) + if oldmic ~= newmic then + if IsValid(oldmic) then + oldmic:SpeakerDeactivated(self) + end + + if IsValid(newmic) then + newmic:SpeakerActivated(self) + end + end +end + +function ENT:OnRemove() + local mic = self:GetMicrophone() + if not IsValid(mic) then return end + + timer.Simple(0, function() + if IsValid(self) or not IsValid(mic) then return end + mic:SpeakerDisconSpeakerDeactivatednected(self) + end) +end \ No newline at end of file From f0fc23d8bc014f5aa064d3a3ac6fb8433221d047 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Thu, 10 Aug 2023 22:51:05 +0300 Subject: [PATCH 02/27] Implemented EntityEmitSound hook handling --- lua/entities/gmod_wire_adv_microphone.lua | 95 ++++++++++++++++++++++- lua/entities/gmod_wire_adv_speaker.lua | 9 +++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 3b45d5c60e..54e6211bba 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -1,5 +1,9 @@ AddCSLuaFile() +local MIN_VOLUME = 0.001 +local SPEAKER_VOLUME_COEFF = 0.4 -- Additionally lower volume of sounds from wire speakers to prevent feedback loops +local MAX_DIST_GAIN = 1000 + ENT.Type = "anim" ENT.Base = "base_wire_entity" ENT.Author = "stpM64" @@ -57,7 +61,6 @@ end function ENT:OnActiveChanged(_,_,active) if table.IsEmpty(self._activeSpeakers) then return end - self:SetLive(active) end @@ -70,13 +73,99 @@ end function ENT:SpeakerActivated(speaker) if not IsValid(speaker) then return end + if self:GetActive() then + -- Must be updated before ._activeSpeakers are updated + self:SetLive(true) + end self._activeSpeakers[speaker] = true end +local function table_IsEmptyOrSingle(tbl) + local k1 = next(tbl) + if k1 == nil then return true end + + local k2 = next(tbl, k1) + return k2 == nil +end + function ENT:SpeakerDeactivated(speaker) + if self:GetActive() then + local live = true + do + local spk = self._activeSpeakers + + local k1 = next(spk) + if k1 == nil then -- No active speakers + live = false + else + local k2 = next(spk) + if k2 == nil and k1 == speaker then -- The only active speaker is 'speaker' + live = false + end + end + end + + -- Must be updated before ._activeSpeakers are updated + self:SetLive(live) + end self._activeSpeakers[speaker] = nil end +function ENT:OnRemove() + timer.Simple(0, function() + if IsValid(self) then return end + + self:SetLive(false) + end) +end + hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) - -end) \ No newline at end of file + for _, mic in ipairs(LiveMics) do + mic:HandleEngineSound(snd) + end +end) + +local CVAR_snd_refdb = GetConVar("snd_refdb") +local CVAR_snd_refdist = GetConVar("snd_refdist") + +local function CalculateDistanceGain(dist, sndlevel) + -- See SNDLVL_TO_DIST_MULT in engine/audio/private/snd_dma.cpp + -- See SND_GetGainFromMult in engine/sound_shared.cpp + + local finalsndlevel = CVAR_snd_refdb:GetFloat() - sndlevel + local distMul = math.pow(10, finalsndlevel / 20) / CVAR_snd_refdist:GetFloat() + + local gain = 1/(distMul * dist) + + return math.min(gain, MAX_DIST_GAIN) -- No infinities +end + +function ENT:HandleEngineSound(snd) + local volume = snd.Volume + + if IsValid(snd.Entity) + and snd.Entity:GetType() == "gmod_wire_adv_speaker" + then + volume = volume * SPEAKER_VOLUME_COEFF + end + + local sndlevel = snd.SoundLevel + if sndlevel ~= 0 then + -- Over-256 values are 'reserved for sounds using goldsrc compatibility attenuation' + -- I don't care about correct attenuation for HLSource entities, + -- but I don't want the system to break. + if sndlevel >= 256 then sndlevel = sndlevel - 256 end + + volume = volume * CalculateDistanceGain( + self:GetPos():Distance(snd.Pos), sndlevel) + end + if volume < MIN_VOLUME then return end + + self:ReproduceSound(snd.SoundName, volume, snd.Pitch, snd.DSP) +end + +function ENT:ReproduceSound(snd, vol, pitch, dsp) + for _, speaker in ipairs(_activeSpeakers) do + speaker:ReproduceSound(snd, vol, pitch, dsp) + end +end \ No newline at end of file diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index 65ed097910..7c1d831771 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -8,6 +8,7 @@ ENT.Purpose = "Reproduces sounds, soundscapes and player voices listened by Adva ENT.WireDebugName = "Advanced Speaker" -- TODO: notify of microphone connection when loading from duplication +-- TODO: stop currently played EmitSound sounds on deactivation function ENT:SetupDataTables() self:NetworkVar("Bool", 0, "Active") @@ -60,4 +61,12 @@ function ENT:OnRemove() if IsValid(self) or not IsValid(mic) then return end mic:SpeakerDisconSpeakerDeactivatednected(self) end) +end + +function ENT:ReproduceSound(snd, vol, pitch, dsp) + if not self:GetActive() then return end + + local soundlevel = 75 + + self:EmitSound(snd, soundlevel, pitch, vol, nil, nil, dsp) end \ No newline at end of file From 71421a830b48775c8c46b8f5c56ae3674b3ba7f4 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Thu, 10 Aug 2023 23:03:32 +0300 Subject: [PATCH 03/27] Added player voice reproduction support to microphones/speakers --- lua/entities/gmod_wire_adv_microphone.lua | 39 +++++++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 54e6211bba..4941630e39 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -4,6 +4,8 @@ local MIN_VOLUME = 0.001 local SPEAKER_VOLUME_COEFF = 0.4 -- Additionally lower volume of sounds from wire speakers to prevent feedback loops local MAX_DIST_GAIN = 1000 +local PLAYER_VOICE_MAXDIST_SQR = 250*250 + ENT.Type = "anim" ENT.Base = "base_wire_entity" ENT.Author = "stpM64" @@ -119,12 +121,6 @@ function ENT:OnRemove() end) end -hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) - for _, mic in ipairs(LiveMics) do - mic:HandleEngineSound(snd) - end -end) - local CVAR_snd_refdb = GetConVar("snd_refdb") local CVAR_snd_refdist = GetConVar("snd_refdist") @@ -140,6 +136,15 @@ local function CalculateDistanceGain(dist, sndlevel) return math.min(gain, MAX_DIST_GAIN) -- No infinities end + + + +hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) + for _, mic in ipairs(LiveMics) do + mic:HandleEngineSound(snd) + end +end) + function ENT:HandleEngineSound(snd) local volume = snd.Volume @@ -168,4 +173,24 @@ function ENT:ReproduceSound(snd, vol, pitch, dsp) for _, speaker in ipairs(_activeSpeakers) do speaker:ReproduceSound(snd, vol, pitch, dsp) end -end \ No newline at end of file +end + +hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, talker) + local talkerPos = talker:GetPos() + local listenerPos = listener:GetPos() + + local speakers = {} + -- Note: any given speaker can only be connected to one microphone, + -- so this loops can be considered O(nMic), not O(nMic*nSpeaker) + for _, mic in ipairs(LiveMics) do + if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then continue end + + for _, speaker in ipairs(mic._activeSpeakers) do + if not IsValid(speaker) then continue end + + if speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR then + return true, false -- Can hear, not in 3D + end + end + end +end) \ No newline at end of file From bd9b711bb40abb3489a81be6b14e76f3da4ec106 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Thu, 10 Aug 2023 23:16:06 +0300 Subject: [PATCH 04/27] Microphone/speaker fixes --- lua/entities/gmod_wire_adv_microphone.lua | 12 +++++++++++- lua/entities/gmod_wire_adv_speaker.lua | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 4941630e39..f15e1fffdf 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -50,6 +50,8 @@ end function ENT:SetLive(isLive) if self:GetLive() == isLive then return end + self:AddEFlags(EFL_FORCE_CHECK_TRANSMIT) + if isLive then table.insert(LiveMics, self) else @@ -57,6 +59,10 @@ function ENT:SetLive(isLive) end end +function ENT:UpdateTransmitState() + return Either(self:GetLive(), TRANSMIT_ALWAYS, TRANSMIT_PVS) +end + function ENT:GetLive() return self:GetActive() and not table.IsEmpty(self._activeSpeakers) end @@ -193,4 +199,8 @@ hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, t end end end -end) \ No newline at end of file +end) + +-- TODO: hook into sound.Play +-- TODO: hook into sound.PlayFile +-- TODO: hook into sound.PlayURL \ No newline at end of file diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index 7c1d831771..daecdcc3da 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -7,7 +7,6 @@ ENT.PrintName = "Wire Advanced Speaker" ENT.Purpose = "Reproduces sounds, soundscapes and player voices listened by Advanced Microphone" ENT.WireDebugName = "Advanced Speaker" --- TODO: notify of microphone connection when loading from duplication -- TODO: stop currently played EmitSound sounds on deactivation function ENT:SetupDataTables() @@ -26,6 +25,8 @@ if SERVER then "Active", "Microphone (Must be Wire Advanced Microphone to work) [ENTITY]" }) + + self:OnMicrophoneChanged(nil, nil, self:GetMicrophone()) end end From 06ad2716c7461c6fe992dd4fc27a4225b477f5b7 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Thu, 10 Aug 2023 23:29:47 +0300 Subject: [PATCH 05/27] Linter pass --- lua/entities/gmod_wire_adv_microphone.lua | 21 ++++++--------------- lua/entities/gmod_wire_adv_speaker.lua | 2 +- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index f15e1fffdf..7890b220ba 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -88,14 +88,6 @@ function ENT:SpeakerActivated(speaker) self._activeSpeakers[speaker] = true end -local function table_IsEmptyOrSingle(tbl) - local k1 = next(tbl) - if k1 == nil then return true end - - local k2 = next(tbl, k1) - return k2 == nil -end - function ENT:SpeakerDeactivated(speaker) if self:GetActive() then local live = true @@ -134,7 +126,7 @@ local function CalculateDistanceGain(dist, sndlevel) -- See SNDLVL_TO_DIST_MULT in engine/audio/private/snd_dma.cpp -- See SND_GetGainFromMult in engine/sound_shared.cpp - local finalsndlevel = CVAR_snd_refdb:GetFloat() - sndlevel + local finalsndlevel = CVAR_snd_refdb:GetFloat() - sndlevel local distMul = math.pow(10, finalsndlevel / 20) / CVAR_snd_refdist:GetFloat() local gain = 1/(distMul * dist) @@ -154,9 +146,9 @@ end) function ENT:HandleEngineSound(snd) local volume = snd.Volume - if IsValid(snd.Entity) - and snd.Entity:GetType() == "gmod_wire_adv_speaker" - then + if IsValid(snd.Entity) + and snd.Entity:GetType() == "gmod_wire_adv_speaker" + then volume = volume * SPEAKER_VOLUME_COEFF end @@ -166,7 +158,7 @@ function ENT:HandleEngineSound(snd) -- I don't care about correct attenuation for HLSource entities, -- but I don't want the system to break. if sndlevel >= 256 then sndlevel = sndlevel - 256 end - + volume = volume * CalculateDistanceGain( self:GetPos():Distance(snd.Pos), sndlevel) end @@ -185,7 +177,6 @@ hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, t local talkerPos = talker:GetPos() local listenerPos = listener:GetPos() - local speakers = {} -- Note: any given speaker can only be connected to one microphone, -- so this loops can be considered O(nMic), not O(nMic*nSpeaker) for _, mic in ipairs(LiveMics) do @@ -193,7 +184,7 @@ hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, t for _, speaker in ipairs(mic._activeSpeakers) do if not IsValid(speaker) then continue end - + if speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR then return true, false -- Can hear, not in 3D end diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index daecdcc3da..db33e5dbc9 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -47,7 +47,7 @@ function ENT:OnMicrophoneChanged(_, oldmic, newmic) if IsValid(oldmic) then oldmic:SpeakerDeactivated(self) end - + if IsValid(newmic) then newmic:SpeakerActivated(self) end From 161982c65543c76f4375de26e6e7ee5a97a444e0 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Thu, 10 Aug 2023 23:57:44 +0300 Subject: [PATCH 06/27] Added duplication support, added tools --- lua/entities/gmod_wire_adv_microphone.lua | 4 +++- lua/entities/gmod_wire_adv_speaker.lua | 4 +++- lua/wire/stools/adv_microphone.lua | 22 ++++++++++++++++++++ lua/wire/stools/adv_speaker.lua | 25 +++++++++++++++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 lua/wire/stools/adv_microphone.lua create mode 100644 lua/wire/stools/adv_speaker.lua diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 7890b220ba..2f73208691 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -194,4 +194,6 @@ end) -- TODO: hook into sound.Play -- TODO: hook into sound.PlayFile --- TODO: hook into sound.PlayURL \ No newline at end of file +-- TODO: hook into sound.PlayURL + +duplicator.RegisterEntityClass("gmod_wire_adv_microphone", WireLib.MakeWireEnt, "Data") \ No newline at end of file diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index db33e5dbc9..ba2dfd5c57 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -70,4 +70,6 @@ function ENT:ReproduceSound(snd, vol, pitch, dsp) local soundlevel = 75 self:EmitSound(snd, soundlevel, pitch, vol, nil, nil, dsp) -end \ No newline at end of file +end + +duplicator.RegisterEntityClass("gmod_wire_adv_speaker", WireLib.MakeWireEnt, "Data") \ No newline at end of file diff --git a/lua/wire/stools/adv_microphone.lua b/lua/wire/stools/adv_microphone.lua new file mode 100644 index 0000000000..ea49dc3a2c --- /dev/null +++ b/lua/wire/stools/adv_microphone.lua @@ -0,0 +1,22 @@ +WireToolSetup.setCategory( "Other/Sound" ) +WireToolSetup.open( "adv_microphone", "Adv. Microphone", "gmod_wire_adv_microphone", nil, "Adv. Microphones" ) + +if CLIENT then + language.Add("tool.wire_adv_microphone.name", "Advanced Microphone") + language.Add("tool.wire_adv_microphone.desc", "Places Advanced Microphones") + language.Add("tool.wire_adv_microphone.0", "Create or update microphone") + + -- TODO: icon? +end + +WireToolSetup.BaseLang() +WireToolSetup.SetupMax( 20 ) + +TOOL.ClientConVar = { + model = "models/jaanus/wiretool/wiretool_siren.mdl", +} + + +function TOOL.BuildCPanel(panel) + ModelPlug_AddToCPanel(panel, "Misc_Tools", "wire_adv_microphone", true) +end \ No newline at end of file diff --git a/lua/wire/stools/adv_speaker.lua b/lua/wire/stools/adv_speaker.lua new file mode 100644 index 0000000000..6f7dda3e5a --- /dev/null +++ b/lua/wire/stools/adv_speaker.lua @@ -0,0 +1,25 @@ +WireToolSetup.setCategory( "Other/Sound" ) +WireToolSetup.open( "adv_speaker", "Adv. Speaker", "gmod_wire_adv_speaker", nil, "Adv. Speakers" ) + +if CLIENT then + language.Add("tool.wire_adv_speaker.name", "Advanced Speaker") + language.Add("tool.wire_adv_speaker.desc", "Places Advanced Speakers") + language.Add("tool.wire_adv_speaker.0", "Create or update speaker") + + WireToolSetup.setToolMenuIcon( "icon16/sound.png" ) +end + +WireToolSetup.BaseLang() +WireToolSetup.SetupMax( 20 ) + +if SERVER then + ModelPlug_Register("speaker") +end + +TOOL.ClientConVar = { + model = "models/cheeze/wires/speaker.mdl", +} + +function TOOL.BuildCPanel(panel) + ModelPlug_AddToCPanel(panel, "speaker", "wire_adv_speaker", true) +end \ No newline at end of file From 4121d718be0d41a89cc85494aca1137222e7414f Mon Sep 17 00:00:00 2001 From: stpM64 Date: Fri, 11 Aug 2023 00:39:42 +0300 Subject: [PATCH 07/27] Adv. Microphone / Speaker fixes --- lua/entities/gmod_wire_adv_microphone.lua | 41 +++++++++++++---------- lua/entities/gmod_wire_adv_speaker.lua | 22 +++++++++--- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 2f73208691..f7a6c57bf1 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -1,7 +1,6 @@ AddCSLuaFile() -local MIN_VOLUME = 0.001 -local SPEAKER_VOLUME_COEFF = 0.4 -- Additionally lower volume of sounds from wire speakers to prevent feedback loops +local MIN_VOLUME = 0.02 local MAX_DIST_GAIN = 1000 local PLAYER_VOICE_MAXDIST_SQR = 250*250 @@ -47,10 +46,13 @@ function ENT:Initialize() self:OnActiveChanged(nil, nil, self:GetActive()) end -function ENT:SetLive(isLive) - if self:GetLive() == isLive then return end - - self:AddEFlags(EFL_FORCE_CHECK_TRANSMIT) +local function Mic_SetLive(self, isLive) + if not IsValid(self) then + isLive = false + else + if self:GetLive() == isLive then return end + self:AddEFlags(EFL_FORCE_CHECK_TRANSMIT) + end if isLive then table.insert(LiveMics, self) @@ -59,6 +61,8 @@ function ENT:SetLive(isLive) end end +ENT.SetLive = Mic_SetLive + function ENT:UpdateTransmitState() return Either(self:GetLive(), TRANSMIT_ALWAYS, TRANSMIT_PVS) end @@ -115,7 +119,7 @@ function ENT:OnRemove() timer.Simple(0, function() if IsValid(self) then return end - self:SetLive(false) + Mic_SetLive(self, false) end) end @@ -139,36 +143,39 @@ end hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) for _, mic in ipairs(LiveMics) do - mic:HandleEngineSound(snd) + if IsValid(mic) then + mic:HandleEngineSound(snd) + end end end) function ENT:HandleEngineSound(snd) local volume = snd.Volume + local pitch = snd.Pitch - if IsValid(snd.Entity) - and snd.Entity:GetType() == "gmod_wire_adv_speaker" - then - volume = volume * SPEAKER_VOLUME_COEFF - end + -- Disable feedback loops + if IsValid(snd.Entity) and snd.Entity:GetClass() == "gmod_wire_adv_speaker" then return end local sndlevel = snd.SoundLevel - if sndlevel ~= 0 then + local pos = snd.Pos + if sndlevel ~= 0 and pos ~= nil then -- Over-256 values are 'reserved for sounds using goldsrc compatibility attenuation' -- I don't care about correct attenuation for HLSource entities, -- but I don't want the system to break. if sndlevel >= 256 then sndlevel = sndlevel - 256 end volume = volume * CalculateDistanceGain( - self:GetPos():Distance(snd.Pos), sndlevel) + self:GetPos():Distance(pos), sndlevel) end + if volume < MIN_VOLUME then return end + if volume > 1 then volume = 1 end - self:ReproduceSound(snd.SoundName, volume, snd.Pitch, snd.DSP) + self:ReproduceSound(snd.SoundName, volume, pitch, snd.DSP) end function ENT:ReproduceSound(snd, vol, pitch, dsp) - for _, speaker in ipairs(_activeSpeakers) do + for speaker in pairs(self._activeSpeakers) do speaker:ReproduceSound(snd, vol, pitch, dsp) end end diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index ba2dfd5c57..dac368a397 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -11,6 +11,7 @@ ENT.WireDebugName = "Advanced Speaker" function ENT:SetupDataTables() self:NetworkVar("Bool", 0, "Active") + self:NetworkVarNotify("Active", self.OnActiveChanged) self:NetworkVar("Entity", 0, "Microphone") self:NetworkVarNotify("Microphone",self.OnMicrophoneChanged) end @@ -34,7 +35,7 @@ function ENT:TriggerInput( name, value ) if name == "Active" then self:SetActive(value ~= 0) elseif name == "Microphone" then - if not (IsValid(value) and value:GetType() == "gmod_wire_adv_microphone") then + if not (IsValid(value) and value:GetClass() == "gmod_wire_adv_microphone") then value = nil end @@ -42,8 +43,21 @@ function ENT:TriggerInput( name, value ) end end +function ENT:OnActiveChanged(_, oldactive, active) + if oldactive == active then return end + + local mic = self:GetMicrophone() + if not IsValid(mic) then return end + + if active then + mic:SpeakerActivated(self) + else + mic:SpeakerDeactivated(self) + end +end + function ENT:OnMicrophoneChanged(_, oldmic, newmic) - if oldmic ~= newmic then + if self:GetActive() and oldmic ~= newmic then if IsValid(oldmic) then oldmic:SpeakerDeactivated(self) end @@ -60,7 +74,7 @@ function ENT:OnRemove() timer.Simple(0, function() if IsValid(self) or not IsValid(mic) then return end - mic:SpeakerDisconSpeakerDeactivatednected(self) + mic:SpeakerDeactivated(self) end) end @@ -68,7 +82,7 @@ function ENT:ReproduceSound(snd, vol, pitch, dsp) if not self:GetActive() then return end local soundlevel = 75 - + print(snd, vol, pitch, dsp) self:EmitSound(snd, soundlevel, pitch, vol, nil, nil, dsp) end From 1dd6c9aa85b391cb47dfc07b382ee17522fb9949 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Fri, 11 Aug 2023 01:55:02 +0300 Subject: [PATCH 08/27] Added WireLib.Sound.IsLooped and WireLib.Sound.StripPrefix, added temp fix for speaker playing looping sound --- lua/autorun/wire_load.lua | 2 + lua/entities/gmod_wire_adv_microphone.lua | 4 +- lua/entities/gmod_wire_adv_speaker.lua | 8 ++- lua/wire/soundlib.lua | 83 +++++++++++++++++++++++ 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 lua/wire/soundlib.lua diff --git a/lua/autorun/wire_load.lua b/lua/autorun/wire_load.lua index b67c790548..a6d1e79a91 100644 --- a/lua/autorun/wire_load.lua +++ b/lua/autorun/wire_load.lua @@ -36,6 +36,7 @@ if SERVER then AddCSLuaFile("wire/flir.lua") AddCSLuaFile("wire/von.lua") AddCSLuaFile("wire/sh_modelplug.lua") + AddCSLuaFile("wire/soundlib.lua") -- client includes AddCSLuaFile("wire/client/cl_wirelib.lua") @@ -83,6 +84,7 @@ include("wire/timedpairs.lua") include("wire/default_data_decompressor.lua") include("wire/flir.lua") include("wire/von.lua") +include("wire/soundlib.lua") -- server includes if SERVER then diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index f7a6c57bf1..393e15b5ff 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -55,7 +55,9 @@ local function Mic_SetLive(self, isLive) end if isLive then - table.insert(LiveMics, self) + if not table.HasValue(LiveMics, self) then + table.insert(LiveMics, self) + end else table.RemoveByValue(LiveMics, self) end diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index dac368a397..28a6715749 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -80,9 +80,15 @@ end function ENT:ReproduceSound(snd, vol, pitch, dsp) if not self:GetActive() then return end + print(snd, vol, pitch, dsp) + + if WireLib.Sound.IsLooped(WireLib.Sound.StripPrefix(snd)) then + print("--looped") + return + end + local soundlevel = 75 - print(snd, vol, pitch, dsp) self:EmitSound(snd, soundlevel, pitch, vol, nil, nil, dsp) end diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua new file mode 100644 index 0000000000..fcb760daea --- /dev/null +++ b/lua/wire/soundlib.lua @@ -0,0 +1,83 @@ +WireLib.Sound = WireLib.Sound or {} +local lib = WireLib.Sound + +local LoopedCache = {} + +local function Riff_ReadChunkHeader(fil) + local id = fil:Read(4) + local content_len = fil:ReadULong() + local next_chunk_offs = content_len + if next_chunk_offs % 2 == 1 then next_chunk_offs = next_chunk_offs + 1 end + print(id, content_len, next_chunk_offs) + + return id, content_len, next_chunk_offs +end + +local function WavIsLooped_Impl(path) + local fil = file.Open(path, "r", "GAME") + if fil == nil then + return false + end + + local id, len, nextoffs = Riff_ReadChunkHeader(fil) + if id ~= "RIFF" then return false end -- Invalid header + local id, len, nextoffs = Riff_ReadChunkHeader(fil) + if id ~= "WAVE" then return false end -- Invalid second header + + while true do + local id, len, nextoffs = Riff_ReadChunkHeader(fil) + if id == "cue " then break end + + fil:Skip(nextoffs) + end + + local cue_count = fil:ReadULong() + fil:Close() + + --[[ + // assume that the cue chunk stored in the wave is the start of the loop + // assume only one cue chunk, UNDONE: Test this assumption here? + cueCount = walk.ChunkReadInt(); + if ( cueCount > 0 ) + { + walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) ); + m_loopStart = LittleLong( cue_chunk.dwSampleOffset ); + } + ]] + + return cue_count ~= 0 +end + +local function WavIsLooped(path) + if LoopedCache[path] ~= nil then return LoopedCache[path] end + + local looped = WavIsLooped_Impl(path) + LoopedCache[path] = looped + return looped +end + +function lib.IsLooped(path) + local ext = string.GetExtensionFromFilename(path) + if ext == "wav" then + return WavIsLooped(path) + else + return false -- MP3s are not loopable + end +end + +local PREFIX_CHARS = {"*","#","@",">","<","^",")","(","}","$","!","?"} +local PREFIX_REGEX +do + local chars_escaped = {} + for i, ch in ipairs(PREFIX_CHARS) do + chars_escaped[i] = string.PatternSafe(ch) + end + + PREFIX_REGEX = "^["..table.concat(chars_escaped).."]+" +end + + + +function lib.StripPrefix(path) + return string.gsub(path, PREFIX_REGEX, "") +end \ No newline at end of file From cef9f6c6403ed00818bd5aca7d9945f1ea5ba1a8 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Fri, 11 Aug 2023 17:01:13 +0300 Subject: [PATCH 09/27] Fixed WireLib.Sound.IsLooped --- lua/wire/soundlib.lua | 58 ++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index fcb760daea..521f886b7a 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -6,11 +6,11 @@ local LoopedCache = {} local function Riff_ReadChunkHeader(fil) local id = fil:Read(4) local content_len = fil:ReadULong() - local next_chunk_offs = content_len - if next_chunk_offs % 2 == 1 then next_chunk_offs = next_chunk_offs + 1 end - print(id, content_len, next_chunk_offs) + content_len = content_len + bit.band(content_len, 1) - return id, content_len, next_chunk_offs + print("> header", id, content_len) + + return id, content_len end local function WavIsLooped_Impl(path) @@ -19,44 +19,52 @@ local function WavIsLooped_Impl(path) return false end - local id, len, nextoffs = Riff_ReadChunkHeader(fil) + local id, _ = Riff_ReadChunkHeader(fil) if id ~= "RIFF" then return false end -- Invalid header - local id, len, nextoffs = Riff_ReadChunkHeader(fil) - if id ~= "WAVE" then return false end -- Invalid second header + if fil:Read(4) ~= "WAVE" then return false end -- Invalid second header + + local resultid while true do - local id, len, nextoffs = Riff_ReadChunkHeader(fil) - if id == "cue " then break end + local id, len = Riff_ReadChunkHeader(fil) + if id == "cue " or id == "smpl" then resultid = id break end + + local p1 = fil:Tell() + local pnext = p1 + len + + if pnext == fil:Size() then return false end -- End of file + fil:Seek(pnext) + end - fil:Skip(nextoffs) + if resultid == "cue " then + local cue_count = fil:ReadULong() + print("Cue count", cue_count) + fil:Close() + + return cue_count ~= 0 + elseif resultid == "smpl" then + fil:Skip(7*4) + local sampler_count = fil:ReadULong() + print("Sampler count", sampler_count) + fil:Close() + + return sampler_count ~= 0 end - local cue_count = fil:ReadULong() - fil:Close() - - --[[ - // assume that the cue chunk stored in the wave is the start of the loop - // assume only one cue chunk, UNDONE: Test this assumption here? - cueCount = walk.ChunkReadInt(); - if ( cueCount > 0 ) - { - walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) ); - m_loopStart = LittleLong( cue_chunk.dwSampleOffset ); - } - ]] - - return cue_count ~= 0 + end local function WavIsLooped(path) if LoopedCache[path] ~= nil then return LoopedCache[path] end local looped = WavIsLooped_Impl(path) + print(path, looped) LoopedCache[path] = looped return looped end function lib.IsLooped(path) + path = "sound/"..path local ext = string.GetExtensionFromFilename(path) if ext == "wav" then return WavIsLooped(path) From b8459adad225e8088e01d128148c8038c66bc4be Mon Sep 17 00:00:00 2001 From: stpM64 Date: Fri, 11 Aug 2023 17:08:22 +0300 Subject: [PATCH 10/27] Code style fixes --- lua/entities/gmod_wire_adv_microphone.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 393e15b5ff..40afca4977 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -66,7 +66,7 @@ end ENT.SetLive = Mic_SetLive function ENT:UpdateTransmitState() - return Either(self:GetLive(), TRANSMIT_ALWAYS, TRANSMIT_PVS) + return self:GetLive() and TRANSMIT_ALWAYS or TRANSMIT_PVS end function ENT:GetLive() @@ -189,15 +189,16 @@ hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, t -- Note: any given speaker can only be connected to one microphone, -- so this loops can be considered O(nMic), not O(nMic*nSpeaker) for _, mic in ipairs(LiveMics) do - if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then continue end + if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then goto mic_next end for _, speaker in ipairs(mic._activeSpeakers) do - if not IsValid(speaker) then continue end - - if speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR then + if IsValid(speaker) and + speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR + then return true, false -- Can hear, not in 3D end end + ::mic_next:: end end) From 95bdfe78862c0378bd03720a0c5d148440e69f9e Mon Sep 17 00:00:00 2001 From: stpM64 Date: Sat, 12 Aug 2023 13:44:17 +0300 Subject: [PATCH 11/27] Removed debug log in soundlib.lua --- lua/wire/soundlib.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index 521f886b7a..1164a83bbc 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -8,8 +8,6 @@ local function Riff_ReadChunkHeader(fil) local content_len = fil:ReadULong() content_len = content_len + bit.band(content_len, 1) - print("> header", id, content_len) - return id, content_len end @@ -38,14 +36,12 @@ local function WavIsLooped_Impl(path) if resultid == "cue " then local cue_count = fil:ReadULong() - print("Cue count", cue_count) fil:Close() return cue_count ~= 0 elseif resultid == "smpl" then fil:Skip(7*4) local sampler_count = fil:ReadULong() - print("Sampler count", sampler_count) fil:Close() return sampler_count ~= 0 From 93d65433c992c6fcb28727b766e5840cd4a52120 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Tue, 12 Sep 2023 12:57:50 +0300 Subject: [PATCH 12/27] Added sound.Play listening support --- lua/entities/gmod_wire_adv_microphone.lua | 32 +++++++++++++++-------- lua/entities/gmod_wire_adv_speaker.lua | 12 ++++++--- lua/wire/soundlib.lua | 7 +++++ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 40afca4977..7eea9548fe 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -146,20 +146,31 @@ end hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) for _, mic in ipairs(LiveMics) do if IsValid(mic) then - mic:HandleEngineSound(snd) + mic:HandleSound( + snd.SoundName, snd.Volume, snd.Pitch, snd.SoundLevel, + snd.Entity, snd.Pos, + "EmitSound" + ) end end end) -function ENT:HandleEngineSound(snd) - local volume = snd.Volume - local pitch = snd.Pitch +hook.Add("Wire_SoundPlay", "Wire.AdvMicrophone", function(name, pos, level, pitch, volume) + for _, mic in ipairs(LiveMics) do + if IsValid(mic) then + mic:HandleSound( + name, volume, pitch, level + nil --[[entity]], pos, + "sound.Play" + ) + end + end +end) +function ENT:HandleSound(sndname, volume, pitch, sndlevel, entity, pos, dsp, emittype) -- Disable feedback loops - if IsValid(snd.Entity) and snd.Entity:GetClass() == "gmod_wire_adv_speaker" then return end + if IsValid(entity) and entity:GetClass() == "gmod_wire_adv_speaker" then return end - local sndlevel = snd.SoundLevel - local pos = snd.Pos if sndlevel ~= 0 and pos ~= nil then -- Over-256 values are 'reserved for sounds using goldsrc compatibility attenuation' -- I don't care about correct attenuation for HLSource entities, @@ -173,12 +184,12 @@ function ENT:HandleEngineSound(snd) if volume < MIN_VOLUME then return end if volume > 1 then volume = 1 end - self:ReproduceSound(snd.SoundName, volume, pitch, snd.DSP) + self:ReproduceSound(sndname, volume, pitch, snd.DSP, emittype) end -function ENT:ReproduceSound(snd, vol, pitch, dsp) +function ENT:ReproduceSound(snd, vol, pitch, dsp, emittype) for speaker in pairs(self._activeSpeakers) do - speaker:ReproduceSound(snd, vol, pitch, dsp) + speaker:ReproduceSound(snd, vol, pitch, dsp, emittype) end end @@ -202,7 +213,6 @@ hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, t end end) --- TODO: hook into sound.Play -- TODO: hook into sound.PlayFile -- TODO: hook into sound.PlayURL diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index 28a6715749..0b4bd0435c 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -78,9 +78,9 @@ function ENT:OnRemove() end) end -function ENT:ReproduceSound(snd, vol, pitch, dsp) +function ENT:ReproduceSound(snd, vol, pitch, dsp, emittype) if not self:GetActive() then return end - print(snd, vol, pitch, dsp) + print(snd, vol, pitch, dsp, emittype) if WireLib.Sound.IsLooped(WireLib.Sound.StripPrefix(snd)) then print("--looped") @@ -89,7 +89,13 @@ function ENT:ReproduceSound(snd, vol, pitch, dsp) local soundlevel = 75 - self:EmitSound(snd, soundlevel, pitch, vol, nil, nil, dsp) + if emittype == "EmitSound" then + self:EmitSound(snd, soundlevel, pitch, vol, nil, nil, dsp) + elseif emittype == "sound.Play" then + sound.Play_NoWireHook(snd, pos, soundlevel, pitch, vol) + else + ErrorNoHalt("Invalid emittype: ", emittype,"\n --sound ",snd) + end end duplicator.RegisterEntityClass("gmod_wire_adv_speaker", WireLib.MakeWireEnt, "Data") \ No newline at end of file diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index 1164a83bbc..4c03c65b4f 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -84,4 +84,11 @@ end function lib.StripPrefix(path) return string.gsub(path, PREFIX_REGEX, "") +end + +sound.Play_NoWireHook = sound.Play_NoWireHook or sound.Play +function sound.Play(...) + hook.Run("Wire_SoundPlay", ...) + + sound.Play_NoWireHook(...) end \ No newline at end of file From e09681a7274144d65727ead30ba956edf07c52d3 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Tue, 12 Sep 2023 13:03:09 +0300 Subject: [PATCH 13/27] Lint fixes --- lua/entities/gmod_wire_adv_microphone.lua | 2 +- lua/wire/soundlib.lua | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 7eea9548fe..b95fdd2739 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -159,7 +159,7 @@ hook.Add("Wire_SoundPlay", "Wire.AdvMicrophone", function(name, pos, level, pitc for _, mic in ipairs(LiveMics) do if IsValid(mic) then mic:HandleSound( - name, volume, pitch, level + name, volume, pitch, level, nil --[[entity]], pos, "sound.Play" ) diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index 4c03c65b4f..5cbf7c9753 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -37,17 +37,17 @@ local function WavIsLooped_Impl(path) if resultid == "cue " then local cue_count = fil:ReadULong() fil:Close() - + return cue_count ~= 0 elseif resultid == "smpl" then fil:Skip(7*4) local sampler_count = fil:ReadULong() fil:Close() - + return sampler_count ~= 0 end - + end local function WavIsLooped(path) From d99ce259db62847ebcbf1e29ef7903a147a140a1 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Tue, 3 Oct 2023 00:06:16 +0300 Subject: [PATCH 14/27] Removed debug logging --- lua/entities/gmod_wire_adv_microphone.lua | 9 +++++---- lua/entities/gmod_wire_adv_speaker.lua | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index b95fdd2739..63eeecd136 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -148,7 +148,7 @@ hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) if IsValid(mic) then mic:HandleSound( snd.SoundName, snd.Volume, snd.Pitch, snd.SoundLevel, - snd.Entity, snd.Pos, + snd.Entity, snd.Pos, snd.DSP, "EmitSound" ) end @@ -160,7 +160,7 @@ hook.Add("Wire_SoundPlay", "Wire.AdvMicrophone", function(name, pos, level, pitc if IsValid(mic) then mic:HandleSound( name, volume, pitch, level, - nil --[[entity]], pos, + nil --[[entity]], pos, 0 --[[DSP]], "sound.Play" ) end @@ -170,7 +170,7 @@ end) function ENT:HandleSound(sndname, volume, pitch, sndlevel, entity, pos, dsp, emittype) -- Disable feedback loops if IsValid(entity) and entity:GetClass() == "gmod_wire_adv_speaker" then return end - + if sndlevel ~= 0 and pos ~= nil then -- Over-256 values are 'reserved for sounds using goldsrc compatibility attenuation' -- I don't care about correct attenuation for HLSource entities, @@ -184,7 +184,7 @@ function ENT:HandleSound(sndname, volume, pitch, sndlevel, entity, pos, dsp, emi if volume < MIN_VOLUME then return end if volume > 1 then volume = 1 end - self:ReproduceSound(sndname, volume, pitch, snd.DSP, emittype) + self:ReproduceSound(sndname, volume, pitch, dsp, emittype) end function ENT:ReproduceSound(snd, vol, pitch, dsp, emittype) @@ -216,4 +216,5 @@ end) -- TODO: hook into sound.PlayFile -- TODO: hook into sound.PlayURL + duplicator.RegisterEntityClass("gmod_wire_adv_microphone", WireLib.MakeWireEnt, "Data") \ No newline at end of file diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index 0b4bd0435c..8df443537c 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -80,10 +80,8 @@ end function ENT:ReproduceSound(snd, vol, pitch, dsp, emittype) if not self:GetActive() then return end - print(snd, vol, pitch, dsp, emittype) if WireLib.Sound.IsLooped(WireLib.Sound.StripPrefix(snd)) then - print("--looped") return end From 40f6a7d56eeef8c2c7a6dea9e5d42cd3df34a703 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Tue, 2 Jan 2024 22:47:49 +0300 Subject: [PATCH 15/27] Fixed error on microphone dupe pasting --- lua/entities/gmod_wire_adv_microphone.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 63eeecd136..809467909a 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -25,7 +25,7 @@ local LiveMics = _WireLiveMicrophones function ENT:SetupDataTables() self:NetworkVar("Bool", 0, "Active") - self:NetworkVarNotify("Active",self.OnActiveChanged) + self:NetworkVarNotify("Active", self.OnActiveChanged) end function ENT:Initialize() @@ -74,6 +74,7 @@ function ENT:GetLive() end function ENT:OnActiveChanged(_,_,active) + if self._activeSpeakers == nil then return end -- This happens on duplication restoration if table.IsEmpty(self._activeSpeakers) then return end self:SetLive(active) end From 3c84dcc48e32f5573d4d6be6505939d9aacd5ec3 Mon Sep 17 00:00:00 2001 From: Flarky55 Date: Wed, 3 Jan 2024 02:29:15 +0600 Subject: [PATCH 16/27] Fixed `PlayerCanHearPlayersVoice` hook --- lua/entities/gmod_wire_adv_microphone.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 63eeecd136..e65db5069b 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -202,7 +202,7 @@ hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, t for _, mic in ipairs(LiveMics) do if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then goto mic_next end - for _, speaker in ipairs(mic._activeSpeakers) do + for speaker in pairs(mic._activeSpeakers) do if IsValid(speaker) and speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR then From d6f7b21a5479b87fa55e74e88e289eb0e1e40147 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Fri, 5 Jan 2024 17:43:55 +0300 Subject: [PATCH 17/27] Linter pass, removed debug logging, small comment improvement --- lua/entities/gmod_wire_adv_microphone.lua | 10 +++++----- lua/wire/soundlib.lua | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 629508eca9..25374da961 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -49,7 +49,7 @@ end local function Mic_SetLive(self, isLive) if not IsValid(self) then isLive = false - else + else if self:GetLive() == isLive then return end self:AddEFlags(EFL_FORCE_CHECK_TRANSMIT) end @@ -169,9 +169,9 @@ hook.Add("Wire_SoundPlay", "Wire.AdvMicrophone", function(name, pos, level, pitc end) function ENT:HandleSound(sndname, volume, pitch, sndlevel, entity, pos, dsp, emittype) - -- Disable feedback loops + -- Prevent feedback loops if IsValid(entity) and entity:GetClass() == "gmod_wire_adv_speaker" then return end - + if sndlevel ~= 0 and pos ~= nil then -- Over-256 values are 'reserved for sounds using goldsrc compatibility attenuation' -- I don't care about correct attenuation for HLSource entities, @@ -204,8 +204,8 @@ hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, t if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then goto mic_next end for speaker in pairs(mic._activeSpeakers) do - if IsValid(speaker) and - speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR + if IsValid(speaker) and + speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR then return true, false -- Can hear, not in 3D end diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index 5cbf7c9753..d6b77b0d27 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -13,7 +13,7 @@ end local function WavIsLooped_Impl(path) local fil = file.Open(path, "r", "GAME") - if fil == nil then + if fil == nil then return false end @@ -54,7 +54,6 @@ local function WavIsLooped(path) if LoopedCache[path] ~= nil then return LoopedCache[path] end local looped = WavIsLooped_Impl(path) - print(path, looped) LoopedCache[path] = looped return looped end From 6ca401f8ceeb86ecb62e5bfd0d812423a82b1113 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Sat, 6 Jan 2024 17:16:44 +0300 Subject: [PATCH 18/27] Added some error handling --- lua/wire/soundlib.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index d6b77b0d27..3c6648fecb 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -6,6 +6,8 @@ local LoopedCache = {} local function Riff_ReadChunkHeader(fil) local id = fil:Read(4) local content_len = fil:ReadULong() + if content_len == nil then return nil, nil end + content_len = content_len + bit.band(content_len, 1) return id, content_len @@ -26,6 +28,10 @@ local function WavIsLooped_Impl(path) while true do local id, len = Riff_ReadChunkHeader(fil) if id == "cue " or id == "smpl" then resultid = id break end + if id == nil then + ErrorNoHaltWithStack("WavIsLooped_Impl: Can't analyze file ", path) + return false + end -- Some error local p1 = fil:Tell() local pnext = p1 + len From cf9011e4d49f8c89416a880d012d421d1fcd3f5e Mon Sep 17 00:00:00 2001 From: stpM64 Date: Sat, 6 Jan 2024 19:12:12 +0300 Subject: [PATCH 19/27] Fixed Wire_SoundPlay hook not replacing nils with default values --- lua/wire/soundlib.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index 3c6648fecb..a45a337ff0 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -92,8 +92,8 @@ function lib.StripPrefix(path) end sound.Play_NoWireHook = sound.Play_NoWireHook or sound.Play -function sound.Play(...) - hook.Run("Wire_SoundPlay", ...) +function sound.Play(snd, pos, level, pitch, volume, ...) + hook.Run("Wire_SoundPlay", snd, pos, level or 75, pitch or 100, volume or 1, ...) - sound.Play_NoWireHook(...) + sound.Play_NoWireHook(snd, pos, level, pitch, volume, ...) end \ No newline at end of file From 94ded385b18f40fd237b79c52871faf60aa7d786 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Sat, 6 Jan 2024 20:34:31 +0300 Subject: [PATCH 20/27] Voice transmission optimization --- lua/entities/gmod_wire_adv_microphone.lua | 48 +++++++++++++---------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 25374da961..ec2e1025a9 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -46,6 +46,26 @@ function ENT:Initialize() self:OnActiveChanged(nil, nil, self:GetActive()) end +local function PlayerCanHearPlayersVoice_Hook(listener, talker) + local talkerPos = talker:GetPos() + local listenerPos = listener:GetPos() + + -- Note: any given speaker can only be connected to one microphone, + -- so this loops can be considered O(nMic), not O(nMic*nSpeaker) + for _, mic in ipairs(LiveMics) do + if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then goto mic_next end + + for speaker in pairs(mic._activeSpeakers) do + if IsValid(speaker) and + speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR + then + return true, false -- Can hear, not in 3D + end + end + ::mic_next:: + end +end + local function Mic_SetLive(self, isLive) if not IsValid(self) then isLive = false @@ -55,10 +75,18 @@ local function Mic_SetLive(self, isLive) end if isLive then + if LiveMics[1] == nil then + hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", PlayerCanHearPlayersVoice_Hook) + end + if not table.HasValue(LiveMics, self) then table.insert(LiveMics, self) end else + if LiveMics[1] ~= nil and LiveMics[2] == nil then + hook.Remove("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone") + end + table.RemoveByValue(LiveMics, self) end end @@ -194,26 +222,6 @@ function ENT:ReproduceSound(snd, vol, pitch, dsp, emittype) end end -hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", function(listener, talker) - local talkerPos = talker:GetPos() - local listenerPos = listener:GetPos() - - -- Note: any given speaker can only be connected to one microphone, - -- so this loops can be considered O(nMic), not O(nMic*nSpeaker) - for _, mic in ipairs(LiveMics) do - if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then goto mic_next end - - for speaker in pairs(mic._activeSpeakers) do - if IsValid(speaker) and - speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR - then - return true, false -- Can hear, not in 3D - end - end - ::mic_next:: - end -end) - -- TODO: hook into sound.PlayFile -- TODO: hook into sound.PlayURL From 861233d9bb76c0d9143a0fc621121698bbfa8f47 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Sat, 6 Jan 2024 20:39:06 +0300 Subject: [PATCH 21/27] Added default values for EntityEmitSound hook calling microphone HandleSound --- lua/entities/gmod_wire_adv_microphone.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index ec2e1025a9..2f74e26928 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -176,8 +176,8 @@ hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) for _, mic in ipairs(LiveMics) do if IsValid(mic) then mic:HandleSound( - snd.SoundName, snd.Volume, snd.Pitch, snd.SoundLevel, - snd.Entity, snd.Pos, snd.DSP, + snd.SoundName, snd.Volume or 1, snd.Pitch or 100, snd.SoundLevel or 75, + snd.Entity, snd.Pos, snd.DSP or 0, "EmitSound" ) end From c3df3585851704912b004fad46e3a2e38aa33d5d Mon Sep 17 00:00:00 2001 From: stpM64 Date: Sun, 7 Jan 2024 21:03:03 +0300 Subject: [PATCH 22/27] Added comments for `Mic_SetLive` logic --- lua/entities/gmod_wire_adv_microphone.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 2f74e26928..995d8d2ed0 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -75,7 +75,7 @@ local function Mic_SetLive(self, isLive) end if isLive then - if LiveMics[1] == nil then + if LiveMics[1] == nil then -- Adding first microphone to live list hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", PlayerCanHearPlayersVoice_Hook) end @@ -83,7 +83,7 @@ local function Mic_SetLive(self, isLive) table.insert(LiveMics, self) end else - if LiveMics[1] ~= nil and LiveMics[2] == nil then + if LiveMics[1] ~= nil and LiveMics[2] == nil then -- Removing last microphone from live list hook.Remove("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone") end From ef4cb58ca1939990c23a269e5c0d4573a67fcaf8 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Tue, 9 Jan 2024 16:29:08 +0300 Subject: [PATCH 23/27] Fixed `sound.Play` microphone/speaker support --- lua/entities/gmod_wire_adv_speaker.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index 8df443537c..ba275358e0 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -90,7 +90,7 @@ function ENT:ReproduceSound(snd, vol, pitch, dsp, emittype) if emittype == "EmitSound" then self:EmitSound(snd, soundlevel, pitch, vol, nil, nil, dsp) elseif emittype == "sound.Play" then - sound.Play_NoWireHook(snd, pos, soundlevel, pitch, vol) + sound.Play_NoWireHook(snd, self:GetPos(), soundlevel, pitch, vol) else ErrorNoHalt("Invalid emittype: ", emittype,"\n --sound ",snd) end From 122bb7555704be4c2cd23c19212a3d7b8945f053 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Tue, 9 Jan 2024 16:29:37 +0300 Subject: [PATCH 24/27] Possibly fixed ReproduceSound error --- lua/entities/gmod_wire_adv_microphone.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 995d8d2ed0..8f02167e3e 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -218,7 +218,9 @@ end function ENT:ReproduceSound(snd, vol, pitch, dsp, emittype) for speaker in pairs(self._activeSpeakers) do - speaker:ReproduceSound(snd, vol, pitch, dsp, emittype) + if IsValid(speaker) then + speaker:ReproduceSound(snd, vol, pitch, dsp, emittype) + end end end From f40197e8ee919286148504a2a313abe29136f46a Mon Sep 17 00:00:00 2001 From: stpM64 Date: Sat, 20 Jan 2024 16:07:04 +0300 Subject: [PATCH 25/27] Added fallback position provider for microphone --- lua/entities/gmod_wire_adv_microphone.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 8f02167e3e..13bc14701c 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -199,6 +199,7 @@ end) function ENT:HandleSound(sndname, volume, pitch, sndlevel, entity, pos, dsp, emittype) -- Prevent feedback loops if IsValid(entity) and entity:GetClass() == "gmod_wire_adv_speaker" then return end + if pos == nil and IsValid(entity) then pos = entity:GetPos() end if sndlevel ~= 0 and pos ~= nil then -- Over-256 values are 'reserved for sounds using goldsrc compatibility attenuation' From 1b46615e3ef2ed652d42358135359d25915430b2 Mon Sep 17 00:00:00 2001 From: stpM64 Date: Tue, 20 Feb 2024 21:14:24 +0300 Subject: [PATCH 26/27] Added nearby players cache to microphone-speaker system, moved some functions to soundlib. --- lua/entities/gmod_wire_adv_microphone.lua | 42 ++++++---------- lua/entities/gmod_wire_adv_speaker.lua | 11 ++++ lua/wire/soundlib.lua | 61 +++++++++++++++++++++++ 3 files changed, 86 insertions(+), 28 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 13bc14701c..429107c8ce 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -1,9 +1,6 @@ AddCSLuaFile() local MIN_VOLUME = 0.02 -local MAX_DIST_GAIN = 1000 - -local PLAYER_VOICE_MAXDIST_SQR = 250*250 ENT.Type = "anim" ENT.Base = "base_wire_entity" @@ -37,6 +34,8 @@ function ENT:Initialize() self.Inputs = WireLib.CreateInputs(self, { "Active" }) + + self._plyCache = WireLib.Sound.NewPlayerDistanceCache(self, WireLib.Sound.VOICE_MAXDIST_SQR) end -- table(Entity(gmod_wire_adv_speaker), true) @@ -46,19 +45,22 @@ function ENT:Initialize() self:OnActiveChanged(nil, nil, self:GetActive()) end -local function PlayerCanHearPlayersVoice_Hook(listener, talker) - local talkerPos = talker:GetPos() - local listenerPos = listener:GetPos() +if SERVER then + function ENT:Think() + if not self:GetLive() then return end + + self._plyCache:Think() + end +end +local function PlayerCanHearPlayersVoice_Hook(listener, talker) -- Note: any given speaker can only be connected to one microphone, -- so this loops can be considered O(nMic), not O(nMic*nSpeaker) for _, mic in ipairs(LiveMics) do - if mic:GetPos():DistToSqr(talkerPos) > PLAYER_VOICE_MAXDIST_SQR then goto mic_next end + if not mic._plyCache.PlayersInRange[talker] then goto mic_next end for speaker in pairs(mic._activeSpeakers) do - if IsValid(speaker) and - speaker:GetPos():DistToSqr(listenerPos) <= PLAYER_VOICE_MAXDIST_SQR - then + if IsValid(speaker) and speaker._plyCache.PlayersInRange[listener] then return true, false -- Can hear, not in 3D end end @@ -74,7 +76,7 @@ local function Mic_SetLive(self, isLive) self:AddEFlags(EFL_FORCE_CHECK_TRANSMIT) end - if isLive then + if isLive then if LiveMics[1] == nil then -- Adding first microphone to live list hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", PlayerCanHearPlayersVoice_Hook) end @@ -154,23 +156,7 @@ function ENT:OnRemove() end) end -local CVAR_snd_refdb = GetConVar("snd_refdb") -local CVAR_snd_refdist = GetConVar("snd_refdist") - -local function CalculateDistanceGain(dist, sndlevel) - -- See SNDLVL_TO_DIST_MULT in engine/audio/private/snd_dma.cpp - -- See SND_GetGainFromMult in engine/sound_shared.cpp - - local finalsndlevel = CVAR_snd_refdb:GetFloat() - sndlevel - local distMul = math.pow(10, finalsndlevel / 20) / CVAR_snd_refdist:GetFloat() - - local gain = 1/(distMul * dist) - - return math.min(gain, MAX_DIST_GAIN) -- No infinities -end - - - +local CalculateDistanceGain = WireLib.Sound.CalculateDistanceGain hook.Add("EntityEmitSound", "Wire.AdvMicrophone", function(snd) for _, mic in ipairs(LiveMics) do diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index ba275358e0..e0653f7d62 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -28,6 +28,17 @@ if SERVER then }) self:OnMicrophoneChanged(nil, nil, self:GetMicrophone()) + + self._plyCache = WireLib.Sound.NewPlayerDistanceCache(self, WireLib.Sound.VOICE_MAXDIST_SQR) + end + + function ENT:Think() + if not self:GetActive() then return end + + local mic = self:GetMicrophone() + if not IsValid(mic) or not mic:GetActive() then return end + + self._plyCache:Think() end end diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index a45a337ff0..54fa9253ec 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -96,4 +96,65 @@ function sound.Play(snd, pos, level, pitch, volume, ...) hook.Run("Wire_SoundPlay", snd, pos, level or 75, pitch or 100, volume or 1, ...) sound.Play_NoWireHook(snd, pos, level, pitch, volume, ...) +end + + +local CVAR_snd_refdb = GetConVar("snd_refdb") +local CVAR_snd_refdist = GetConVar("snd_refdist") +local MAX_DIST_GAIN = 1000 + +function lib.CalculateDistanceGain(dist, sndlevel) + -- See SNDLVL_TO_DIST_MULT in engine/audio/private/snd_dma.cpp + -- See SND_GetGainFromMult in engine/sound_shared.cpp + + local finalsndlevel = CVAR_snd_refdb:GetFloat() - sndlevel + local distMul = math.pow(10, finalsndlevel / 20) / CVAR_snd_refdist:GetFloat() + + local gain = 1/(distMul * dist) + + return math.min(gain, MAX_DIST_GAIN) -- No infinities +end + +-- Maximum distance from player to adv_microphone and from adv_speaker to player, in which voice can be heard +lib.VOICE_MAXDIST_SQR = 250 * 250 + +lib._DCACHE = lib._DCACHE or {} +local DCACHE = lib._DCACHE +DCACHE.__index = DCACHE + +--[[ + struct PlayerDistanceCache { + fn :Think() + + readonly .PlayersInRange: table(Player, true) + } + + fn WireLib.Sound.NewPlayerDistanceCache(ent: Entity, max_dist_sqr: number) -> PlayerDistanceCache +]] + +function lib.NewPlayerDistanceCache(ent, max_dist_sqr) + local obj = setmetatable({ + _ent = ent, + _maxDistSqr = max_dist_sqr, + PlayersInRange = {} + }, DCACHE) + + return obj +end + +function DCACHE:Think() + local ent = self._ent + if not IsValid(ent) then return end + + local plys = {} + self.PlayersInRange = plys + + local entPos = ent:GetPos() + local maxDistSqr = self._maxDistSqr + + for _, ply in ipairs(player.GetHumans()) do + if ply:GetPos():DistToSqr(entPos) < maxDistSqr then + plys[ply] = true + end + end end \ No newline at end of file From c778e8f428f57c957a52346dd81aee3d5040993d Mon Sep 17 00:00:00 2001 From: stpM64 Date: Thu, 7 Mar 2024 20:19:58 +0300 Subject: [PATCH 27/27] Linter pass --- lua/entities/gmod_wire_adv_microphone.lua | 4 ++-- lua/entities/gmod_wire_adv_speaker.lua | 2 +- lua/wire/soundlib.lua | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/entities/gmod_wire_adv_microphone.lua b/lua/entities/gmod_wire_adv_microphone.lua index 429107c8ce..a43b5ee0a6 100644 --- a/lua/entities/gmod_wire_adv_microphone.lua +++ b/lua/entities/gmod_wire_adv_microphone.lua @@ -76,10 +76,10 @@ local function Mic_SetLive(self, isLive) self:AddEFlags(EFL_FORCE_CHECK_TRANSMIT) end - if isLive then + if isLive then if LiveMics[1] == nil then -- Adding first microphone to live list hook.Add("PlayerCanHearPlayersVoice", "Wire.AdvMicrophone", PlayerCanHearPlayersVoice_Hook) - end + end if not table.HasValue(LiveMics, self) then table.insert(LiveMics, self) diff --git a/lua/entities/gmod_wire_adv_speaker.lua b/lua/entities/gmod_wire_adv_speaker.lua index e0653f7d62..c55c39e109 100644 --- a/lua/entities/gmod_wire_adv_speaker.lua +++ b/lua/entities/gmod_wire_adv_speaker.lua @@ -34,7 +34,7 @@ if SERVER then function ENT:Think() if not self:GetActive() then return end - + local mic = self:GetMicrophone() if not IsValid(mic) or not mic:GetActive() then return end diff --git a/lua/wire/soundlib.lua b/lua/wire/soundlib.lua index 54fa9253ec..a7f78c9e30 100644 --- a/lua/wire/soundlib.lua +++ b/lua/wire/soundlib.lua @@ -29,8 +29,8 @@ local function WavIsLooped_Impl(path) local id, len = Riff_ReadChunkHeader(fil) if id == "cue " or id == "smpl" then resultid = id break end if id == nil then - ErrorNoHaltWithStack("WavIsLooped_Impl: Can't analyze file ", path) - return false + ErrorNoHaltWithStack("WavIsLooped_Impl: Can't analyze file ", path) + return false end -- Some error local p1 = fil:Tell() @@ -145,7 +145,7 @@ end function DCACHE:Think() local ent = self._ent if not IsValid(ent) then return end - + local plys = {} self.PlayersInRange = plys