Files
wnsrc/lua/pac3/core/client/parts/sound.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

347 lines
8.6 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local webaudio = include("pac3/libraries/webaudio.lua")
pac.webaudio2 = webaudio
local BUILDER, PART = pac.PartTemplate("base_movable")
PART.FriendlyName = "web sound"
PART.ClassName = "sound2"
PART.Icon = 'icon16/music.png'
PART.Group = 'effects'
BUILDER:StartStorableVars()
BUILDER:SetPropertyGroup("generic")
BUILDER:GetSet("Path", "", {editor_panel = "sound"})
BUILDER:GetSet("Volume", 1, {editor_sensitivity = 0.25})
BUILDER:GetSet("Pitch", 1, {editor_sensitivity = 0.125})
BUILDER:GetSet("Radius", 1500)
BUILDER:GetSet("Doppler", false)
BUILDER:GetSet("MinPitch", 0, {editor_sensitivity = 0.125})
BUILDER:GetSet("MaxPitch", 0, {editor_sensitivity = 0.125})
BUILDER:SetPropertyGroup("playback")
BUILDER:GetSet("PlayCount", 1,
{editor_onchange =
function(self, num)
self.sens = 0.25
num = tonumber(num)
return math.Round(math.max(num, 0))
end, editor_friendly = "PlayCount (0=loop)"}
)
BUILDER:GetSet("Sequential",false,{description = "if there are multiple sounds (separated by ; ), plays these sounds in sequential order instead of randomly"})
BUILDER:GetSet("SequentialStep", 1,
{editor_onchange =
function(self, num)
self.sens = 0.25
num = tonumber(num)
return math.Round(num)
end})
BUILDER:GetSet("StopOnHide", false)
BUILDER:GetSet("PauseOnHide", false)
BUILDER:GetSet("Overlapping", false)
BUILDER:GetSet("PlayOnFootstep", false)
BUILDER:SetPropertyGroup("filter")
BUILDER:GetSet("FilterType", 0, {enums = {
none = "0",
lowpass = "1",
highpass = "2",
}})
BUILDER:GetSet("FilterFraction", 1, {editor_sensitivity = 0.125, editor_clamp = {0, 1}})
BUILDER:SetPropertyGroup("echo")
BUILDER:GetSet("Echo", false)
BUILDER:GetSet("EchoDelay", 0.5, {editor_sensitivity = 0.125})
BUILDER:GetSet("EchoFeedback", 0.75, {editor_sensitivity = 0.125})
BUILDER:SetPropertyGroup("lfo")
BUILDER:GetSet("PitchLFOAmount", 0, {editor_sensitivity = 0.125, editor_friendly = "pitch amount"})
BUILDER:GetSet("PitchLFOTime", 0, {editor_sensitivity = 0.125, editor_friendly = "pitch time"})
BUILDER:GetSet("VolumeLFOAmount", 0, {editor_sensitivity = 0.125, editor_friendly = "volume amount"})
BUILDER:GetSet("VolumeLFOTime", 0, {editor_sensitivity = 0.125, editor_friendly = "volume time"})
BUILDER:EndStorableVars()
function PART:Initialize()
webaudio.Initialize()
self.streams = {}
end
function PART:GetNiceName()
local path = self:GetPath() .. ";"
local tbl = {}
for i, path in ipairs(path:Split(";")) do
if path ~= "" then
if path:StartWith("http") then
path = path:gsub("%%(..)", function(char)
local num = tonumber("0x" .. char)
if num then
return string.char(num)
end
end)
end
tbl[i] = pac.PrettifyName(("/".. path):match(".+/(.-)%.") or path:match("(.-)%.")) or "sound"
end
end
return table.concat(tbl, ";")
end
local stream_vars = {}
local BIND = function(propertyName, setterMethodName, check)
table.insert(stream_vars, propertyName)
setterMethodName = setterMethodName or "Set" .. propertyName
PART["Set" .. propertyName] = function(self, value)
if check then
value = check(value)
end
for url, stream in pairs(self.streams) do
if stream:IsValid() then
stream[setterMethodName](stream, value)
else
self.streams[url] = nil
end
end
self[propertyName] = value
end
end
BIND("Pitch", "SetPlaybackRate")
BIND("PlayCount", "SetMaxLoopCount" )
BIND("Volume", nil, function(n) return math.Clamp(n, 0, 4) end)
BIND("Radius", "SetSourceRadius" )
BIND("FilterType")
BIND("FilterFraction")
BIND("Echo")
BIND("EchoDelay")
BIND("EchoFeedback", nil, function(n) return math.Clamp(n, 0, 0.99) end)
BIND("PitchLFOAmount")
BIND("PitchLFOTime")
BIND("VolumeLFOAmount")
BIND("VolumeLFOTime")
BIND("Doppler")
function PART:OnThink()
local owner = self:GetRootPart():GetOwner()
for url, stream in pairs(self.streams) do
if not stream:IsValid() then self.streams[url] = nil goto CONTINUE end
if self.PlayCount == 0 then
stream:Resume()
end
if stream.owner_set ~= owner and owner:IsValid() then
stream:SetSourceEntity(owner, true)
stream.owner_set = owner
end
::CONTINUE::
end
if self.last_playonfootstep ~= self.PlayOnFootstep then
local ent = self:GetOwner()
if ent:IsValid() and ent:IsPlayer() then
ent.pac_footstep_override = ent.pac_footstep_override or {}
if self.PlayOnFootstep then
ent.pac_footstep_override[self.UniqueID] = self
else
ent.pac_footstep_override[self.UniqueID] = nil
end
if table.Count(ent.pac_footstep_override) == 0 then
ent.pac_footstep_override = nil
end
self.last_playonfootstep = self.PlayOnFootstep
end
end
end
function PART:SetPath(path)
self.seq_index = 1
self.Path = path
local paths = {}
for _, path in ipairs(path:Split(";")) do
local min, max = path:match(".+%[(.-),(.-)%]")
min = tonumber(min)
max = tonumber(max)
if min and max then
for i = min, max do
table.insert(paths, (path:gsub("%[.-%]", i)))
end
else
table.insert(paths, path)
end
end
for _, stream in pairs(self.streams) do
if stream:IsValid() then
stream:Remove()
end
end
self.streams = {}
local function load(path)
local stream = webaudio.CreateStream(path)
self.streams[path] = stream
stream:Set3D(true)
stream.OnLoad = function()
for _, key in ipairs(stream_vars) do
self["Set" .. key](self, self["Get" .. key](self))
end
end
stream.OnError = function(_, err, info)
info = info or "unknown error"
if self:IsValid() and pac.LocalPlayer == self:GetPlayerOwner() and pace and pace.IsActive() then
if pace and pace.current_part == self and not IsValid(pace.BusyWithProperties) then
pace.MessagePrompt(err .. "\n" .. info, "OGG error for" .. path, "OK")
else
pac.Message("OGG error: ", err, " reason: ", err .. "\n" .. info, "OGG error for" .. path)
self:SetError("OGG error: " .. err .. "\n" .. info .. "\nfor:" .. path)
end
end
end
stream.UpdateSourcePosition = function()
if self:IsValid() then
stream.SourcePosition = self:GetDrawPosition()
end
end
if
pace and
pace.Editor:IsValid() and
pace.current_part:IsValid() and
pace.current_part.ClassName == "ogg2" and
self:GetPlayerOwner() == pac.LocalPlayer
then
stream:Play()
end
end
for _, path in ipairs(paths) do
local info = sound.GetProperties(path)
if info then
path = info.sound
end
if not string.StartsWith(path, "http") or not pac.resource.Download(path, function(path) load("data/" .. path) end)
then load("sound/" .. path)
end
end
self.paths = paths
end
PART.last_stream = NULL
function PART:PlaySound(_, additiveVolumeFraction)
--PrintTable(self.streams)
additiveVolumeFraction = additiveVolumeFraction or 0
local stream = table.Random(self.streams) or NULL
if not stream:IsValid() then return end
if self.Sequential then
self.seq_index = self.seq_index or 1
local basepath = self.paths[self.seq_index] or self.paths[1]
local snd = "sound/".. basepath
local cached_path = "data/pac3_cache/downloads/" .. pac.Hash(basepath) .. ".dat"
if string.find(basepath, "^http") then
snd = cached_path
end
if self.streams[snd]:IsValid() then
stream = self.streams[snd]
print(snd,self.seq_index)
end
self.seq_index = self.seq_index + self.SequentialStep
if self.seq_index > #self.paths then
self.seq_index = self.seq_index - #self.paths
elseif self.seq_index < 1 then
self.seq_index = self.seq_index + #self.paths
end
end
stream:SetAdditiveVolumeModifier(additiveVolumeFraction)
if self.last_stream:IsValid() and not self.Overlapping and not self.PauseOnHide then
self.last_stream:Stop()
end
if self.MinPitch ~= self.MaxPitch then
stream:SetAdditivePitchModifier(math.Rand(self.MinPitch, self.MaxPitch))
else
stream:SetAdditivePitchModifier(0)
end
if self.PauseOnHide then
stream:Resume()
else
stream:Play()
end
self.last_stream = stream
end
function PART:StopSound()
for key, stream in pairs(self.streams) do
if stream:IsValid() then
if self.PauseOnHide then
stream:Pause()
elseif self.StopOnHide then
stream:Stop()
end
end
end
end
function PART:OnShow(from_rendering)
if not from_rendering then
self:PlaySound()
end
end
function PART:OnHide()
self:StopSound()
end
function PART:OnRemove()
for key, stream in pairs(self.streams) do
if not stream:IsValid() then self.streams[key] = nil goto CONTINUE end
stream:Remove()
::CONTINUE::
end
end
BUILDER:Register()