@rbxts/sound-manager 2.0.1 β†’ 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,48 @@
1
- # Sound Manager
1
+ <h1 align="center">
2
+ <a href="https://www.npmjs.com/package/@rbxts/sound-manager">
3
+ <img src="public/logo.png" alt="Sound-Manager" width="200" />
4
+ </a>
5
+ <br />
6
+ <b>Sound Manager</b>
7
+
8
+ </h1>
2
9
 
3
- A Roblox sound management library built with Roblox-TS
10
+ <div align="center">
11
+
12
+ ![npm](https://img.shields.io/npm/dt/@rbxts/sound-manager?style=flat-square) ![npm](https://img.shields.io/npm/v/@rbxts/sound-manager?style=flat-square) ![GitHub](https://img.shields.io/github/license/dev-lukas0/Sound-Manager?style=flat-square)
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ &nbsp;
19
+
20
+
21
+ ## πŸ”‰ Sound Manager
22
+
23
+ **Sound Manager** is a roblox sound-management library built with Roblox-TS, designed to simplify sound handling in your roblox projects.
24
+
25
+ &nbsp;
26
+
27
+ ## πŸ“¦ Installation
4
28
 
5
- ## Getting Started
6
29
  To use the Sound Manager library in your Roblox-TS project, install it via npm:
30
+
7
31
  ```bash
8
32
  npm install @rbxts/sound-manager
9
33
  ```
10
34
 
11
- Example usage:
12
- ```typescript
35
+ &nbsp;
36
+
37
+ ## πŸš€ Quick Start
38
+
39
+ [See the Documentation ->](https://dev-lukas0.github.io/Sound-Manager-Docs/)
40
+
41
+ ### ⚑ Starting with Sound Manager
42
+
43
+ Sound-Manager uses [`createSoundRegistry`]() and [`createSoundCategoryRegistry`]() to create sounds and categories.
44
+
45
+ ```ts
13
46
  import { createSoundRegistry } from "@rbxts/sound-manager";
14
47
 
15
48
  const Sounds = createSoundRegistry({
@@ -17,13 +50,37 @@ const Sounds = createSoundRegistry({
17
50
  id: "rbxassetid://4714389545",
18
51
  volume: 1,
19
52
  loop: false,
20
- }
53
+ },
54
+
55
+ Test: {
56
+ id: "rbxassetid://17771398985",
57
+ volume: 1,
58
+ loop: true,
59
+ }
21
60
  });
61
+ ```
62
+
63
+ ### 🎡 Playing Sounds
22
64
 
23
- Sounds.preloadAll();
65
+ To play a sound, you can use the [`play`](https://dev-lukas0.github.io/Sound-Manager-Docs/docs/API/play) method on the sound registry:
24
66
 
67
+ ```ts
25
68
  Sounds.play("SCP096");
26
69
  ```
27
70
 
71
+ &nbsp;
72
+
73
+ ## πŸ—ΊοΈ Roadmap
74
+ -------
75
+ See the [Roadmap](./Roadmap.md) for planned features and current progress.
76
+
77
+ &nbsp;
78
+
79
+ ## πŸ“ License
80
+
81
+ **Sound Manager** is licensed under the [MIT LICENSE](./LICENSE).
82
+
83
+ &nbsp;
84
+
28
85
  For more Details:
29
86
  https://dev-lukas0.github.io/Sound-Manager-Docs/
@@ -1,9 +1,25 @@
1
1
  import { CategoryOptions } from "./options";
2
+ import { SoundHandle } from "./options";
2
3
  /**
3
4
  * Create a sound category Registry
4
5
  * @param definitions Define the Categorys
5
6
  */
6
7
  export declare function createSoundCategoryRegistry<T extends Record<string, CategoryOptions>>(definitions: T): {
7
8
  loadCategory: (name: keyof T) => void;
8
- playCategory: <C extends keyof T>(name: C) => void;
9
+ playCategory: <C extends keyof T>(name: C, spatial?: {
10
+ emitters: BasePart[];
11
+ }) => Map<string, SoundHandle> | undefined;
12
+ stopCategory: <C extends keyof T>(name: C) => void;
13
+ stopAllCategories: () => void;
14
+ setCategoryVolume: <C extends keyof T>(category: C, volume: number) => void;
15
+ setGlobalCategoryVolume: (volume: number) => void;
16
+ isCategoryPlaying: <C extends keyof T>(category: C) => boolean;
17
+ playSoundFromCategory: <C extends keyof T, S extends keyof T[C]["sounds"]>(category: C, sound: S) => void;
18
+ resetCategory: <C extends keyof T>(category: C) => void;
19
+ resetAllCategories: <C extends keyof T>(category: C) => void;
20
+ preloadAllCategories: () => void;
21
+ preloadCategory: <C extends keyof T>(category: C) => void;
22
+ fadeInCategory: <C extends keyof T>(category: C, duration: number, volume: number) => void;
23
+ fadeOutCategory: <C extends keyof T>(category: C, duration: number, targetVolume?: number) => void;
24
+ onCategoryEnd: <C extends keyof T>(category: C, callback: () => void) => void;
9
25
  };
@@ -1,4 +1,6 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local createSpatialHandle = TS.import(script, script.Parent, "createSpatialHandle").createSpatialHandle
2
4
  --[[
3
5
  *
4
6
  * Create a sound category Registry
@@ -6,6 +8,7 @@
6
8
 
7
9
  ]]
8
10
  local function createSoundCategoryRegistry(definitions)
11
+ local spatialHandles = {}
9
12
  local ReplicatedStorage = game:GetService("ReplicatedStorage")
10
13
  local folder = ReplicatedStorage:FindFirstChild("Sounds")
11
14
  if not folder then
@@ -53,8 +56,35 @@ local function createSoundCategoryRegistry(definitions)
53
56
  * @param name Sound Category
54
57
 
55
58
  ]]
56
- local function playCategory(name)
59
+ local function playCategory(name, spatial)
57
60
  loadCategory(name)
61
+ local config = definitions[name]
62
+ local categoryFolder = folder:FindFirstChild(config.category)
63
+ if not categoryFolder then
64
+ return nil
65
+ end
66
+ for _, sound in categoryFolder:GetChildren() do
67
+ if not sound:IsA("Sound") then
68
+ continue
69
+ end
70
+ if spatial and #spatial.emitters > 0 then
71
+ local handle = createSpatialHandle(sound.SoundId, spatial.emitters, sound.Volume)
72
+ local _name = sound.Name
73
+ spatialHandles[_name] = handle
74
+ handle:play()
75
+ else
76
+ sound:Play()
77
+ end
78
+ end
79
+ return spatialHandles
80
+ end
81
+ --[[
82
+ *
83
+ * Stop every Sound from a Sound Category
84
+ * @param name Sound Category
85
+
86
+ ]]
87
+ local function stopCategory(name)
58
88
  local config = definitions[name]
59
89
  local ReplicatedStorage = game:GetService("ReplicatedStorage")
60
90
  local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
@@ -75,13 +105,365 @@ local function createSoundCategoryRegistry(definitions)
75
105
  _result = _result:IsA("Sound")
76
106
  end
77
107
  if _result then
78
- _sound:Play()
108
+ _sound:Stop()
109
+ end
110
+ end
111
+ end
112
+ --[[
113
+ *
114
+ * Stops every Sound from every Sound Category
115
+
116
+ ]]
117
+ local function stopAllCategories()
118
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
119
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
120
+ if not soundsFolder then
121
+ return nil
122
+ end
123
+ for _, category in soundsFolder:GetChildren() do
124
+ if not category:IsA("Folder") then
125
+ continue
126
+ end
127
+ for _1, sound in category:GetChildren() do
128
+ if not sound:IsA("Sound") then
129
+ continue
130
+ end
131
+ sound:Stop()
132
+ end
133
+ end
134
+ end
135
+ --[[
136
+ *
137
+ * Set the Volume of a Sound Category
138
+ * @param category Sound Category
139
+ * @param volume Volume
140
+
141
+ ]]
142
+ local function setCategoryVolume(category, volume)
143
+ local config = definitions[category]
144
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
145
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
146
+ if not soundsFolder then
147
+ return nil
148
+ end
149
+ local categoryFolder = soundsFolder:FindFirstChild(config.category)
150
+ if not categoryFolder then
151
+ return nil
152
+ end
153
+ for _, sound in categoryFolder:GetChildren() do
154
+ if not sound:IsA("Sound") then
155
+ continue
156
+ end
157
+ sound.Volume = volume
158
+ end
159
+ end
160
+ --[[
161
+ *
162
+ * Set the Global Volume of every Sound Category
163
+ * @param volume Volume
164
+
165
+ ]]
166
+ local function setGlobalCategoryVolume(volume)
167
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
168
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
169
+ if not soundsFolder then
170
+ return nil
171
+ end
172
+ for _, category in soundsFolder:GetChildren() do
173
+ if not category:IsA("Folder") then
174
+ continue
175
+ end
176
+ for _1, sound in category:GetChildren() do
177
+ if not sound:IsA("Sound") then
178
+ continue
179
+ end
180
+ sound.Volume = volume
181
+ end
182
+ end
183
+ end
184
+ --[[
185
+ *
186
+ * Whether a Category is playing or not
187
+ * @param category Sound Category
188
+ * @returns Whether a Category is playing or not
189
+
190
+ ]]
191
+ local function isCategoryPlaying(category)
192
+ local config = definitions[category]
193
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
194
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
195
+ if not soundsFolder then
196
+ return false
197
+ end
198
+ local categoryFolder = soundsFolder:FindFirstChild(config.category)
199
+ if not categoryFolder then
200
+ return false
201
+ end
202
+ local _exp = categoryFolder:GetChildren()
203
+ -- β–Ό ReadonlyArray.filter β–Ό
204
+ local _newValue = {}
205
+ local _callback = function(sound)
206
+ return sound:IsA("Sound")
207
+ end
208
+ local _length = 0
209
+ for _k, _v in _exp do
210
+ if _callback(_v, _k - 1, _exp) == true then
211
+ _length += 1
212
+ _newValue[_length] = _v
213
+ end
214
+ end
215
+ -- β–² ReadonlyArray.filter β–²
216
+ local sounds = _newValue
217
+ if #sounds == 0 then
218
+ return false
219
+ end
220
+ for _, sound in sounds do
221
+ if not sound.IsPlaying then
222
+ return false
79
223
  end
80
224
  end
225
+ return true
226
+ end
227
+ --[[
228
+ *
229
+ * Play a Sound from a Category
230
+ * @param category Sound Category
231
+ * @param sound Sound Instance
232
+
233
+ ]]
234
+ local function playSoundFromCategory(category, sound)
235
+ loadCategory(category)
236
+ local config = definitions[category]
237
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
238
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
239
+ if not soundsFolder then
240
+ return nil
241
+ end
242
+ local categoryFolder = soundsFolder:FindFirstChild(config.category)
243
+ if not categoryFolder then
244
+ return nil
245
+ end
246
+ local soundInstance = categoryFolder:FindFirstChild(sound)
247
+ local _result = soundInstance
248
+ if _result ~= nil then
249
+ _result = _result:IsA("Sound")
250
+ end
251
+ if _result then
252
+ soundInstance:Play()
253
+ end
254
+ end
255
+ --[[
256
+ *
257
+ * Reset a Sound Category Timeposition to 0
258
+ * @param category Sound Category
259
+
260
+ ]]
261
+ local function resetCategory(category)
262
+ local config = definitions[category]
263
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
264
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
265
+ if not soundsFolder then
266
+ return nil
267
+ end
268
+ local categoryFolder = soundsFolder:FindFirstChild(config.category)
269
+ if not categoryFolder then
270
+ return nil
271
+ end
272
+ for _, sound in categoryFolder:GetChildren() do
273
+ if not sound:IsA("Sound") then
274
+ continue
275
+ end
276
+ sound.TimePosition = 0
277
+ end
278
+ end
279
+ --[[
280
+ *
281
+ * Resets every Sound from every Sound Category
282
+ * @param category Sound Category
283
+
284
+ ]]
285
+ local function resetAllCategories(category)
286
+ local config = definitions[category]
287
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
288
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
289
+ if not soundsFolder then
290
+ return nil
291
+ end
292
+ for _, folder in soundsFolder:GetChildren() do
293
+ if not folder:IsA("Folder") then
294
+ continue
295
+ end
296
+ for _1, sound in folder:GetChildren() do
297
+ if not sound:IsA("Sound") then
298
+ continue
299
+ end
300
+ sound.TimePosition = 0
301
+ end
302
+ end
303
+ end
304
+ --[[
305
+ *
306
+ * Preloads a Sound Category
307
+ * @param category Sound Category
308
+
309
+ ]]
310
+ local function preloadCategory(category)
311
+ loadCategory(category)
312
+ end
313
+ --[[
314
+ *
315
+ * Preloads every Sound Category
316
+
317
+ ]]
318
+ local function preloadAllCategories()
319
+ for category in pairs(definitions) do
320
+ loadCategory(category)
321
+ end
322
+ end
323
+ --[[
324
+ *
325
+ * Smoothly fade in a Sound Category
326
+ * @param category Sound Category
327
+ * @param duration Duration
328
+ * @param volume Volume
329
+
330
+ ]]
331
+ local function fadeInCategory(category, duration, volume)
332
+ local config = definitions[category]
333
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
334
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
335
+ if not soundsFolder then
336
+ return nil
337
+ end
338
+ local categoryFolder = soundsFolder:FindFirstChild(config.category)
339
+ if not categoryFolder then
340
+ return nil
341
+ end
342
+ for _, sound in categoryFolder:GetChildren() do
343
+ if not sound:IsA("Sound") then
344
+ continue
345
+ end
346
+ sound.Volume = 0
347
+ sound:Play()
348
+ local step = 0.05
349
+ local interval = duration * step
350
+ task.spawn(function()
351
+ local vol = 0
352
+ while vol < volume do
353
+ vol += step
354
+ sound.Volume = math.clamp(vol, 0, volume)
355
+ task.wait(interval)
356
+ end
357
+ end)
358
+ end
359
+ end
360
+ --[[
361
+ *
362
+ * Smoothly fade out a Sound Category
363
+ * @param category Sound Category
364
+ * @param duration Duration
365
+ * @param targetVolume Target Volume
366
+
367
+ ]]
368
+ local function fadeOutCategory(category, duration, targetVolume)
369
+ local config = definitions[category]
370
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
371
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
372
+ if not soundsFolder then
373
+ return nil
374
+ end
375
+ local categoryFolder = soundsFolder:FindFirstChild(config.category)
376
+ if not categoryFolder then
377
+ return nil
378
+ end
379
+ for _, sound in categoryFolder:GetChildren() do
380
+ if not sound:IsA("Sound") then
381
+ continue
382
+ end
383
+ local startVolume = sound.Volume
384
+ local _condition = targetVolume
385
+ if _condition == nil then
386
+ _condition = 0
387
+ end
388
+ local endVolume = _condition
389
+ local step = 0.05
390
+ local interval = duration * step
391
+ task.spawn(function()
392
+ local vol = startVolume
393
+ while vol > endVolume do
394
+ vol = math.clamp(vol - step, endVolume, startVolume)
395
+ sound.Volume = vol
396
+ task.wait(interval)
397
+ end
398
+ sound.Volume = endVolume
399
+ if endVolume == 0 then
400
+ sound:Stop()
401
+ end
402
+ end)
403
+ end
404
+ end
405
+ --[[
406
+ *
407
+ * Does something on Category end
408
+ * @param category Sound Category
409
+ * @param callback Callback
410
+
411
+ ]]
412
+ local function onCategoryEnd(category, callback)
413
+ local config = definitions[category]
414
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
415
+ local soundsFolder = ReplicatedStorage:FindFirstChild("Sounds")
416
+ if not soundsFolder then
417
+ return nil
418
+ end
419
+ local categoryFolder = soundsFolder:FindFirstChild(config.category)
420
+ if not categoryFolder then
421
+ return nil
422
+ end
423
+ local _exp = categoryFolder:GetChildren()
424
+ -- β–Ό ReadonlyArray.filter β–Ό
425
+ local _newValue = {}
426
+ local _callback = function(s)
427
+ return s:IsA("Sound")
428
+ end
429
+ local _length = 0
430
+ for _k, _v in _exp do
431
+ if _callback(_v, _k - 1, _exp) == true then
432
+ _length += 1
433
+ _newValue[_length] = _v
434
+ end
435
+ end
436
+ -- β–² ReadonlyArray.filter β–²
437
+ local sounds = _newValue
438
+ if #sounds == 0 then
439
+ return nil
440
+ end
441
+ local remaining = #sounds
442
+ for _, sound in sounds do
443
+ sound.Ended:Connect(function()
444
+ remaining -= 1
445
+ if remaining <= 0 then
446
+ callback()
447
+ end
448
+ end)
449
+ end
81
450
  end
82
451
  return {
83
452
  loadCategory = loadCategory,
84
453
  playCategory = playCategory,
454
+ stopCategory = stopCategory,
455
+ stopAllCategories = stopAllCategories,
456
+ setCategoryVolume = setCategoryVolume,
457
+ setGlobalCategoryVolume = setGlobalCategoryVolume,
458
+ isCategoryPlaying = isCategoryPlaying,
459
+ playSoundFromCategory = playSoundFromCategory,
460
+ resetCategory = resetCategory,
461
+ resetAllCategories = resetAllCategories,
462
+ preloadAllCategories = preloadAllCategories,
463
+ preloadCategory = preloadCategory,
464
+ fadeInCategory = fadeInCategory,
465
+ fadeOutCategory = fadeOutCategory,
466
+ onCategoryEnd = onCategoryEnd,
85
467
  }
86
468
  end
87
469
  return {
@@ -1,21 +1,32 @@
1
- import { SoundOptions } from "./options";
1
+ import { SoundHandle, SoundOptions } from "./options";
2
2
  /**
3
3
  * Create a sound Registry
4
4
  * @param definitions Define the Sounds
5
5
  */
6
6
  export declare function createSoundRegistry<T extends Record<string, SoundOptions>>(definitions: T): {
7
- play: (name: keyof T) => void;
8
- stop: (name: keyof T) => void;
7
+ play: (name: keyof T, spatial?: {
8
+ emitters: BasePart[];
9
+ }) => SoundHandle | undefined;
10
+ stop: (name: keyof T, spatial?: {
11
+ emitters: BasePart[];
12
+ }) => void;
9
13
  preloadAll: () => void;
10
14
  load: (name: keyof T) => void;
11
- fadeIn: (soundName: keyof T, duration: number, volume: number) => void;
12
- fadeOut: (soundName: keyof T, duration: number, targetVolume?: number) => void;
15
+ fadeIn: (soundName: keyof T, duration: number, volume: number, spatial?: {
16
+ emitters: BasePart[];
17
+ }) => void;
18
+ fadeOut: (soundName: keyof T, duration: number, targetVolume?: number, spatial?: {
19
+ emitters: BasePart[];
20
+ }) => void;
13
21
  reset: (sound: keyof T) => void;
14
22
  setTimePosition: (sound: keyof T, timePosition: number) => void;
15
23
  stopAll: (reset?: true) => void;
16
24
  preload: (sound: keyof T) => void;
17
25
  setGlobalVolume: (volume: number) => void;
18
- setVolume: (sound: keyof T, volume: number) => void;
26
+ setVolume: (sound: keyof T, volume: number, spatial?: {
27
+ emitters: BasePart[];
28
+ }) => void;
19
29
  resetAll: (sound: keyof T) => void;
20
30
  onEnd: (sound: keyof T, callback: () => void) => void;
31
+ isPlaying: (sound: keyof T) => boolean;
21
32
  };
@@ -1,4 +1,6 @@
1
1
  -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local createSpatialHandle = TS.import(script, script.Parent, "createSpatialHandle").createSpatialHandle
2
4
  --[[
3
5
  *
4
6
  * Create a sound Registry
@@ -6,6 +8,7 @@
6
8
 
7
9
  ]]
8
10
  local function createSoundRegistry(definitions)
11
+ local spatialHandles = {}
9
12
  local ReplicatedStorage = game:GetService("ReplicatedStorage")
10
13
  local folder = ReplicatedStorage:FindFirstChild("Sounds")
11
14
  if not folder then
@@ -43,28 +46,58 @@ local function createSoundRegistry(definitions)
43
46
  *
44
47
  * Plays a Sound
45
48
  * @param name Define which Sound should be played
49
+ * @param spatial Array of Baseparts
46
50
 
47
51
  ]]
48
- local function play(name)
49
- load(name)
50
- local sound = folder:FindFirstChild(name)
51
- local _result = sound
52
- if _result ~= nil then
53
- _result:Play()
52
+ local function play(name, spatial)
53
+ local config = definitions[name]
54
+ if not spatial or not spatial.emitters then
55
+ load(name)
56
+ local sound = folder:FindFirstChild(name)
57
+ local _result = sound
58
+ if _result ~= nil then
59
+ _result:Play()
60
+ end
61
+ return nil
62
+ else
63
+ local emittersArray = spatial.emitters
64
+ local _exp = config.id
65
+ local _condition = config.volume
66
+ if _condition == nil then
67
+ _condition = 1
68
+ end
69
+ local handle = createSpatialHandle(_exp, emittersArray, _condition)
70
+ local _name = name
71
+ spatialHandles[_name] = handle
72
+ handle:play()
73
+ return handle
54
74
  end
55
75
  end
56
76
  --[[
57
77
  *
58
78
  * Stops a Sound
59
79
  * @param name Define which Sound should be stopped
80
+ * @param spatial Array of Baseparts
60
81
 
61
82
  ]]
62
- local function stop(name)
63
- local sound = folder:FindFirstChild(name)
64
- local _result = sound
65
- if _result ~= nil then
66
- _result:Stop()
83
+ local function stop(name, spatial)
84
+ if not spatial then
85
+ local sound = folder:FindFirstChild(name)
86
+ local _result = sound
87
+ if _result ~= nil then
88
+ _result:Stop()
89
+ end
90
+ return nil
91
+ end
92
+ local _name = name
93
+ local handle = spatialHandles[_name]
94
+ if not handle then
95
+ return nil
67
96
  end
97
+ handle:stop()
98
+ handle:destroy()
99
+ local _name_1 = name
100
+ spatialHandles[_name_1] = nil
68
101
  end
69
102
  --[[
70
103
  *
@@ -82,22 +115,44 @@ local function createSoundRegistry(definitions)
82
115
  * @param soundName Sound Instance
83
116
  * @param duration Time in Seconds
84
117
  * @param volume Volume
118
+ * @param spatial Array of Baseparts
85
119
 
86
120
  ]]
87
- local function fadeIn(soundName, duration, volume)
88
- local sound = folder:FindFirstChild(soundName)
89
- sound.Volume = 0
90
- sound:Play()
91
- local step = 0.05
92
- local interval = duration * step
93
- task.spawn(function()
94
- local vol = 0
95
- while vol < volume do
96
- vol += step
97
- sound.Volume = math.clamp(vol, 0, volume)
98
- task.wait(interval)
121
+ local function fadeIn(soundName, duration, volume, spatial)
122
+ local config = definitions[soundName]
123
+ if not spatial or not spatial.emitters then
124
+ local sound = folder:FindFirstChild(soundName)
125
+ sound.Volume = 0
126
+ sound:Play()
127
+ local step = 0.05
128
+ local interval = duration * step
129
+ task.spawn(function()
130
+ local vol = 0
131
+ while vol < volume do
132
+ vol += step
133
+ sound.Volume = math.clamp(vol, 0, volume)
134
+ task.wait(interval)
135
+ end
136
+ end)
137
+ else
138
+ local emittersArray = spatial.emitters
139
+ local _exp = config.id
140
+ local _condition = config.volume
141
+ if _condition == nil then
142
+ _condition = 1
143
+ end
144
+ local handle = createSpatialHandle(_exp, emittersArray, _condition)
145
+ local _self = handle
146
+ local _result = _self.fadeIn
147
+ if _result ~= nil then
148
+ _result(_self, duration, volume)
99
149
  end
100
- end)
150
+ if not config.loop == true then
151
+ handle:played(function()
152
+ handle:destroy()
153
+ end)
154
+ end
155
+ end
101
156
  end
102
157
  --[[
103
158
  *
@@ -105,33 +160,55 @@ local function createSoundRegistry(definitions)
105
160
  * @param soundName Sound name from Registry
106
161
  * @param duration Time in seconds
107
162
  * @param targetVolume Optional target volume (default 0)
163
+ * @param spatial Array of Baseparts
108
164
 
109
165
  ]]
110
- local function fadeOut(soundName, duration, targetVolume)
111
- local sound = folder:FindFirstChild(soundName)
112
- if not sound then
113
- return nil
114
- end
115
- local startVolume = sound.Volume
116
- local _condition = targetVolume
117
- if _condition == nil then
118
- _condition = 0
119
- end
120
- local endVolume = _condition
121
- local step = 0.05
122
- local interval = duration * step
123
- task.spawn(function()
124
- local vol = startVolume
125
- while vol > endVolume do
126
- vol = math.clamp(vol - step, endVolume, startVolume)
127
- sound.Volume = vol
128
- task.wait(interval)
166
+ local function fadeOut(soundName, duration, targetVolume, spatial)
167
+ local config = definitions[soundName]
168
+ if not spatial or not spatial.emitters then
169
+ local sound = folder:FindFirstChild(soundName)
170
+ if not sound then
171
+ return nil
129
172
  end
130
- sound.Volume = endVolume
131
- if endVolume == 0 then
132
- sound:Stop()
173
+ local startVolume = sound.Volume
174
+ local _condition = targetVolume
175
+ if _condition == nil then
176
+ _condition = 0
133
177
  end
134
- end)
178
+ local endVolume = _condition
179
+ local step = 0.05
180
+ local interval = duration * step
181
+ task.spawn(function()
182
+ local vol = startVolume
183
+ while vol > endVolume do
184
+ vol = math.clamp(vol - step, endVolume, startVolume)
185
+ sound.Volume = vol
186
+ task.wait(interval)
187
+ end
188
+ sound.Volume = endVolume
189
+ if endVolume == 0 then
190
+ sound:Stop()
191
+ end
192
+ end)
193
+ else
194
+ local emittersArray = spatial.emitters
195
+ local _exp = config.id
196
+ local _condition = config.volume
197
+ if _condition == nil then
198
+ _condition = 1
199
+ end
200
+ local handle = createSpatialHandle(_exp, emittersArray, _condition)
201
+ local _self = handle
202
+ local _result = _self.fadeOut
203
+ if _result ~= nil then
204
+ _result(_self, duration)
205
+ end
206
+ if not config.loop == true then
207
+ handle:played(function()
208
+ handle:destroy()
209
+ end)
210
+ end
211
+ end
135
212
  end
136
213
  --[[
137
214
  *
@@ -195,14 +272,21 @@ local function createSoundRegistry(definitions)
195
272
  * Set Sound Volume
196
273
  * @param sound Sound Instance
197
274
  * @param volume Sound Volume
275
+ * @param spatial Array of Baseparts
198
276
 
199
277
  ]]
200
- local function setVolume(sound, volume)
201
- local _sound = folder:FindFirstChild(sound)
202
- if not (sound ~= 0 and sound == sound and sound ~= "" and sound) then
203
- return nil
278
+ local function setVolume(sound, volume, spatial)
279
+ local config = definitions[sound]
280
+ if not spatial or spatial.emitters then
281
+ local _sound = folder:FindFirstChild(sound)
282
+ if not (sound ~= 0 and sound == sound and sound ~= "" and sound) then
283
+ return nil
284
+ end
285
+ _sound.Volume = volume
286
+ else
287
+ local emittersArray = spatial.emitters
288
+ local handle = createSpatialHandle(config.id, emittersArray, volume)
204
289
  end
205
- _sound.Volume = volume
206
290
  end
207
291
  --[[
208
292
  *
@@ -222,7 +306,7 @@ local function createSoundRegistry(definitions)
222
306
  end
223
307
  --[[
224
308
  *
225
- * Plays Sound on Event Callback
309
+ * Does something on Sound end
226
310
  * @param name Sound Name
227
311
  * @param callback Callback
228
312
 
@@ -243,6 +327,21 @@ local function createSoundRegistry(definitions)
243
327
  local function preload(sound)
244
328
  load(sound)
245
329
  end
330
+ --[[
331
+ *
332
+ * Check whether a Sound is playing
333
+ * @param sound Sound Instance
334
+ * @returns Boolean
335
+
336
+ ]]
337
+ local function isPlaying(sound)
338
+ local _sound = folder:WaitForChild(sound)
339
+ if _sound.IsPlaying == true then
340
+ return true
341
+ else
342
+ return false
343
+ end
344
+ end
246
345
  return {
247
346
  play = play,
248
347
  stop = stop,
@@ -258,6 +357,7 @@ local function createSoundRegistry(definitions)
258
357
  setVolume = setVolume,
259
358
  resetAll = resetAll,
260
359
  onEnd = onEnd,
360
+ isPlaying = isPlaying,
261
361
  }
262
362
  end
263
363
  return {
@@ -0,0 +1,7 @@
1
+ import { SoundHandle } from "./options";
2
+ /**
3
+ * Create a Spatial Handle
4
+ * @param assetId Asset ID
5
+ * @param emitters Emitter
6
+ */
7
+ export declare function createSpatialHandle(assetId: string, emitters: BasePart[], volume: number): SoundHandle;
@@ -0,0 +1,59 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _functions = TS.import(script, script.Parent.Parent, "utils", "functions")
4
+ local fadeIn = _functions.fadeIn
5
+ local fadeOut = _functions.fadeOut
6
+ --[[
7
+ *
8
+ * Create a Spatial Handle
9
+ * @param assetId Asset ID
10
+ * @param emitters Emitter
11
+
12
+ ]]
13
+ local function createSpatialHandle(assetId, emitters, volume)
14
+ local player = Instance.new("AudioPlayer")
15
+ player.Asset = assetId
16
+ player.Volume = volume
17
+ player.Parent = game:GetService("ReplicatedStorage")
18
+ local emitterInstances = {}
19
+ for _, part in emitters do
20
+ local emitter = Instance.new("AudioEmitter")
21
+ emitter.Parent = part
22
+ local wire = Instance.new("Wire")
23
+ wire.SourceInstance = player
24
+ wire.TargetInstance = emitter
25
+ wire.Parent = emitter
26
+ table.insert(emitterInstances, emitter)
27
+ end
28
+ return {
29
+ play = function(self)
30
+ player:Play()
31
+ end,
32
+ stop = function(self)
33
+ player:Stop()
34
+ end,
35
+ fadeIn = function(self, duration, volume)
36
+ fadeIn(player, volume, duration)
37
+ end,
38
+ fadeOut = function(self, duration)
39
+ fadeOut(player, duration)
40
+ end,
41
+ destroy = function(self)
42
+ player:Destroy()
43
+ -- β–Ό ReadonlyArray.forEach β–Ό
44
+ local _callback = function(e)
45
+ return e:Destroy()
46
+ end
47
+ for _k, _v in emitterInstances do
48
+ _callback(_v, _k - 1, emitterInstances)
49
+ end
50
+ -- β–² ReadonlyArray.forEach β–²
51
+ end,
52
+ played = function(self, callback)
53
+ player.Ended:Connect(callback)
54
+ end,
55
+ }
56
+ end
57
+ return {
58
+ createSpatialHandle = createSpatialHandle,
59
+ }
@@ -2,6 +2,18 @@ export interface SoundOptions {
2
2
  volume?: number;
3
3
  loop?: boolean;
4
4
  id: string;
5
+ spatial?: {
6
+ attenuation?: number;
7
+ directional?: boolean;
8
+ };
9
+ }
10
+ export interface SoundHandle {
11
+ play(): void;
12
+ stop(): void;
13
+ fadeIn?(duration: number, volume: number): void;
14
+ fadeOut?(duration: number): void;
15
+ destroy(): void;
16
+ played(callback: () => void): void;
5
17
  }
6
18
  interface SoundDefinition {
7
19
  id: string;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Fades in a Sound
3
+ * @param player AudioPlayer
4
+ * @param targetVolume Target volume
5
+ * @param duration Duration
6
+ */
7
+ export declare function fadeIn(player: AudioPlayer, targetVolume: number, duration: number): void;
8
+ /**
9
+ * Smoothly fades out a Sound
10
+ * @param player AudioPlayer
11
+ * @param duration Duration
12
+ */
13
+ export declare function fadeOut(player: AudioPlayer, duration: number): void;
@@ -0,0 +1,47 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ --[[
3
+ *
4
+ * Fades in a Sound
5
+ * @param player AudioPlayer
6
+ * @param targetVolume Target volume
7
+ * @param duration Duration
8
+
9
+ ]]
10
+ local function fadeIn(player, targetVolume, duration)
11
+ player.Volume = 0
12
+ player:Play()
13
+ local steps = 20
14
+ task.spawn(function()
15
+ for i = 0, steps do
16
+ player.Volume = (i / steps) * targetVolume
17
+ task.wait(duration / steps)
18
+ end
19
+ end)
20
+ end
21
+ --[[
22
+ *
23
+ * Smoothly fades out a Sound
24
+ * @param player AudioPlayer
25
+ * @param duration Duration
26
+
27
+ ]]
28
+ local function fadeOut(player, duration)
29
+ local _condition = player.Volume
30
+ if _condition == nil then
31
+ _condition = 1
32
+ end
33
+ local startVolume = _condition
34
+ local steps = 20
35
+ local interval = duration / steps
36
+ task.spawn(function()
37
+ for i = steps, 0, -1 do
38
+ player.Volume = (i / steps) * startVolume
39
+ task.wait(interval)
40
+ end
41
+ player:Stop()
42
+ end)
43
+ end
44
+ return {
45
+ fadeIn = fadeIn,
46
+ fadeOut = fadeOut,
47
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@rbxts/sound-manager",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "A sound manager for Roblox-Typescript projects.",
5
5
  "main": "out/init.lua",
6
6
  "scripts": {
7
7
  "build": "rbxtsc",
8
8
  "watch": "rbxtsc -w",
9
- "prepublishOnly": "npm run build"
9
+ "prepublishOnly": "npm run build",
10
+ "start": "npm pack && npm run watch"
10
11
  },
11
12
  "keywords": [],
12
13
  "author": "Easy-Build-Studio",