Skip to content

Commit

Permalink
Feat: Add WebAudio:Set3DEnabled
Browse files Browse the repository at this point in the history
* Added WebAudio:Set3DEnabled, to make sound "flat" inside of the radius where it will play on the client and 3D functions won't do anything.

* Added webaudio:set3DEnabled for E2

* Nerfed wa_radius_max to 4,000 units by default. 10k was fine with volume scaling but will definitely be an issue without it.

* Make streams default play on the chip if never used with setPos(), so you can now just do webAudio("Sound"):play().

* Make 3D Audio not fizzle out after such a short distance (with magic numbers)

* Use DistToSqr instead of Distance in volume calculation as an optimization
  • Loading branch information
Vurv78 committed May 7, 2022
1 parent d032167 commit 49c215a
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 21 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ See CONTRIBUTING.md
| SERVER | wa_admin_only | 0 | Restrict E2 WebAudio access to admins. 0 is everyone, 1 is >=admin, 2 is super admins only. |
| SERVER | wa_stream_max | 5 | Max number of E2 WebAudio streams a player can have |
| SHARED | wa_volume_max | 300 | Max volume of streams, will clamp the volume of streams to this on both the client and on the server |
| SHARED | wa_radius_max | 10000 | Max distance where WebAudio streams can be heard from their origin. |
| SHARED | wa_radius_max | 3000 | Max distance where WebAudio streams can be heard from their origin. |
| SHARED | wa_fft_enable | 1 | Whether FFT data is enabled for the server / your client. You shouldn't need to disable it as it is very lightweight |
| CLIENT | wa_verbosity | 1 | Verbosity of console notifications. 2 => URL/Logging + Extra Info, 1 => Only warnings/errors, 0 => Nothing (Avoid this) |

Expand Down Expand Up @@ -105,6 +105,10 @@ Sets the radius in which to the stream will be heard in. Default is 200 and (def
``void webaudio:setLooping(number looping)``
Sets the stream to loop if n ~= 0, else stops looping.

``void webaudio:set3DEnabled(number enabled)``
By default WebAudio streams are 3D, so if enabled is 0 it will be set to "2D",
so the sound will be on the player's position if they are within the radius of the stream.

## Special

``number webaudio:pause()``
Expand Down
2 changes: 1 addition & 1 deletion STEAM_README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ This is a list of [b]ConVars[/b] that you can change to configure the addon to y
[tr]
[td]SHARED[/td]
[td]wa_radius_max[/td]
[td]10000[/td]
[td]3000[/td]
[td]Allows you to set the maximum distance a stream can be heard from. Works on your client.[/td]
[/tr]
[tr]
Expand Down
20 changes: 17 additions & 3 deletions lua/autorun/webaudio.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ local Modify = {
parented = 64,
radius = 128,
looping = 256,
mode = 512,

destroyed = 512
destroyed = 1024
}

local function hasModifyFlag(first, ...)
Expand All @@ -44,7 +45,7 @@ local WAMaxStreamsPerUser = CreateConVar("wa_stream_max", "5", FCVAR_REPLICATED,
-- SHARED
local WAEnabled = CreateConVar("wa_enable", "1", FCVAR_ARCHIVE + FCVAR_USERINFO, "Whether webaudio should be enabled to play on your client/server or not.", 0, 1)
local WAMaxVolume = CreateConVar("wa_volume_max", "300", FCVAR_ARCHIVE, "Highest volume a webaudio sound can be played at, in percentage. 200 is 200%. SHARED Convar", 0, 100000)
local WAMaxRadius = CreateConVar("wa_radius_max", "10000", FCVAR_ARCHIVE, "Farthest distance a WebAudio stream can be heard from. Will clamp to this value. SHARED Convar", 0, 1000000)
local WAMaxRadius = CreateConVar("wa_radius_max", "3000", FCVAR_ARCHIVE, "Farthest distance a WebAudio stream can be heard from. Will clamp to this value. SHARED Convar", 0, 1000000)
local WAFFTEnabled = CreateConVar("wa_fft_enable", "1", FCVAR_ARCHIVE, "Whether FFT data is enabled for the server / your client. You shouldn't need to disable it as it is very lightweight.", 0, 1)

-- CLIENT
Expand Down Expand Up @@ -86,6 +87,7 @@ end
---@class WebAudio
---@field stopwatch Stopwatch # SERVER
---@field radius number # SHARED
---@field radius_sqr number # SHARED
---@field looping boolean # SHARED
---@field parent GEntity # SHARED
---@field parented boolean # SHARED
Expand All @@ -99,9 +101,17 @@ end
---@field pos GVector # SERVER. Position of stream
---@field id integer # Custom ID for webaudio stream allocated between 0-MAX
---@field ignored GCRecipientFilter # Players to ignore when sending net messages.
---@field mode WebAudioMode
_G.WebAudio = {}
WebAudio.__index = WebAudio

---@alias WebAudioMode 0|1

---@type WebAudioMode
WebAudio.MODE_2D = 0
---@type WebAudioMode
WebAudio.MODE_3D = 1

local WebAudioCounter = 0
local WebAudios = {} -- TODO: See why weak kv doesn't work clientside for this

Expand Down Expand Up @@ -266,7 +276,7 @@ end

-- Bit lengths
local ID_LEN = 10
local MODIFY_LEN = 10
local MODIFY_LEN = 11
local FFTSAMP_LEN = 8

WebAudio.ID_LEN = ID_LEN
Expand Down Expand Up @@ -309,6 +319,7 @@ function WebAudioStatic.writeModify(modify)
net.WriteUInt(modify, MODIFY_LEN)
end

---@return WebAudio
function WebAudioStatic.getFromID(id)
return WebAudios[id]
end
Expand Down Expand Up @@ -372,6 +383,7 @@ local function createWebAudio(_, url, owner, bassobj, id)

self.url = url
self.owner = owner
self.mode = WebAudio.MODE_3D

-- Mutable --
self.playing = false
Expand All @@ -385,7 +397,9 @@ local function createWebAudio(_, url, owner, bassobj, id)
self.playback_rate = 1
self.volume = 1
self.time = 0

self.radius = math.min(200, WAMaxRadius:GetInt()) -- Default IGmodAudioChannel radius
self.radius_sqr = self.radius * self.radius

self.pos = nil
self.direction = Vector(0, 0, 0)
Expand Down
13 changes: 13 additions & 0 deletions lua/entities/gmod_wire_expression2/core/custom/webaudio.lua
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ end
__e2setcost(5)
e2function void webaudio:setPos(vector pos)
checkPermissions(self)

this.did_set_pos = true
this:SetPos(pos)
end

Expand All @@ -273,6 +275,12 @@ e2function number webaudio:play()
checkPermissions(self)
if not NetBurst:use(self.player) then return self:throw("You are transmitting too fast, check webAudioCanTransmit!", 0) end

if this.did_set_pos == nil then
-- They didn't set position. Probably want to default to chip position & parent.
-- this:SetPos( self.entity:GetPos() )
this:SetParent( self.entity )
end

return this:Play() and 1 or 0
end

Expand Down Expand Up @@ -314,6 +322,11 @@ e2function void webaudio:setLooping(number loop)
this:SetLooping( loop ~= 0 )
end

e2function void webaudio:set3DEnabled(number enabled)
checkPermissions(self)
this:Set3DEnabled( enabled ~= 0 )
end

__e2setcost(15)
e2function void webaudio:destroy()
-- No limit here because they'd already have been limited by the creation burst.
Expand Down
40 changes: 30 additions & 10 deletions lua/webaudio/interface.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ local StreamDisabledPlayers = { -- People who have wa_enabled set to 0

--- Adds a modify flag to the payload to be sent on transmission
--- @param n number Modify flag from the Modify struct in wa_common
--- @return boolean # Whether it modified. Will return nil if stream is destroyed.
--- @return boolean? # Whether it modified. Will return nil if stream is destroyed.
function WebAudio:AddModify(n)
if self:IsDestroyed() then return end
self.modified = bit.bor(self.modified, n)
Expand All @@ -33,7 +33,7 @@ end
--- Sets the volume of the stream
-- Does not transmit
--- @param vol number Float Volume (1 is 100%, 2 is 200% ..)
--- @return boolean # Successfully set time, will return nil if stream or 'vol' are invalid or 'vol' didn't change.
--- @return boolean? # Successfully set time, will return nil if stream or 'vol' are invalid or 'vol' didn't change.
function WebAudio:SetVolume(vol)
if self:IsDestroyed() then return end
if isnumber(vol) and self.volume ~= vol then
Expand All @@ -46,7 +46,7 @@ end
--- Sets the current playback time of the stream.
-- Does not transmit
--- @param time number UInt16 Playback time
--- @return boolean # Successfully set time, will return nil if stream is invalid.
--- @return boolean? # Successfully set time, will return nil if stream is invalid.
function WebAudio:SetTime(time)
if self:IsDestroyed() then return end
if isnumber(time) then
Expand All @@ -72,7 +72,7 @@ end

--- Sets the direction in which the stream will play
--- @param dir GVector Direction to set to
--- @return boolean # Successfully set direction, will return nil if stream or 'dir' are invalid or if 'dir' didn't change.
--- @return boolean? # Successfully set direction, will return nil if stream or 'dir' are invalid or if 'dir' didn't change.
function WebAudio:SetDirection(dir)
if self:IsDestroyed() then return end
if isvector(dir) and self.direction ~= dir then
Expand All @@ -83,7 +83,7 @@ function WebAudio:SetDirection(dir)
end

--- Resumes or starts the stream.
--- @return boolean # Successfully played, will return nil if the stream is destroyed or if already playing
--- @return boolean? # Successfully played, will return nil if the stream is destroyed or if already playing
function WebAudio:Play()
if self:IsDestroyed() then return end

Expand All @@ -103,7 +103,7 @@ function WebAudio:Play()
end

--- Pauses the stream and automatically transmits.
--- @return boolean # Successfully paused, will return nil if the stream is destroyed or if already paused
--- @return boolean? # Successfully paused, will return nil if the stream is destroyed or if already paused
function WebAudio:Pause()
if self:IsDestroyed() then return end

Expand All @@ -119,7 +119,7 @@ end

--- Sets the playback rate of the stream.
--- @param rate number Playback rate. Float64 that clamps to 255.
--- @return boolean # Successfully set rate, will return nil if stream or 'rate' are invalid or if 'rate' didn't change.
--- @return boolean? # Successfully set rate, will return nil if stream or 'rate' are invalid or if 'rate' didn't change.
function WebAudio:SetPlaybackRate(rate)
if self:IsDestroyed() then return end
if not isnumber(rate) then return end
Expand All @@ -136,19 +136,21 @@ end

--- Sets the radius of the stream. Uses Set3DFadeDistance internally.
--- @param radius number UInt16 radius
--- @return boolean # Successfully set radius, will return nil if the stream or 'radius' are invalid or if radius didn't change.
--- @return boolean? # Successfully set radius, will return nil if the stream or 'radius' are invalid or if radius didn't change.
function WebAudio:SetRadius(radius)
if self:IsDestroyed() then return end
if isnumber(radius) and self.radius ~= radius then
self.radius = radius
self.radius_sqr = radius * radius

self:AddModify(Modify.radius)
return true
end
end

--- Sets the parent of the WebAudio object. Nil to unparent
--- @param ent GEntity? Entity to parent to or nil to unparent.
--- @return boolean # Whether it was successfully parented. Returns nil if the stream is invalid.
--- @return boolean? # Whether it was successfully parented. Returns nil if the stream is invalid.
function WebAudio:SetParent(ent)
if self:IsDestroyed() then return end
if IsEntity(ent) and IsValid(ent) then
Expand All @@ -164,7 +166,7 @@ end

--- Makes the stream loop or stop looping.
--- @param loop boolean Whether it should be looping
--- @return boolean # If we set the value or not. Returns nil if the stream isn't valid or if the value didn't change.
--- @return boolean? # If we set the value or not. Returns nil if the stream isn't valid or if the value didn't change.
function WebAudio:SetLooping(loop)
if self:IsDestroyed() then return end
if self.looping ~= loop then
Expand All @@ -175,6 +177,20 @@ function WebAudio:SetLooping(loop)
end
end

--- Enables/Disables 3D Mode for a stream.
---@param is3d boolean
---@return boolean? # If we set the value or not. Returns nil if the stream isn't valid or if the value didn't change.
function WebAudio:Set3DEnabled(is3d)
if self:IsDestroyed() then return end
local desired = is3d and WebAudio.MODE_3D or WebAudio.MODE_2D

if self.mode ~= desired then
self.mode = desired
self:AddModify(Modify.mode)
return true
end
end

local LastUpdates = setmetatable({}, {
__mode = "k"
})
Expand Down Expand Up @@ -242,6 +258,10 @@ function WebAudio:Transmit()
net.WriteBool(self.looping)
end

if hasModifyFlag(modified, Modify.mode) then
net.WriteBit(self.mode == 1)
end

if hasModifyFlag(modified, Modify.parented) then
net.WriteBool(self.parented)
if self.parented then
Expand Down
37 changes: 31 additions & 6 deletions lua/webaudio/receiver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,21 @@ timer.Create("wa_think", 100 / 1000, 0, function()

-- Manually handle volume as you go farther from the stream.
if stream.pos then
local dist_to_stream = player_pos:Distance( stream.pos )
bass:SetVolume( stream.volume * ( 1 - math_min(dist_to_stream / stream.radius, 1) ) )
local dist_to_stream = player_pos:DistToSqr( stream.pos )

-- Could also ( 1 - math_min(dist_to_stream / stream.radius, 1) )
if dist_to_stream > stream.radius_sqr then
-- Stream is too far away.
bass:SetVolume( 0 )
elseif stream.mode == WebAudio.MODE_2D then
bass:SetVolume( stream.volume )
elseif dist_to_stream > stream.radius_sqr * 0.5 then
-- Stream is kinda far, now start smoothing
bass:SetVolume( stream.volume - ( dist_to_stream / stream.radius_sqr ) * stream.volume * 1.5 + stream.volume / 2 )
else
-- Stream is pretty close. Slow smooth
bass:SetVolume( stream.volume - ( dist_to_stream / stream.radius_sqr ) * stream.volume / 2 )
end
end
end
end
Expand All @@ -91,6 +104,7 @@ net.Receive("wa_create", function(len)
elseif verbosity >= 1 then
-- Warnings only (new default)
warn = wa_warn
function notify(...) end
else
function warn(...) end
function notify(...) end
Expand Down Expand Up @@ -208,7 +222,7 @@ function updateObject(id, modify_enum, handle_bass, inside_net)
if hasModifyFlag(modify_enum, Modify.destroyed) then
-- Don't destroy until we have the bass object.
if handle_bass then
self:Destroy()
self:Destroy(false)
self = nil
end
return
Expand All @@ -234,7 +248,7 @@ function updateObject(id, modify_enum, handle_bass, inside_net)
if hasModifyFlag(modify_enum, Modify.pos) then
if inside_net then self.pos = net.ReadVector() end
if handle_bass then
bass:SetPos(self.pos)
bass:SetPos(self.pos, nil)
end
end

Expand All @@ -256,7 +270,11 @@ function updateObject(id, modify_enum, handle_bass, inside_net)

-- Radius changed
if hasModifyFlag(modify_enum, Modify.radius) then
if inside_net then self.radius = math_min(net.ReadUInt(16), MaxRadius:GetInt()) end
if inside_net then
self.radius = math_min(net.ReadUInt(16), MaxRadius:GetInt())
self.radius_sqr = self.radius * self.radius
end

if handle_bass and self.pos then
local dist_to_stream = LocalPlayer():GetPos():Distance( self.pos )
bass:SetVolume( self.volume * ( 1 - math_min(dist_to_stream / self.radius, 1) ) )
Expand All @@ -270,6 +288,13 @@ function updateObject(id, modify_enum, handle_bass, inside_net)
end
end

if hasModifyFlag(modify_enum, Modify.mode) then
if inside_net then self.mode = net.ReadBit() end
if handle_bass then
bass:Set3DEnabled(self.mode == WebAudio.MODE_3D)
end
end

-- Was parented or unparented
if hasModifyFlag(modify_enum, Modify.parented) then
if inside_net then
Expand Down Expand Up @@ -354,7 +379,7 @@ concommand.Add("wa_purge", function()
net.WriteUInt(stream_count, 8)
stopStreams(true)
net.SendToServer()
end, nil, "Purges all of the currently playing WebAudio streams")
end, nil, "Purges all of the currently playing WebAudio streams", 0)

cvars.RemoveChangeCallback("wa_enable", "wa_enable")

Expand Down

0 comments on commit 49c215a

Please sign in to comment.