mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
Upload
This commit is contained in:
436
lua/pac3/libraries/webaudio/browser.lua
Normal file
436
lua/pac3/libraries/webaudio/browser.lua
Normal file
@@ -0,0 +1,436 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- made by Morten and CapsAdmin
|
||||
|
||||
pac.webaudio = pac.webaudio or {}
|
||||
local webaudio = pac.webaudio
|
||||
|
||||
webaudio.Browser = webaudio.Browser or {}
|
||||
|
||||
webaudio.Browser.States =
|
||||
{
|
||||
Uninitialized = 0,
|
||||
Initializing = 1,
|
||||
Initialized = 2
|
||||
}
|
||||
|
||||
if webaudio.Browser.Control and
|
||||
webaudio.Browser.Control:IsValid () then
|
||||
webaudio.Browser.Control:Remove ()
|
||||
webaudio.Browser.Control = nil
|
||||
end
|
||||
|
||||
webaudio.Browser.State = webaudio.Browser.States.Uninitialized
|
||||
webaudio.Browser.Control = nil
|
||||
webaudio.Browser.JavascriptQueue = {}
|
||||
|
||||
webaudio.Browser.Volume = nil
|
||||
|
||||
local CONSOLE_MESSAGE_PREFIX = Color(121, 221, 203)
|
||||
local CONSOLE_MESSAGE_COLOR = Color(200, 200, 200)
|
||||
|
||||
function webaudio.Browser.Initialize()
|
||||
if webaudio.Browser.State ~= webaudio.Browser.States.Uninitialized then return end
|
||||
|
||||
webaudio.Browser.State = webaudio.Browser.States.Initializing
|
||||
|
||||
if webaudio.Browser.Control then webaudio.Browser.Control:Remove() end
|
||||
|
||||
webaudio.Browser.Control = vgui.Create("DHTML")
|
||||
webaudio.Browser.Control:SetVisible(false)
|
||||
webaudio.Browser.Control:SetPos(ScrW(), ScrH())
|
||||
webaudio.Browser.Control:SetSize(1, 1)
|
||||
|
||||
local lastMessage = nil
|
||||
webaudio.Browser.Control.ConsoleMessage = function(self, message)
|
||||
-- why does awesomium crash in the first place?
|
||||
if msg == "Uncaught ReferenceError: lua is not defined" then
|
||||
webaudio.Browser.State = webaudio.Browser.States.Uninitialized
|
||||
end
|
||||
|
||||
if lastMessage ~= message then
|
||||
lastMessage = message
|
||||
pac.Message(CONSOLE_MESSAGE_PREFIX, '[OGG] ', CONSOLE_MESSAGE_COLOR, unpack(pac.RepackMessage(message)))
|
||||
end
|
||||
end
|
||||
|
||||
webaudio.Browser.Control:AddFunction("lua", "print", webaudio.DebugPrint)
|
||||
|
||||
webaudio.Browser.Control:AddFunction("lua", "message", function(messageType, ...)
|
||||
local args = {...}
|
||||
|
||||
webaudio.DebugPrint(messageType, ...)
|
||||
|
||||
if messageType == "initialized" then
|
||||
webaudio.Browser.State = webaudio.Browser.States.Initialized
|
||||
webaudio.SampleRate = args[1]
|
||||
elseif messageType == "stream" then
|
||||
local stream = webaudio.Streams.GetStream(tonumber(args[2]) or 0)
|
||||
if not stream then return end
|
||||
|
||||
local messageType = args[1]
|
||||
stream:HandleBrowserMessage(messageType, unpack(args, 3, table.maxn(args)))
|
||||
end
|
||||
end)
|
||||
|
||||
--webaudio.Browser.Control:OpenURL("asset://garrysmod/lua/pac3/core/client/libraries/urlogg.lua")
|
||||
webaudio.Browser.Control:SetHTML(webaudio.Browser.HTML)
|
||||
end
|
||||
|
||||
function webaudio.Browser.IsInitializing()
|
||||
return webaudio.Browser.State == webaudio.Browser.States.Initializing
|
||||
end
|
||||
|
||||
function webaudio.Browser.IsInitialized()
|
||||
return webaudio.Browser.State == webaudio.Browser.States.Initialized
|
||||
end
|
||||
|
||||
-- Javascript
|
||||
function webaudio.Browser.RunJavascript(code)
|
||||
if IsValid(webaudio.Browser.Control) then webaudio.Browser.Control:QueueJavascript(code) end
|
||||
end
|
||||
|
||||
function webaudio.Browser.QueueJavascript(code)
|
||||
webaudio.Browser.JavascriptQueue [#webaudio.Browser.JavascriptQueue + 1] = code
|
||||
end
|
||||
|
||||
function webaudio.Browser.Think()
|
||||
if #webaudio.Browser.JavascriptQueue == 0 then return end
|
||||
|
||||
local code = table.concat(webaudio.Browser.JavascriptQueue, "\n")
|
||||
webaudio.Browser.RunJavascript(code)
|
||||
webaudio.Browser.JavascriptQueue = {}
|
||||
end
|
||||
|
||||
-- Audio
|
||||
function webaudio.Browser.SetVolume (volumeFraction)
|
||||
if webaudio.Browser.Volume == volumeFraction then return end
|
||||
|
||||
webaudio.Browser.Volume = volumeFraction
|
||||
webaudio.Browser.QueueJavascript(string.format("gain.gain.value = %f", volumeFraction))
|
||||
end
|
||||
|
||||
webaudio.Browser.HTML = [==[
|
||||
<script>
|
||||
|
||||
window.onerror = function(description, url, line)
|
||||
{
|
||||
lua.print("Unhandled exception at line " + line + ": " + description)
|
||||
}
|
||||
|
||||
function lerp(x0, x1, t)
|
||||
{
|
||||
return x0 * (1 - t) + x1 * t;
|
||||
}
|
||||
|
||||
var audio, gain, processor, analyser, streams = [];
|
||||
|
||||
function open()
|
||||
{
|
||||
if(audio)
|
||||
{
|
||||
audio.destination.disconnect();
|
||||
delete audio;
|
||||
delete gain;
|
||||
}
|
||||
|
||||
if(typeof AudioContext != "undefined"){
|
||||
audio = new AudioContext;
|
||||
processor = audio.createScriptProcessor(4096, 0, 2);
|
||||
gain = audio.createGain();
|
||||
}else{
|
||||
audio = new webkitAudioContext;
|
||||
processor = audio.createJavaScriptNode(4096, 0, 1);
|
||||
gain = audio.createGainNode();
|
||||
}
|
||||
|
||||
processor.onaudioprocess = function(event)
|
||||
{
|
||||
var outputLeft = event.outputBuffer.getChannelData(0);
|
||||
var outputRight = event.outputBuffer.getChannelData(1);
|
||||
|
||||
for(var i = 0; i < event.outputBuffer.length; ++i)
|
||||
{
|
||||
outputLeft [i] = 0;
|
||||
outputRight[i] = 0;
|
||||
}
|
||||
|
||||
for(var i in streams)
|
||||
{
|
||||
var stream = streams[i];
|
||||
|
||||
if(!stream.use_echo && (stream.paused || (stream.vol_left < 0.001 && stream.vol_right < 0.001)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var echol;
|
||||
var echor;
|
||||
|
||||
if (stream.use_echo && stream.echo_buffer)
|
||||
{
|
||||
echol = stream.echo_buffer.getChannelData(0);
|
||||
echor = stream.echo_buffer.getChannelData(1);
|
||||
}
|
||||
|
||||
var inputLength = stream.buffer.length;
|
||||
var inputLeft = stream.buffer.getChannelData(0);
|
||||
var inputRight = stream.buffer.numberOfChannels == 1 ? inputLeft : stream.buffer.getChannelData(1);
|
||||
|
||||
var sml = 0;
|
||||
var smr = 0;
|
||||
|
||||
for(var j = 0; j < event.outputBuffer.length; ++j)
|
||||
{
|
||||
if (stream.use_smoothing)
|
||||
{
|
||||
stream.speed_smooth = stream.speed_smooth + (stream.speed - stream.speed_smooth) * 0.001;
|
||||
stream.vol_left_smooth = stream.vol_left_smooth + (stream.vol_left - stream.vol_left_smooth ) * 0.001;
|
||||
stream.vol_right_smooth = stream.vol_right_smooth + (stream.vol_right - stream.vol_right_smooth) * 0.001;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.speed_smooth = stream.speed;
|
||||
stream.vol_left_smooth = stream.vol_left_smooth;
|
||||
stream.vol_right_smooth = stream.vol_right_smooth;
|
||||
}
|
||||
|
||||
if (stream.paused || stream.max_loop > 0 && stream.position > inputLength * stream.max_loop)
|
||||
{
|
||||
stream.done_playing = true;
|
||||
|
||||
if (!stream.paused)
|
||||
{
|
||||
lua.message("stream", "stop", stream.id);
|
||||
stream.paused = true;
|
||||
}
|
||||
|
||||
if (!stream.use_echo)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.done_playing = false;
|
||||
}
|
||||
|
||||
var index = (stream.position >> 0) % inputLength;
|
||||
var echo_index = (stream.position >> 0) % stream.echo_delay;
|
||||
|
||||
var left = 0;
|
||||
var right = 0;
|
||||
|
||||
if (!stream.done_playing)
|
||||
{
|
||||
// filters
|
||||
if (stream.filter_type == 0)
|
||||
{
|
||||
// None
|
||||
left = inputLeft [index] * stream.vol_left_smooth;
|
||||
right = inputRight[index] * stream.vol_right_smooth;
|
||||
}
|
||||
else
|
||||
{
|
||||
sml = sml + (inputLeft [index] - sml) * stream.filter_fraction;
|
||||
smr = smr + (inputRight[index] - smr) * stream.filter_fraction;
|
||||
|
||||
if (stream.filter_type == 1)
|
||||
{
|
||||
// Low pass
|
||||
left = sml * stream.vol_left_smooth;
|
||||
right = smr * stream.vol_right_smooth;
|
||||
}
|
||||
else if (stream.filter_type == 2)
|
||||
{
|
||||
// High pass
|
||||
left = (inputLeft [index] - sml) * stream.vol_left_smooth;
|
||||
right = (inputRight[index] - smr) * stream.vol_right_smooth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stream.use_echo)
|
||||
{
|
||||
echol[echo_index] = echol[echo_index] * stream.echo_feedback + left;
|
||||
echor[echo_index] = echor[echo_index] * stream.echo_feedback + right;
|
||||
|
||||
outputLeft [j] += echol[echo_index];
|
||||
outputRight[j] += echor[echo_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
outputLeft [j] += left;
|
||||
outputRight[j] += right;
|
||||
}
|
||||
|
||||
stream.position += stream.speed_smooth;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
processor.connect(gain)
|
||||
gain.connect(audio.destination)
|
||||
|
||||
lua.message("initialized", audio.sampleRate)
|
||||
}
|
||||
|
||||
function close()
|
||||
{
|
||||
if(audio)
|
||||
{
|
||||
audio.destination.disconnect();
|
||||
delete audio
|
||||
audio = null
|
||||
lua.message("uninitialized")
|
||||
}
|
||||
}
|
||||
|
||||
var buffer_cache = []
|
||||
|
||||
function download_buffer(url, callback, skip_cache, id)
|
||||
{
|
||||
if (!skip_cache && buffer_cache[url])
|
||||
{
|
||||
callback(buffer_cache[url])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var request = new XMLHttpRequest
|
||||
|
||||
request.open("GET", url)
|
||||
request.responseType = "arraybuffer"
|
||||
request.send()
|
||||
|
||||
request.onload = function()
|
||||
{
|
||||
lua.print("decoding " + url + " " + request.response.byteLength + " ...")
|
||||
|
||||
audio.decodeAudioData(request.response,
|
||||
|
||||
function(buffer)
|
||||
{
|
||||
lua.print("decoded " + url + " successfully")
|
||||
|
||||
callback(buffer)
|
||||
|
||||
buffer_cache[url] = buffer
|
||||
},
|
||||
|
||||
function(err)
|
||||
{
|
||||
lua.print("decoding error " + url + " " + err)
|
||||
lua.message("stream", "call", id, "OnError", "decoding failed", err)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
request.onprogress = function(event)
|
||||
{
|
||||
lua.print("downloading " + (event.loaded / event.total) * 100)
|
||||
}
|
||||
|
||||
request.onerror = function()
|
||||
{
|
||||
lua.print("downloading " + url + " errored")
|
||||
lua.message("stream", "call", id, "OnError", "download failed")
|
||||
};
|
||||
}
|
||||
|
||||
function createStream(url, id, skip_cache)
|
||||
{
|
||||
lua.print("Loading " + url)
|
||||
|
||||
download_buffer(url, function(buffer)
|
||||
{
|
||||
var stream = {}
|
||||
|
||||
stream.id = id
|
||||
stream.position = 0
|
||||
stream.buffer = buffer
|
||||
stream.url = url
|
||||
stream.speed = 1 // 1 = normal pitch
|
||||
stream.max_loop = 1 // -1 = inf
|
||||
stream.vol_left = 1
|
||||
stream.vol_right = 1
|
||||
stream.filter = audio.createBiquadFilter();
|
||||
stream.paused = true
|
||||
stream.use_smoothing = true
|
||||
stream.echo_volume = 0
|
||||
stream.filter_type = 0
|
||||
stream.filter_fraction = 1
|
||||
stream.done_playing = false
|
||||
|
||||
stream.use_echo = false
|
||||
stream.echo_feedback = 0.75
|
||||
stream.echo_buffer = false
|
||||
|
||||
stream.vol_left_smooth = 0
|
||||
stream.vol_right_smooth = 0
|
||||
stream.speed_smooth = stream.speed
|
||||
|
||||
stream.play = function(stop, position)
|
||||
{
|
||||
if(position !== undefined)
|
||||
{
|
||||
stream.position = position
|
||||
}
|
||||
|
||||
stream.paused = !stop
|
||||
};
|
||||
|
||||
stream.usefft = function(enable)
|
||||
{
|
||||
// later
|
||||
}
|
||||
|
||||
stream.useEcho = function(b) {
|
||||
stream.use_echo = b
|
||||
|
||||
if (b)
|
||||
{
|
||||
stream.setEchoDelay(stream.echo_delay)
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.echo_buffer = undefined
|
||||
}
|
||||
}
|
||||
|
||||
stream.setEchoDelay = function(x) {
|
||||
|
||||
if(stream.use_echo && (!stream.echo_buffer || (x != stream.echo_buffer.length))) {
|
||||
var size = 1;
|
||||
|
||||
while((size <<= 1) < x);
|
||||
|
||||
stream.echo_buffer = audio.createBuffer(2, size, audio.sampleRate);
|
||||
}
|
||||
|
||||
stream.echo_delay = x;
|
||||
}
|
||||
|
||||
streams[id] = stream
|
||||
|
||||
lua.message("stream", "loaded", id, buffer.length)
|
||||
}, skip_cache, id)
|
||||
}
|
||||
|
||||
function destroyStream(id)
|
||||
{
|
||||
}
|
||||
|
||||
open()
|
||||
|
||||
</script>
|
||||
|
||||
]==]
|
||||
473
lua/pac3/libraries/webaudio/stream.lua
Normal file
473
lua/pac3/libraries/webaudio/stream.lua
Normal file
@@ -0,0 +1,473 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- made by Morten and CapsAdmin
|
||||
|
||||
pac.webaudio = pac.webaudio or {}
|
||||
local webaudio = pac.webaudio
|
||||
|
||||
webaudio.Streams = webaudio.Streams or {}
|
||||
|
||||
webaudio.FilterType =
|
||||
{
|
||||
None = 0,
|
||||
LowPass = 1,
|
||||
HighPass = 2,
|
||||
}
|
||||
|
||||
local listenerPosition, listenerAngle, listenerVelocity = Vector(), Angle(), Vector()
|
||||
local lastListenerPosition, lastListenerPositionTime
|
||||
|
||||
pac.AddHook("RenderScene", "webaudio_3d", function(position, angle)
|
||||
listenerPosition = position
|
||||
listenerAngle = angle
|
||||
|
||||
lastListenerPosition = lastListenerPosition or listenerPosition
|
||||
lastListenerPositionTime = lastListenerPositionTime or (CurTime() - FrameTime())
|
||||
|
||||
listenerVelocity = (listenerPosition - lastListenerPosition) / (CurTime() - lastListenerPositionTime)
|
||||
|
||||
lastListenerPosition = listenerPosition
|
||||
lastListenerPositionTime = CurTime()
|
||||
end)
|
||||
|
||||
webaudio.Streams.STREAM = {}
|
||||
local STREAM = webaudio.Streams.STREAM
|
||||
STREAM.__index = STREAM
|
||||
|
||||
-- Identity
|
||||
STREAM.Id = nil
|
||||
STREAM.Url = "" -- ??
|
||||
|
||||
-- State
|
||||
STREAM.Loaded = false
|
||||
|
||||
-- Audio
|
||||
STREAM.SampleCount = 0
|
||||
|
||||
-- Playback
|
||||
STREAM.Paused = true
|
||||
STREAM.SamplePosition = 0
|
||||
STREAM.MaxLoopCount = nil
|
||||
|
||||
-- Playback speed
|
||||
STREAM.PlaybackSpeed = 1
|
||||
STREAM.AdditivePitchModifier = 0
|
||||
|
||||
-- Volume
|
||||
STREAM.Panning = 0
|
||||
STREAM.Volume = 1
|
||||
STREAM.AdditiveVolumeFraction = 0
|
||||
|
||||
-- 3d
|
||||
STREAM.Use3d = nil
|
||||
STREAM.UseDoppler = true
|
||||
|
||||
STREAM.SourceEntity = NULL
|
||||
STREAM.SourcePosition = nil
|
||||
STREAM.LastSourcePosition = nil
|
||||
STREAM.LastSourcePositionTime = nil
|
||||
STREAM.SourceVelocity = nil
|
||||
STREAM.SourceRadius = 1000
|
||||
STREAM.ListenerOutOfRadius = false
|
||||
|
||||
local function DECLARE_PROPERTY(propertyName, javascriptSetterCode, defaultValue, filterFunction)
|
||||
STREAM[propertyName] = defaultValue
|
||||
|
||||
STREAM["Set" .. propertyName] = function(self, value)
|
||||
if filterFunction then
|
||||
value = filterFunction(value, self)
|
||||
end
|
||||
|
||||
self[propertyName] = value
|
||||
|
||||
self:Call(javascriptSetterCode, value)
|
||||
end
|
||||
|
||||
STREAM["Get" .. propertyName] = function(self, ...)
|
||||
return self[propertyName]
|
||||
end
|
||||
end
|
||||
|
||||
-- Identity
|
||||
function STREAM:GetId()
|
||||
return self.Id
|
||||
end
|
||||
|
||||
function STREAM:SetId(id)
|
||||
self.Id = id
|
||||
return self
|
||||
end
|
||||
|
||||
function STREAM:GetUrl()
|
||||
return self.Url
|
||||
end
|
||||
|
||||
function STREAM:SetUrl(url)
|
||||
self.Url = url
|
||||
return self
|
||||
end
|
||||
|
||||
-- State
|
||||
function STREAM:IsLoaded()
|
||||
return self.Loaded
|
||||
end
|
||||
|
||||
function STREAM:IsValid()
|
||||
return true
|
||||
end
|
||||
|
||||
function STREAM:Remove()
|
||||
self.IsValid = function() return false end
|
||||
end
|
||||
|
||||
-- Browser
|
||||
function STREAM:Call(fmt, ...)
|
||||
if not self.Loaded then return end
|
||||
|
||||
local code = string.format("try { streams[%d]%s } catch(e) { lua.print(e.toString()) }", self:GetId(), string.format(fmt, ...))
|
||||
|
||||
webaudio.Browser.QueueJavascript(code)
|
||||
end
|
||||
|
||||
function STREAM:CallNow(fmt, ...)
|
||||
if not self.Loaded then return end
|
||||
|
||||
local code = string.format("try { streams[%d]%s } catch(e) { lua.print(e.toString()) }", self:GetId(), string.format(fmt, ...))
|
||||
|
||||
webaudio.Browser.RunJavascript(code)
|
||||
end
|
||||
|
||||
function STREAM:HandleBrowserMessage(messageType, ...)
|
||||
if messageType == "call" then
|
||||
self:HandleCallBrowserMessage(...)
|
||||
elseif messageType == "fft" then
|
||||
self:HandleFFTBrowserMessage(...)
|
||||
elseif messageType == "stop" then
|
||||
self.Paused = true
|
||||
elseif messageType == "return" then
|
||||
self.ReturnedValues = {...}
|
||||
elseif messageType == "loaded" then
|
||||
self:HandleLoadedBrowserMessage(...)
|
||||
elseif t == "position" then
|
||||
self:HandlePositionBrowserMessage(...)
|
||||
end
|
||||
end
|
||||
|
||||
-- Playback
|
||||
function STREAM:GetMaxLoopCount()
|
||||
return self.MaxLoopCount
|
||||
end
|
||||
|
||||
function STREAM:SetMaxLoopCount(maxLoopCount)
|
||||
self:Call(".max_loop = %i", maxLoopCount == true and -1 or maxLoopCount == false and 1 or tonumber(maxLoopCount) or 1)
|
||||
self.MaxLoopCount = maxLoopCount
|
||||
end
|
||||
|
||||
STREAM.SetLooping = STREAM.SetMaxLoopCount
|
||||
|
||||
function STREAM:GetSampleCount()
|
||||
return self.SampleCount
|
||||
end
|
||||
|
||||
function STREAM:Pause()
|
||||
self.Paused = true
|
||||
self:CallNow(".play(false)")
|
||||
end
|
||||
|
||||
function STREAM:Resume()
|
||||
self.Paused = false
|
||||
|
||||
self:UpdatePlaybackSpeed()
|
||||
self:UpdateVolume()
|
||||
|
||||
self:CallNow(".play(true)")
|
||||
end
|
||||
|
||||
function STREAM:Start()
|
||||
self.Paused = false
|
||||
|
||||
self:UpdatePlaybackSpeed()
|
||||
self:UpdateVolume()
|
||||
|
||||
self:CallNow(".play(true, 0)")
|
||||
end
|
||||
STREAM.Play = STREAM.Start
|
||||
|
||||
function STREAM:Stop()
|
||||
self.Paused = true
|
||||
self:CallNow(".play(false, 0)")
|
||||
end
|
||||
|
||||
function STREAM:Restart()
|
||||
self:SetSamplePosition(0)
|
||||
end
|
||||
|
||||
function STREAM:SetPosition(positionFraction)
|
||||
self:SetSamplePosition((positionFraction % 1) * self:GetSampleCount())
|
||||
end
|
||||
|
||||
DECLARE_PROPERTY("SamplePosition", ".position = %f", 0)
|
||||
|
||||
-- Playback speed
|
||||
function STREAM:GetPlaybackSpeed()
|
||||
return self.PlaybackSpeed
|
||||
end
|
||||
|
||||
function STREAM:GetAdditivePitchModifier()
|
||||
return self.AdditivePitchModifier
|
||||
end
|
||||
|
||||
function STREAM:SetPlaybackSpeed(playbackSpeedMultiplier)
|
||||
if self.PlaybackSpeed == playbackSpeedMultiplier then return self end
|
||||
|
||||
self.PlaybackSpeed = playbackSpeedMultiplier
|
||||
|
||||
self:UpdatePlaybackSpeed()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function STREAM:SetAdditivePitchModifier(additivePitchModifier)
|
||||
if self.AdditivePitchModifier == additivePitchModifier then return self end
|
||||
|
||||
self.AdditivePitchModifier = additivePitchModifier
|
||||
|
||||
self:UpdatePlaybackSpeed()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function STREAM:UpdatePlaybackSpeed()
|
||||
self:Call(".speed = %f", self.PlaybackSpeed + self.AdditivePitchModifier)
|
||||
end
|
||||
|
||||
-- Volume
|
||||
function STREAM:GetPanning()
|
||||
return self.Panning
|
||||
end
|
||||
|
||||
function STREAM:GetVolume()
|
||||
return self.Volume
|
||||
end
|
||||
|
||||
function STREAM:GetAdditiveVolumeModifier()
|
||||
return self.AdditiveVolumeModifier
|
||||
end
|
||||
|
||||
function STREAM:SetPanning(panning)
|
||||
if self.Panning == panning then return self end
|
||||
|
||||
self.Panning = panning
|
||||
|
||||
self:UpdateVolume()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function STREAM:SetVolume(volumeFraction)
|
||||
if self.Volume == volumeFraction then return self end
|
||||
|
||||
self.Volume = volumeFraction
|
||||
|
||||
self:UpdateVolume()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function STREAM:SetAdditiveVolumeModifier (additiveVolumeFraction)
|
||||
if self.AdditiveVolumeFraction == additiveVolumeFraction then return self end
|
||||
|
||||
self.AdditiveVolumeFraction = additiveVolumeFraction
|
||||
|
||||
self:UpdateVolume()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function STREAM:UpdateSourcePosition()
|
||||
if not self.SourceEntity:IsValid() then return end
|
||||
|
||||
self.SourcePosition = self.SourceEntity:GetPos()
|
||||
end
|
||||
|
||||
function STREAM:UpdateVolume()
|
||||
if self.Use3d then
|
||||
self:UpdateVolume3d()
|
||||
else
|
||||
self:UpdateVolumeFlat()
|
||||
end
|
||||
end
|
||||
|
||||
function STREAM:UpdateVolumeFlat()
|
||||
self:Call(".vol_right = %f", (math.Clamp(1 + self.Panning, 0, 1) * self.Volume) + self.AdditiveVolumeFraction)
|
||||
self:Call(".vol_left = %f", (math.Clamp(1 - self.Panning, 0, 1) * self.Volume) + self.AdditiveVolumeFraction)
|
||||
end
|
||||
|
||||
function STREAM:UpdateVolume3d()
|
||||
self:UpdateSourcePosition()
|
||||
|
||||
self.SourcePosition = self.SourcePosition or Vector()
|
||||
|
||||
self.LastSourcePosition = self.LastSourcePosition or self.SourcePosition
|
||||
self.LastSourcePositionTime = self.LastSourcePositionTime or (CurTime() - FrameTime())
|
||||
|
||||
self.SourceVelocity = (self.SourcePosition - self.LastSourcePosition) / (CurTime() - self.LastSourcePositionTime)
|
||||
|
||||
self.LastSourcePosition = self.SourcePosition
|
||||
self.LastSourcePositionTime = CurTime()
|
||||
|
||||
local relativeSourcePosition = self.SourcePosition - listenerPosition
|
||||
local distanceToSource = relativeSourcePosition:Length()
|
||||
|
||||
if distanceToSource < self.SourceRadius then
|
||||
local pan = relativeSourcePosition:GetNormalized():Dot(listenerAngle:Right())
|
||||
local volumeFraction = math.Clamp(1 - distanceToSource / self.SourceRadius, 0, 1) ^ 1.5
|
||||
volumeFraction = volumeFraction * 0.75 * self.Volume
|
||||
|
||||
self:Call(".vol_right = %f", (math.Clamp(1 + pan, 0, 1) * volumeFraction) + self.AdditiveVolumeFraction)
|
||||
self:Call(".vol_left = %f", (math.Clamp(1 - pan, 0, 1) * volumeFraction) + self.AdditiveVolumeFraction)
|
||||
|
||||
if self.UseDoppler then
|
||||
local relativeSourcePosition = self.SourcePosition - listenerPosition
|
||||
local relativeSourceVelocity = self.SourceVelocity - listenerVelocity
|
||||
local relativeSourceSpeed = relativeSourcePosition:GetNormalized():Dot(-relativeSourceVelocity) * 0.0254
|
||||
|
||||
self:Call(".speed = %f", (self.PlaybackSpeed + (relativeSourceSpeed / webaudio.SpeedOfSound)) + self.AdditivePitchModifier)
|
||||
end
|
||||
|
||||
self.ListenerOutOfRadius = false
|
||||
else
|
||||
if not self.ListenerOutOfRadius then
|
||||
self:Call(".vol_right = 0")
|
||||
self:Call(".vol_left = 0")
|
||||
self.ListenerOutOfRadius = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Filtering
|
||||
DECLARE_PROPERTY("FilterType", ".filter_type = %i")
|
||||
DECLARE_PROPERTY("FilterFraction", ".filter_fraction = %f", 0, function(num) return math.Clamp(num, 0, 1) end)
|
||||
|
||||
DECLARE_PROPERTY("Echo", ".useEcho(%s)", false)
|
||||
DECLARE_PROPERTY("EchoDelay", ".setEchoDelay(Math.ceil(audio.sampleRate * %f))", 1, function(num) return math.max(num, 0) end)
|
||||
DECLARE_PROPERTY("EchoFeedback", ".echo_feedback = %f", 0.75)
|
||||
|
||||
-- 3D
|
||||
function STREAM:Enable3D(b)
|
||||
self.Use3d = b
|
||||
end
|
||||
|
||||
function STREAM:EnableDoppler(b)
|
||||
self.UseDoppler = b
|
||||
end
|
||||
|
||||
function STREAM:GetSourceEntity()
|
||||
return self.SourceEntity
|
||||
end
|
||||
|
||||
function STREAM:SetSourceEntity(sourceEntity, doNotRemove)
|
||||
self.SourceEntity = sourceEntity
|
||||
|
||||
if not doNotRemove then
|
||||
sourceEntity:CallOnRemove("webaudio_remove_stream_" .. tostring(self), function()
|
||||
if self:IsValid() then
|
||||
self:Remove()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function STREAM:GetSourcePosition()
|
||||
return self.SourcePosition
|
||||
end
|
||||
|
||||
function STREAM:SetSourcePosition(vec)
|
||||
self.SourcePosition = vec
|
||||
end
|
||||
|
||||
function STREAM:GetSourceVelocity()
|
||||
return self.SourceVelocity
|
||||
end
|
||||
|
||||
function STREAM:SetSourceVelocity(vec)
|
||||
self.SourceVelocity = vec
|
||||
end
|
||||
|
||||
function STREAM:GetSourceRadius()
|
||||
return self.SourceRadius
|
||||
end
|
||||
|
||||
function STREAM:SetSourceRadius(sourceRadius)
|
||||
self.SourceRadius = sourceRadius
|
||||
end
|
||||
|
||||
function STREAM:Think()
|
||||
if self.Paused then return end
|
||||
|
||||
if self.Use3d then
|
||||
self:UpdateVolume3d() -- updates source position internally
|
||||
else
|
||||
self:UpdateSourcePosition()
|
||||
end
|
||||
end
|
||||
|
||||
function STREAM:__newindex(key, val)
|
||||
if key == "OnFFT" then
|
||||
if isfunction(val) then
|
||||
self:Call(".usefft(true)")
|
||||
else
|
||||
self:Call(".usefft(false)")
|
||||
end
|
||||
end
|
||||
|
||||
rawset(self, key, val)
|
||||
end
|
||||
|
||||
function STREAM:__tostring()
|
||||
return string.format("stream[%p][%d][%s]", self, self:GetId(), self:GetUrl())
|
||||
end
|
||||
|
||||
-- Internal browser message handlers
|
||||
function STREAM:HandleCallBrowserMessage(methodName, ...)
|
||||
if not self[methodName] then return end
|
||||
|
||||
self[methodName](self, ...)
|
||||
end
|
||||
|
||||
function STREAM:HandleFFTBrowserMessage(serializeFFTData)
|
||||
local fftArray = CompileString(serializeFFTData, "stream_fft_data")()
|
||||
self.OnFFT(fftArray)
|
||||
end
|
||||
|
||||
function STREAM:HandleLoadedBrowserMessage(sampleCount)
|
||||
self.Loaded = true
|
||||
|
||||
self.SampleCount = sampleCount
|
||||
self:SetFilterType(0)
|
||||
|
||||
if not self.Paused then
|
||||
-- self:Play()
|
||||
end
|
||||
|
||||
self:SetMaxLoopCount(self:GetMaxLoopCount())
|
||||
self:SetEcho (self:GetEcho ())
|
||||
self:SetEchoFeedback(self:GetEchoFeedback())
|
||||
self:SetEchoDelay (self:GetEchoDelay ())
|
||||
|
||||
if self.OnLoad then
|
||||
self:OnLoad()
|
||||
end
|
||||
end
|
||||
|
||||
function STREAM:HandlePositionBrowserMessage(samplePosition)
|
||||
self.SamplePosition = samplePosition
|
||||
end
|
||||
61
lua/pac3/libraries/webaudio/streams.lua
Normal file
61
lua/pac3/libraries/webaudio/streams.lua
Normal file
@@ -0,0 +1,61 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- made by Morten and CapsAdmin
|
||||
|
||||
pac.webaudio = pac.webaudio or {}
|
||||
local webaudio = pac.webaudio
|
||||
|
||||
webaudio.Streams = webaudio.Streams or {}
|
||||
|
||||
webaudio.Streams.LastStreamId = 0
|
||||
webaudio.Streams.Streams = {}
|
||||
|
||||
function webaudio.Streams.CreateStream(url)
|
||||
--url = url:gsub("http[s?]://", "http://")
|
||||
|
||||
if not url:find("http",1,true) then
|
||||
url = "asset://garrysmod/sound/" .. url
|
||||
end
|
||||
|
||||
local stream = setmetatable({}, webaudio.Streams.STREAM)
|
||||
|
||||
webaudio.Streams.LastStreamId = webaudio.Streams.LastStreamId + 1
|
||||
stream:SetId(webaudio.Streams.LastStreamId)
|
||||
stream:SetUrl(url)
|
||||
|
||||
webaudio.Streams.Streams[stream:GetId()] = stream
|
||||
|
||||
webaudio.Browser.QueueJavascript(string.format("createStream(%q, %d)", stream:GetUrl(), stream:GetId()))
|
||||
|
||||
return stream
|
||||
end
|
||||
|
||||
function webaudio.Streams.GetStream(streamId)
|
||||
return webaudio.Streams.Streams[streamId]
|
||||
end
|
||||
|
||||
function webaudio.Streams.StreamExists(streamId)
|
||||
return webaudio.Streams.Streams[streamId] ~= nil
|
||||
end
|
||||
|
||||
function webaudio.Streams.Think()
|
||||
for streamId, stream in pairs(webaudio.Streams.Streams) do
|
||||
if stream:IsValid() then
|
||||
stream:Think()
|
||||
else
|
||||
stream:Stop()
|
||||
webaudio.Streams.Streams[streamId] = nil
|
||||
webaudio.Browser.QueueJavascript(string.format("destroyStream(%i)", stream:GetId()))
|
||||
|
||||
setmetatable(stream, getmetatable(NULL))
|
||||
end
|
||||
end
|
||||
end
|
||||
68
lua/pac3/libraries/webaudio/urlogg.lua
Normal file
68
lua/pac3/libraries/webaudio/urlogg.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- made by Morten and CapsAdmin
|
||||
|
||||
pac.webaudio = pac.webaudio or {}
|
||||
local webaudio = pac.webaudio
|
||||
|
||||
webaudio.Debug = 0
|
||||
|
||||
webaudio.SampleRate = nil
|
||||
|
||||
webaudio.SpeedOfSound = 340.29 -- metres per second
|
||||
|
||||
function webaudio.DebugPrint(str, ...)
|
||||
if webaudio.Debug == 0 then return end
|
||||
|
||||
if webaudio.Debug >= 1 then
|
||||
if epoe then
|
||||
-- Why is this even present here?
|
||||
epoe.MsgC(Color(0, 255, 0), "[WebAudio] ")
|
||||
epoe.MsgC(Color(255, 255, 255), str)
|
||||
epoe.Print("")
|
||||
end
|
||||
|
||||
pac.Message(Color(0, 255, 0), "[WebAudio] ", Color(255, 255, 255), str, ...)
|
||||
end
|
||||
|
||||
if webaudio.Debug >= 2 then
|
||||
if easylua then
|
||||
easylua.PrintOnServer("[WebAudio] " .. str .. ' ' .. table.concat({...}, ', '))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function webaudio.GetSampleRate ()
|
||||
return webaudio.SampleRate
|
||||
end
|
||||
|
||||
local volume = GetConVar("volume")
|
||||
local snd_mute_losefocus = GetConVar("snd_mute_losefocus")
|
||||
|
||||
pac.AddHook("Think", "webaudio", function()
|
||||
if not webaudio.Browser.IsInitialized () then
|
||||
if not webaudio.Browser.IsInitializing () then
|
||||
webaudio.Browser.Initialize ()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Update volume
|
||||
if not system.HasFocus() and snd_mute_losefocus:GetBool() then
|
||||
-- Garry's Mod not in foreground and we're not supposed to be making any sound
|
||||
webaudio.Browser.SetVolume(0)
|
||||
else
|
||||
webaudio.Browser.SetVolume(volume:GetFloat())
|
||||
end
|
||||
|
||||
webaudio.Streams.Think()
|
||||
webaudio.Browser.Think()
|
||||
end)
|
||||
Reference in New Issue
Block a user