Skip to content

Commit 49c215a

Browse files
committed
Feat: Add WebAudio:Set3DEnabled
* 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
1 parent d032167 commit 49c215a

6 files changed

Lines changed: 97 additions & 21 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ See CONTRIBUTING.md
3535
| SERVER | wa_admin_only | 0 | Restrict E2 WebAudio access to admins. 0 is everyone, 1 is >=admin, 2 is super admins only. |
3636
| SERVER | wa_stream_max | 5 | Max number of E2 WebAudio streams a player can have |
3737
| 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 |
38-
| SHARED | wa_radius_max | 10000 | Max distance where WebAudio streams can be heard from their origin. |
38+
| SHARED | wa_radius_max | 3000 | Max distance where WebAudio streams can be heard from their origin. |
3939
| 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 |
4040
| CLIENT | wa_verbosity | 1 | Verbosity of console notifications. 2 => URL/Logging + Extra Info, 1 => Only warnings/errors, 0 => Nothing (Avoid this) |
4141

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

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

110114
``number webaudio:pause()``

STEAM_README.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ This is a list of [b]ConVars[/b] that you can change to configure the addon to y
7676
[tr]
7777
[td]SHARED[/td]
7878
[td]wa_radius_max[/td]
79-
[td]10000[/td]
79+
[td]3000[/td]
8080
[td]Allows you to set the maximum distance a stream can be heard from. Works on your client.[/td]
8181
[/tr]
8282
[tr]

lua/autorun/webaudio.lua

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ local Modify = {
1616
parented = 64,
1717
radius = 128,
1818
looping = 256,
19+
mode = 512,
1920

20-
destroyed = 512
21+
destroyed = 1024
2122
}
2223

2324
local function hasModifyFlag(first, ...)
@@ -44,7 +45,7 @@ local WAMaxStreamsPerUser = CreateConVar("wa_stream_max", "5", FCVAR_REPLICATED,
4445
-- SHARED
4546
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)
4647
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)
47-
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)
48+
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)
4849
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)
4950

5051
-- CLIENT
@@ -86,6 +87,7 @@ end
8687
---@class WebAudio
8788
---@field stopwatch Stopwatch # SERVER
8889
---@field radius number # SHARED
90+
---@field radius_sqr number # SHARED
8991
---@field looping boolean # SHARED
9092
---@field parent GEntity # SHARED
9193
---@field parented boolean # SHARED
@@ -99,9 +101,17 @@ end
99101
---@field pos GVector # SERVER. Position of stream
100102
---@field id integer # Custom ID for webaudio stream allocated between 0-MAX
101103
---@field ignored GCRecipientFilter # Players to ignore when sending net messages.
104+
---@field mode WebAudioMode
102105
_G.WebAudio = {}
103106
WebAudio.__index = WebAudio
104107

108+
---@alias WebAudioMode 0|1
109+
110+
---@type WebAudioMode
111+
WebAudio.MODE_2D = 0
112+
---@type WebAudioMode
113+
WebAudio.MODE_3D = 1
114+
105115
local WebAudioCounter = 0
106116
local WebAudios = {} -- TODO: See why weak kv doesn't work clientside for this
107117

@@ -266,7 +276,7 @@ end
266276

267277
-- Bit lengths
268278
local ID_LEN = 10
269-
local MODIFY_LEN = 10
279+
local MODIFY_LEN = 11
270280
local FFTSAMP_LEN = 8
271281

272282
WebAudio.ID_LEN = ID_LEN
@@ -309,6 +319,7 @@ function WebAudioStatic.writeModify(modify)
309319
net.WriteUInt(modify, MODIFY_LEN)
310320
end
311321

322+
---@return WebAudio
312323
function WebAudioStatic.getFromID(id)
313324
return WebAudios[id]
314325
end
@@ -372,6 +383,7 @@ local function createWebAudio(_, url, owner, bassobj, id)
372383

373384
self.url = url
374385
self.owner = owner
386+
self.mode = WebAudio.MODE_3D
375387

376388
-- Mutable --
377389
self.playing = false
@@ -385,7 +397,9 @@ local function createWebAudio(_, url, owner, bassobj, id)
385397
self.playback_rate = 1
386398
self.volume = 1
387399
self.time = 0
400+
388401
self.radius = math.min(200, WAMaxRadius:GetInt()) -- Default IGmodAudioChannel radius
402+
self.radius_sqr = self.radius * self.radius
389403

390404
self.pos = nil
391405
self.direction = Vector(0, 0, 0)

lua/entities/gmod_wire_expression2/core/custom/webaudio.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ end
265265
__e2setcost(5)
266266
e2function void webaudio:setPos(vector pos)
267267
checkPermissions(self)
268+
269+
this.did_set_pos = true
268270
this:SetPos(pos)
269271
end
270272

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

278+
if this.did_set_pos == nil then
279+
-- They didn't set position. Probably want to default to chip position & parent.
280+
-- this:SetPos( self.entity:GetPos() )
281+
this:SetParent( self.entity )
282+
end
283+
276284
return this:Play() and 1 or 0
277285
end
278286

@@ -314,6 +322,11 @@ e2function void webaudio:setLooping(number loop)
314322
this:SetLooping( loop ~= 0 )
315323
end
316324

325+
e2function void webaudio:set3DEnabled(number enabled)
326+
checkPermissions(self)
327+
this:Set3DEnabled( enabled ~= 0 )
328+
end
329+
317330
__e2setcost(15)
318331
e2function void webaudio:destroy()
319332
-- No limit here because they'd already have been limited by the creation burst.

lua/webaudio/interface.lua

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ local StreamDisabledPlayers = { -- People who have wa_enabled set to 0
2424

2525
--- Adds a modify flag to the payload to be sent on transmission
2626
--- @param n number Modify flag from the Modify struct in wa_common
27-
--- @return boolean # Whether it modified. Will return nil if stream is destroyed.
27+
--- @return boolean? # Whether it modified. Will return nil if stream is destroyed.
2828
function WebAudio:AddModify(n)
2929
if self:IsDestroyed() then return end
3030
self.modified = bit.bor(self.modified, n)
@@ -33,7 +33,7 @@ end
3333
--- Sets the volume of the stream
3434
-- Does not transmit
3535
--- @param vol number Float Volume (1 is 100%, 2 is 200% ..)
36-
--- @return boolean # Successfully set time, will return nil if stream or 'vol' are invalid or 'vol' didn't change.
36+
--- @return boolean? # Successfully set time, will return nil if stream or 'vol' are invalid or 'vol' didn't change.
3737
function WebAudio:SetVolume(vol)
3838
if self:IsDestroyed() then return end
3939
if isnumber(vol) and self.volume ~= vol then
@@ -46,7 +46,7 @@ end
4646
--- Sets the current playback time of the stream.
4747
-- Does not transmit
4848
--- @param time number UInt16 Playback time
49-
--- @return boolean # Successfully set time, will return nil if stream is invalid.
49+
--- @return boolean? # Successfully set time, will return nil if stream is invalid.
5050
function WebAudio:SetTime(time)
5151
if self:IsDestroyed() then return end
5252
if isnumber(time) then
@@ -72,7 +72,7 @@ end
7272

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

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

@@ -103,7 +103,7 @@ function WebAudio:Play()
103103
end
104104

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

@@ -119,7 +119,7 @@ end
119119

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

137137
--- Sets the radius of the stream. Uses Set3DFadeDistance internally.
138138
--- @param radius number UInt16 radius
139-
--- @return boolean # Successfully set radius, will return nil if the stream or 'radius' are invalid or if radius didn't change.
139+
--- @return boolean? # Successfully set radius, will return nil if the stream or 'radius' are invalid or if radius didn't change.
140140
function WebAudio:SetRadius(radius)
141141
if self:IsDestroyed() then return end
142142
if isnumber(radius) and self.radius ~= radius then
143143
self.radius = radius
144+
self.radius_sqr = radius * radius
145+
144146
self:AddModify(Modify.radius)
145147
return true
146148
end
147149
end
148150

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

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

180+
--- Enables/Disables 3D Mode for a stream.
181+
---@param is3d boolean
182+
---@return boolean? # If we set the value or not. Returns nil if the stream isn't valid or if the value didn't change.
183+
function WebAudio:Set3DEnabled(is3d)
184+
if self:IsDestroyed() then return end
185+
local desired = is3d and WebAudio.MODE_3D or WebAudio.MODE_2D
186+
187+
if self.mode ~= desired then
188+
self.mode = desired
189+
self:AddModify(Modify.mode)
190+
return true
191+
end
192+
end
193+
178194
local LastUpdates = setmetatable({}, {
179195
__mode = "k"
180196
})
@@ -242,6 +258,10 @@ function WebAudio:Transmit()
242258
net.WriteBool(self.looping)
243259
end
244260

261+
if hasModifyFlag(modified, Modify.mode) then
262+
net.WriteBit(self.mode == 1)
263+
end
264+
245265
if hasModifyFlag(modified, Modify.parented) then
246266
net.WriteBool(self.parented)
247267
if self.parented then

lua/webaudio/receiver.lua

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,21 @@ timer.Create("wa_think", 100 / 1000, 0, function()
7070

7171
-- Manually handle volume as you go farther from the stream.
7272
if stream.pos then
73-
local dist_to_stream = player_pos:Distance( stream.pos )
74-
bass:SetVolume( stream.volume * ( 1 - math_min(dist_to_stream / stream.radius, 1) ) )
73+
local dist_to_stream = player_pos:DistToSqr( stream.pos )
74+
75+
-- Could also ( 1 - math_min(dist_to_stream / stream.radius, 1) )
76+
if dist_to_stream > stream.radius_sqr then
77+
-- Stream is too far away.
78+
bass:SetVolume( 0 )
79+
elseif stream.mode == WebAudio.MODE_2D then
80+
bass:SetVolume( stream.volume )
81+
elseif dist_to_stream > stream.radius_sqr * 0.5 then
82+
-- Stream is kinda far, now start smoothing
83+
bass:SetVolume( stream.volume - ( dist_to_stream / stream.radius_sqr ) * stream.volume * 1.5 + stream.volume / 2 )
84+
else
85+
-- Stream is pretty close. Slow smooth
86+
bass:SetVolume( stream.volume - ( dist_to_stream / stream.radius_sqr ) * stream.volume / 2 )
87+
end
7588
end
7689
end
7790
end
@@ -91,6 +104,7 @@ net.Receive("wa_create", function(len)
91104
elseif verbosity >= 1 then
92105
-- Warnings only (new default)
93106
warn = wa_warn
107+
function notify(...) end
94108
else
95109
function warn(...) end
96110
function notify(...) end
@@ -208,7 +222,7 @@ function updateObject(id, modify_enum, handle_bass, inside_net)
208222
if hasModifyFlag(modify_enum, Modify.destroyed) then
209223
-- Don't destroy until we have the bass object.
210224
if handle_bass then
211-
self:Destroy()
225+
self:Destroy(false)
212226
self = nil
213227
end
214228
return
@@ -234,7 +248,7 @@ function updateObject(id, modify_enum, handle_bass, inside_net)
234248
if hasModifyFlag(modify_enum, Modify.pos) then
235249
if inside_net then self.pos = net.ReadVector() end
236250
if handle_bass then
237-
bass:SetPos(self.pos)
251+
bass:SetPos(self.pos, nil)
238252
end
239253
end
240254

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

257271
-- Radius changed
258272
if hasModifyFlag(modify_enum, Modify.radius) then
259-
if inside_net then self.radius = math_min(net.ReadUInt(16), MaxRadius:GetInt()) end
273+
if inside_net then
274+
self.radius = math_min(net.ReadUInt(16), MaxRadius:GetInt())
275+
self.radius_sqr = self.radius * self.radius
276+
end
277+
260278
if handle_bass and self.pos then
261279
local dist_to_stream = LocalPlayer():GetPos():Distance( self.pos )
262280
bass:SetVolume( self.volume * ( 1 - math_min(dist_to_stream / self.radius, 1) ) )
@@ -270,6 +288,13 @@ function updateObject(id, modify_enum, handle_bass, inside_net)
270288
end
271289
end
272290

291+
if hasModifyFlag(modify_enum, Modify.mode) then
292+
if inside_net then self.mode = net.ReadBit() end
293+
if handle_bass then
294+
bass:Set3DEnabled(self.mode == WebAudio.MODE_3D)
295+
end
296+
end
297+
273298
-- Was parented or unparented
274299
if hasModifyFlag(modify_enum, Modify.parented) then
275300
if inside_net then
@@ -354,7 +379,7 @@ concommand.Add("wa_purge", function()
354379
net.WriteUInt(stream_count, 8)
355380
stopStreams(true)
356381
net.SendToServer()
357-
end, nil, "Purges all of the currently playing WebAudio streams")
382+
end, nil, "Purges all of the currently playing WebAudio streams", 0)
358383

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

0 commit comments

Comments
 (0)