-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAudio Manager.lua
231 lines (190 loc) · 6.33 KB
/
Audio Manager.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
local CLASS = {}
--// SERVICES //--
local DEBRIS_SERVICE = game:GetService("Debris")
local SOUND_SERVICE = game:GetService("SoundService")
local TWEEN_SERVICE = game:GetService("TweenService")
--// CONSTANTS //--
local LOADING_TIMEOUT = 10
local LOADING_INTERVAL = 1
local BACKGROUND_MUSIC_FADE_INTERVAL = 5
local AUDIO_ASSETS_FOLDER = script:WaitForChild("Audio Assets")
local BACKGROUND_MUSICS_FOLDER = AUDIO_ASSETS_FOLDER:WaitForChild("Background Musics")
local SOUND_EFFECTS_FOLDER = AUDIO_ASSETS_FOLDER:WaitForChild("Sound Effects")
local WORKSPACE_ASSET_FOLDER = Instance.new("Folder", workspace)
WORKSPACE_ASSET_FOLDER.Name = "Audio Manager Assets"
local SOUND_SERVICE_ASSET_FOLDER = Instance.new("Folder", SOUND_SERVICE)
SOUND_SERVICE_ASSET_FOLDER.Name = "Audio Manager Assets"
--// VARIABLES //--
--// CONSTRUCTOR //--
function CLASS.New()
local dataTable = setmetatable(
{
BackgroundMusics = {},
SoundEffects = {},
CurrentBackgroundMusic = nil
},
CLASS
)
local proxyTable = setmetatable(
{
},
{
__index = function(self, index)
return dataTable[index]
end,
__newindex = function(self, index, newValue)
dataTable[index] = newValue
end
}
)
proxyTable:Initialize()
return proxyTable
end
--// FUNCTIONS //--
local function ConvertDictionaryToTableByValues(dictionary)
local valuesTable = {}
for _, value in pairs(dictionary) do
table.insert(valuesTable, value)
end
return valuesTable
end
--// METHODS //--
function CLASS:GetSoundWeight(sound)
return (sound:FindFirstChild("Weight") ~= nil and sound.Weight:IsA("NumberValue")) and (sound.Weight.Value) or (1)
end
function CLASS:GetSoundName(sound)
return (sound:FindFirstChild("Id") ~= nil and sound.Id:IsA("StringValue") and sound.Id.Value ~= "") and (sound.Id.Value) or (sound.Name)
end
function CLASS:ValidateSoundId(soundId)
if (soundId == nil) then
error("Audio Manager Loading Sounds Error: Malformed sound id")
end
end
function CLASS:LoadWithPredicate(predicate, errorMessage)
local timeStamp = tick()
while (predicate() == false) do
if (tick() - timeStamp > LOADING_TIMEOUT) then
error("Audio Manager Loading Sounds Timeout: " .. errorMessage, 0)
end
wait(LOADING_INTERVAL)
end
end
function CLASS:LoadSound(soundInstance)
self:LoadWithPredicate(
function()
return soundInstance.TimeLength > 0
end,
"Please make sure no sound is of length 0 or increase loading timeout"
)
end
function CLASS:LoadSoundsIntoDictionary(soundsFolder)
local soundsDictionary = {}
for _, child in pairs(soundsFolder:GetChildren()) do
if (child:IsA("Sound")) then
self:ValidateSoundId(child.SoundId)
self:LoadSound(child)
soundsDictionary[self:GetSoundName(child)] = child
elseif (child:IsA("Folder")) then
soundsDictionary[child.Name] = self:LoadSoundsIntoDictionary(child)
end
end
return soundsDictionary
end
function CLASS:LoadBackgroundMusics()
self.BackgroundMusics = self:LoadSoundsIntoDictionary(BACKGROUND_MUSICS_FOLDER)
end
function CLASS:LoadSoundEffects()
self.SoundEffects = self:LoadSoundsIntoDictionary(SOUND_EFFECTS_FOLDER)
end
function CLASS:Initialize()
self:LoadBackgroundMusics()
self:LoadSoundEffects()
end
function CLASS:GetAudioAssetFromGroup(group)
local sumOfWeights = 0
for _, sound in pairs(group) do
local weight = self:GetSoundWeight(sound)
sumOfWeights = sumOfWeights + weight
end
local randomNumber = Random.new():NextNumber(0, sumOfWeights)
for _, sound in pairs(group) do
local weight = self:GetSoundWeight(sound)
if (randomNumber < weight) then return sound end
randomNumber = randomNumber - weight
end
end
function CLASS:GetAudioAssetFromId(Id, assetDictionary)
for match in string.gmatch(Id, "/?([^/]+)/?") do
assetDictionary = assetDictionary[match]
if (assetDictionary == nil) then
error("Audio Manager Asset Error: '" .. Id .. "' is not a valid audio asset id", 3)
end
end
local asset = assetDictionary
if (typeof(asset) == "table") then
if (next(asset) == nil) then
error("Audio Manager Asset Error: '" .. Id .. "' is an empty group", 3)
else
asset = self:GetAudioAssetFromGroup(asset)
end
end
return asset
end
function CLASS:PlayBackgroundMusic(backgroundMusicId)
local sound = self:GetAudioAssetFromId(backgroundMusicId, self.BackgroundMusics)
if (self.CurrentBackgroundMusic ~= nil) then
local currentBackgroundMusicId = self:GetSoundName(self.CurrentBackgroundMusic)
if (currentBackgroundMusicId == backgroundMusicId) then
return
else
self:StopBackgroundMusic()
end
end
local soundClone = sound:Clone()
soundClone.Volume = 0
soundClone.Looped = true
soundClone.Parent = SOUND_SERVICE_ASSET_FOLDER
soundClone:Play()
TWEEN_SERVICE:Create(soundClone, TweenInfo.new(BACKGROUND_MUSIC_FADE_INTERVAL), {Volume = sound.Volume}):Play()
self.CurrentBackgroundMusic = soundClone
end
function CLASS:StopBackgroundMusic()
if (self.CurrentBackgroundMusic == nil) then
warn("Audio Manager Invalid Action Warning: Attempt to stop background music without playing one before")
else
DEBRIS_SERVICE:AddItem(self.CurrentBackgroundMusic, BACKGROUND_MUSIC_FADE_INTERVAL/2)
TWEEN_SERVICE:Create(self.CurrentBackgroundMusic, TweenInfo.new(BACKGROUND_MUSIC_FADE_INTERVAL/2), {Volume = 0}):Play()
self.CurrentBackgroundMusic = nil
end
end
function CLASS:PlaySoundEffect(soundEffectId)
local sound = self:GetAudioAssetFromId(soundEffectId, self.SoundEffects)
local soundClone = sound:Clone()
soundClone.Parent = SOUND_SERVICE_ASSET_FOLDER
DEBRIS_SERVICE:AddItem(soundClone, soundClone.TimeLength)
soundClone:Play()
end
function CLASS:PlaySoundEffectAtPosition(soundEffectId, position)
local sound = self:GetAudioAssetFromId(soundEffectId, self.SoundEffects)
local soundPart = Instance.new("Part")
soundPart.Anchored = true
soundPart.CanCollide = false
soundPart.Transparency = 1
soundPart.Size = Vector3.new()
soundPart.Position = position
soundPart.Parent = WORKSPACE_ASSET_FOLDER
local soundClone = sound:Clone()
soundClone.Parent = soundPart
DEBRIS_SERVICE:AddItem(soundPart, soundClone.TimeLength)
soundClone:Play()
end
function CLASS:PlaySoundEffectOnInstance(soundEffectId, instance)
local sound = self:GetAudioAssetFromId(soundEffectId, self.SoundEffects)
local soundClone = sound:Clone()
soundClone.Parent = instance
DEBRIS_SERVICE:AddItem(soundClone, soundClone.TimeLength)
soundClone:Play()
end
--// INSTRUCTIONS //--
CLASS.__index = CLASS
return CLASS.New()