local i={}function i.new(r)local F={_sound=nil,_defaultSoundName="\065\117\100\105\111\076\105\098\095\083\111\117\110\100",_container=nil,_connections={};_rawPlaylist={Position=0},_proxyProperties={"\073\115\076\111\097\100\101\100","\073\115\080\097\117\115\101\100","\073\115\080\108\097\121\105\110\103";"\076\111\111\112\101\100";"\080\108\097\121\098\097\099\107\076\111\117\100\110\101\115\115";"\080\108\097\121\098\097\099\107\083\112\101\101\100","\080\108\097\121\105\110\103";"\084\105\109\101\076\101\110\103\116\104","\084\105\109\101\080\111\115\105\116\105\111\110";"\086\111\108\117\109\101";"\068\105\100\076\111\111\112";"\069\110\100\101\100";"\076\111\097\100\101\100";"\080\097\117\115\101\100";"\080\108\097\121\101\100"};_defaultProperties={Looped=false;PlaybackSpeed=1,Playing=false,SoundId="";TimePosition=0,Volume=1};Shuffle=false;AutoPlay=false;Playlist={}}setmetatable(F,{__index=i.__index;__newindex=i.__newindex,__tostring=i.__tostring})rawset(F,"\080\108\097\121\108\105\115\116",table.freeze(setmetatable(F.Playlist,{__index=F._rawPlaylist;__len=function()return#F._rawPlaylist end})))if r:IsA("\083\111\117\110\100")then F:SetSound(r)else F:LoadDefault(r)end return F end function i.__index(F,r)if i[r]then return i[r]elseif table.find(rawget(F,"\095\112\114\111\120\121\080\114\111\112\101\114\116\105\101\115"),r)then return(assert(rawget(F,"\095\115\111\117\110\100"),"\083\111\117\110\100 \109\105\115\115\105\110\103 \102\114\111\109 \097\117\100\105\111\108\105\098"))[r]elseif r~="\095\115\111\117\110\100"and r~="\095\099\111\110\116\097\105\110\101\114"then error(tostring(r).." \105\115 \110\111\116 \097 \118\097\108\105\100 \109\101\109\098\101\114 \111\102 \065\117\100\105\111\076\105\098")else return nil end end function i.__newindex(F,i,r)if table.find(rawget(F,"\095\112\114\111\120\121\080\114\111\112\101\114\116\105\101\115"),i)then(assert(rawget(F,"\095\115\111\117\110\100"),"\083\111\117\110\100 \109\105\115\115\105\110\103 \102\114\111\109 \097\117\100\105\111\108\105\098"))[i]=r elseif i~="\095\115\111\117\110\100"and i~="\095\099\111\110\116\097\105\110\101\114"then error(tostring(i).." \105\115 \110\111\116 \097 \118\097\108\105\100 \109\101\109\098\101\114 \111\102 \065\117\100\105\111\076\105\098")else rawset(F,i,r)end end function i.tostring(i)return"\065\117\100\105\111\076\105\098"end function i._updateSound(r,i)for i,F in i do r._sound[i]=F end end function i.GetTrack(r,i)assert(i<=#r._rawPlaylist,"\084\114\097\099\107 \112\111\115\105\116\105\111\110 \111\117\116\115\105\100\101 \111\102 \112\108\097\121\108\105\115\116 \115\105\122\101")return r.Playlist[i]end function i.SetTrack(r,i)assert(i<=#r._rawPlaylist,"\084\114\097\099\107 \112\111\115\105\116\105\111\110 \111\117\116\115\105\100\101 \111\102 \112\108\097\121\108\105\115\116 \115\105\122\101")r._rawPlaylist.Position=i r._sound.SoundId=r.Playlist[i]or""if i==0 then r:Stop()end end function i.AddTrack(F,i,r)assert(not r or r<=#F._rawPlaylist,"\084\114\097\099\107 \112\111\115\105\116\105\111\110 \111\117\116\115\105\100\101 \111\102 \112\108\097\121\108\105\115\116 \115\105\122\101")if r then table.insert(F._rawPlaylist,r,i)if r<=F._rawPlaylist.Position then F._rawPlaylist.Position+=1 F:SetTrack(F._rawPlaylist.Position)end else table.insert(F._rawPlaylist,i)end end function i.RemoveTrack(r,i)assert(not i or i<=#r._rawPlaylist,"\084\114\097\099\107 \112\111\115\105\116\105\111\110 \111\117\116\115\105\100\101 \111\102 \112\108\097\121\108\105\115\116 \115\105\122\101")i=i or#r._rawPlaylist table.remove(r._rawPlaylist,i)if i<=r._rawPlaylist.Position then r._rawPlaylist.Position-=1 r:SetTrack(r._rawPlaylist.Position)end end function i.SetPlaylist(r,i)r:SetTrack(0)table.clear(r._rawPlaylist)r._rawPlaylist.Position=0 table.move(i,1,#i,1,r._rawPlaylist)end function i.GetSound(i)return i._sound end function i.SetSound(r,i)for i,r in r._connections do r:Disconnect()end table.clear(r._connections)r._sound=i r:_updateSound(r._defaultProperties)table.insert(r._connections,i.Ended:Connect(function()if not r.Looped and r.AutoPlay then r:PlayNext()end end))end function i.Play(r,i)if i then local F=table.find(r._rawPlaylist,i)if F then r:SetTrack(F)else r:AddTrack(i)r:SetTrack(#r._rawPlaylist)end end r.TimePosition=0 r._sound:Play()end function i.Stop(i)i._sound:Stop()end function i.Pause(i)i._sound:Pause()end function i.Resume(i)i._sound:Resume()end function i.PlayNext(i)if#i.Playlist>0 then i:SetTrack(i.Shuffle and math.random(1,#i.Playlist)or i.Playlist.Position%#i.Playlist+1)i:Play()end end function i.GetContainer(i)return i._container end function i.SetContainer(r,i)r._container=i end function i.LoadDefault(r,i)local F=i:FindFirstChild(r._defaultSoundName)if not F then F=Instance.new("\083\111\117\110\100")F.Name=r._defaultSoundName F.Archivable=false end r:SetSound(F)F.Parent=i end return i
--[[
	Name: AudioLib
	Description: An simple and versatile audio library with getter&setter functions and an immutable data structure.
	Authors: Github@ccuser44/VortexColor & P3tray
	Contributors: Github@ccuser44/VortexColor, P3tray
	Date: 2024
	License: MIT
]]

local AudioLib = {}

function AudioLib.new(object)
	local self = {
		-- // Internal variables
		_sound = nil,
		_defaultSoundName = "AudioLib_Sound",
		_container = nil,
		_connections = {},
		_rawPlaylist = {
			Position = 0
		},
		_proxyProperties = {
			-- // Properties
			"IsLoaded",
			"IsPaused",
			"IsPlaying",
			"Looped",
			"PlaybackLoudness",
			"PlaybackSpeed",
			"Playing",
			"TimeLength",
			"TimePosition",
			"Volume",
			-- // Events
			"DidLoop",
			"Ended",
			"Loaded",
			"Paused",
			"Played"
		},
		_defaultProperties = {
			Looped = false,
			PlaybackSpeed = 1,
			Playing = false,
			SoundId = "",
			TimePosition = 0,
			Volume = 1
		},
		-- // External Variables
		Shuffle = false,
		AutoPlay = false,
		Playlist = {}
	}

	setmetatable(self, {
		__index = AudioLib.__index,
		__newindex = AudioLib.__newindex,
		__tostring = AudioLib.__tostring
	})

	rawset(self, "Playlist", table.freeze(setmetatable(self.Playlist, {
		__index = self._rawPlaylist,
		__len = function()
			return #self._rawPlaylist
		end
	})))

	-- // Allow convienient initializing of AudioLib without having to use setters
	if object:IsA("Sound") then
		self:SetSound(object)
	else
		self:LoadDefault(object)
	end

	return self
end

function AudioLib:__index(k)
	if AudioLib[k] then
		return AudioLib[k]
	elseif table.find(rawget(self, "_proxyProperties"), k) then
		return assert(rawget(self, "_sound"), "Sound missing from audiolib")[k]
	elseif k ~= "_sound" and k ~= "_container" then
		error(tostring(k).." is not a valid member of AudioLib")
	else
		return nil
	end
end

function AudioLib:__newindex(k, v)
	if table.find(rawget(self, "_proxyProperties"), k) then
		assert(rawget(self, "_sound"), "Sound missing from audiolib")[k] = v
	elseif k ~= "_sound" and k ~= "_container" then
		error(tostring(k).." is not a valid member of AudioLib")
	else
		rawset(self, k, v)
	end
end

function AudioLib:tostring()
	return "AudioLib"
end

function AudioLib:_updateSound(data)
	for k, v in data do
		self._sound[k] = v
	end
end

function AudioLib:GetTrack(position)
	assert(position <= #self._rawPlaylist, "Track position outside of playlist size")
	return self.Playlist[position]
end

function AudioLib:SetTrack(position)
	assert(position <= #self._rawPlaylist, "Track position outside of playlist size")
	self._rawPlaylist.Position = position
	self._sound.SoundId = self.Playlist[position] or ""

	if position == 0 then
		self:Stop()
	end
end

function AudioLib:AddTrack(track, position)
	assert(not position or position <= #self._rawPlaylist, "Track position outside of playlist size")

	if position then
		table.insert(self._rawPlaylist, position, track)

		if position <= self._rawPlaylist.Position then
			self._rawPlaylist.Position += 1
			self:SetTrack(self._rawPlaylist.Position)
		end
	else
		table.insert(self._rawPlaylist, track)
	end
end

function AudioLib:RemoveTrack(position)
	assert(not position or position <= #self._rawPlaylist, "Track position outside of playlist size")
	position = position or #self._rawPlaylist

	table.remove(self._rawPlaylist, position)

	if position <= self._rawPlaylist.Position then
		self._rawPlaylist.Position -= 1
		self:SetTrack(self._rawPlaylist.Position)
	end
end

function AudioLib:SetPlaylist(playlist)
	self:SetTrack(0)
	table.clear(self._rawPlaylist)
	self._rawPlaylist.Position = 0
	table.move(playlist, 1, #playlist, 1, self._rawPlaylist)
end

function AudioLib:GetSound()
	return self._sound
end

function AudioLib:SetSound(sound)
	for _, v in self._connections do -- // Disconnect events from past sounds
		v:Disconnect()
	end

	table.clear(self._connections)
	self._sound = sound
	self:_updateSound(self._defaultProperties)
	table.insert(self._connections, sound.Ended:Connect(function()
		if not self.Looped and self.AutoPlay then
			self:PlayNext()
		end
	end))
end

function AudioLib:Play(soundId)
	if soundId then
		local foundPos = table.find(self._rawPlaylist, soundId)

		if foundPos then
			self:SetTrack(foundPos)
		else
			self:AddTrack(soundId)
			self:SetTrack(#self._rawPlaylist)
		end
	end

	self.TimePosition = 0
	self._sound:Play()
end

function AudioLib:Stop()
	self._sound:Stop()
end

function AudioLib:Pause()
	self._sound:Pause()
end

function AudioLib:Resume()
	self._sound:Resume()
end

function AudioLib:PlayNext()
	if #self.Playlist > 0 then
		self:SetTrack(self.Shuffle and math.random(1, #self.Playlist) or self.Playlist.Position % #self.Playlist + 1)
		self:Play()
	end
end

function AudioLib:GetContainer()
	return self._container
end

function AudioLib:SetContainer(container)
	self._container = container
end

function AudioLib:LoadDefault(container)
	local sound = container:FindFirstChild(self._defaultSoundName)

	if not sound then
		sound = Instance.new("Sound")
		sound.Name = self._defaultSoundName
		sound.Archivable = false
	end

	self:SetSound(sound)
	sound.Parent = container
end

return AudioLib
