This is all the code in it: local RunService = game:GetService(“RunService”)
local SafeSpawn = require(script.Parent.Parent.Parent.Parent.Utilities.SafeSpawn)
local function _assert(data)
assert(data.StartTime ~= nil, “Failed to create config | expected config.StartTime got nil”)
assert(data.SoundId ~= nil, “Invalid SoundId | string expected got nil”)
assert(type(data.SoundId) == “string”, ("Invalid SoundId | string expected got " … type(data.SoundId)))
assert(data.Volume == nil or type(data.Volume) == “number”, “Volume has to be a number”)
if data.OnStart then
assert(type(data.OnStart) == "function", ("Invalid OnStart | function expected got " .. type(data.OnStart)))
end
end
local Audio = {}
Audio.__index = Audio
Audio.Type = “Audio”
Audio.task = task
Audio.SafeSpawn = SafeSpawn
function Audio:_constructSound(soundId)
local sound = Instance.new(“Sound”)
sound.SoundId = soundId
sound.Volume = self.Volume
-- TODO either have devs reparent wherever or default to env folder
sound.Parent = workspace
return sound
end
function Audio:_calculateCurrentSoundIntensityRatio(audio)
local loudestPlayback = self.LoudestPlayback
local loudness = audio.PlaybackLoudness
self.LoudestPlayback = math.max(loudness, loudestPlayback)
return loudness / self.LoudestPlayback
end
function Audio.new(data)
_assert(data)
local self = setmetatable({}, Audio)
self.SoundId = data.SoundId
self.StartTime = data.StartTime
self.OnStart = data.OnStart or function(_config) end
self.OnEnd = data.OnEnd or function(_config) end
self.CurrentSoundIntensityRatio = 1
self.LoudestPlayback = 1
self.Volume = data.Volume or 0.5
-- This is used by other config classes that sync to audio to prevent the class' process functions from waiting
-- if someone joins/seeks after audio scene time + audio length
self.IsDestroyed = false
return self
end
function Audio:_destroy()
if self.Sound then
self.Sound:Destroy()
self.Sound = nil
self.IsDestroyed = true
end
end
function Audio:_attemptOnStart()
if self.OnStart ~= nil then
self.SafeSpawn.new(function()
self:OnStart()
end)
end
end
function Audio:Process(schemaProcessor, startTime)
schemaProcessor.Maid:add(self, “SeekFlag”)
self.Sound = self:_constructSound(self.SoundId)
-- this will t-pose/a-pose rigs at the end of a scene
schemaProcessor.Maid:add(self, "Clean")
repeat
self.task.wait()
until self.IsCancelled or (self.Sound and self.Sound.IsLoaded)
if self.IsCancelled then
self:_destroy()
return
end
if self.Sound then
print("Audio loaded")
local timePositionOffset = schemaProcessor.timePosition.Value - startTime
self.Sound.TimePosition = timePositionOffset
self.Sound:Play()
local intensityRatioCalculatorConnection = RunService.Heartbeat:Connect(function()
self.CurrentSoundIntensityRatio = self:_calculateCurrentSoundIntensityRatio(self.Sound)
end)
schemaProcessor.Maid:add(intensityRatioCalculatorConnection)
schemaProcessor.Maid:add(self.Sound)
self.Sound.Ended:Connect(function()
self:_destroy()
intensityRatioCalculatorConnection:Disconnect()
self.OnEnd(self)
end)
self:_attemptOnStart()
end
end
function Audio:Clean()
self:_destroy()
end
function Audio:SeekFlag()
self.IsCancelled = true
end
return Audio
Its a module script.