@quenty/soundplayer 7.25.0 → 7.25.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [7.25.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/soundplayer@7.25.0...@quenty/soundplayer@7.25.1) (2026-01-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Add typing to SoundPlayer ([a7e538e](https://github.com/Quenty/NevermoreEngine/commit/a7e538eddcb002327c1fdff4e2a60c769f0a1ba5))
12
+ * Update the sound group package to include sound volume multipliers ([bf9591f](https://github.com/Quenty/NevermoreEngine/commit/bf9591fe2d08dbefae47f8c449b7bbf20c01fdfe))
13
+
14
+
15
+
16
+
17
+
6
18
  # [7.25.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/soundplayer@7.24.4...@quenty/soundplayer@7.25.0) (2026-01-04)
7
19
 
8
20
  **Note:** Version bump only for package @quenty/soundplayer
package/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2014-2025 James Onnen (Quenty)
3
+ Copyright (c) 2014-2026 James Onnen (Quenty)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/soundplayer",
3
- "version": "7.25.0",
3
+ "version": "7.25.1",
4
4
  "description": "Sound playback helper",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -27,7 +27,7 @@
27
27
  "dependencies": {
28
28
  "@quenty/adorneeutils": "^3.3.3",
29
29
  "@quenty/baseobject": "^10.9.1",
30
- "@quenty/blend": "^12.23.0",
30
+ "@quenty/blend": "^12.23.1",
31
31
  "@quenty/brio": "^14.21.0",
32
32
  "@quenty/instanceutils": "^13.21.0",
33
33
  "@quenty/loader": "^10.9.1",
@@ -41,12 +41,12 @@
41
41
  "@quenty/signal": "^7.11.3",
42
42
  "@quenty/sounds": "^10.14.4",
43
43
  "@quenty/table": "^3.9.0",
44
- "@quenty/transitionmodel": "^7.24.0",
45
- "@quenty/valueobject": "^13.22.0",
44
+ "@quenty/transitionmodel": "^7.24.1",
45
+ "@quenty/valueobject": "^13.22.1",
46
46
  "@quentystudios/t": "^3.0.0"
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"
50
50
  },
51
- "gitHead": "22144a14aa443a177ff307edd3813a838f8c1d5f"
51
+ "gitHead": "5232cd2c58ca0dcdf591dd8ae78995211da2f3e2"
52
52
  }
@@ -1,5 +1,9 @@
1
- --!nonstrict
1
+ --!strict
2
2
  --[=[
3
+ This class provides layered synchronized sound playback with looping and scheduling, which is useful for
4
+ implementing complex ambient soundscapes or music tracks that require multiple layers to be played in sync, for example,
5
+ constructed music that adapts to game states.
6
+
3
7
  @class LayeredLoopedSoundPlayer
4
8
  ]=]
5
9
 
@@ -12,14 +16,35 @@ local SoundLoopScheduleUtils = require("SoundLoopScheduleUtils")
12
16
  local SoundUtils = require("SoundUtils")
13
17
  local SpringTransitionModel = require("SpringTransitionModel")
14
18
  local ValueObject = require("ValueObject")
15
- local t = require("t")
19
+ local t: any = require("t")
16
20
 
17
21
  local LayeredLoopedSoundPlayer = setmetatable({}, SpringTransitionModel)
18
22
  LayeredLoopedSoundPlayer.ClassName = "LayeredLoopedSoundPlayer"
19
23
  LayeredLoopedSoundPlayer.__index = LayeredLoopedSoundPlayer
20
24
 
21
- function LayeredLoopedSoundPlayer.new(soundParent)
22
- local self = setmetatable(SpringTransitionModel.new(), LayeredLoopedSoundPlayer)
25
+ export type LayeredLoopedSoundPlayer =
26
+ typeof(setmetatable(
27
+ {} :: {
28
+ _layerMaid: Maid.Maid,
29
+ _soundParent: ValueObject.ValueObject<Instance?>,
30
+ _soundGroup: ValueObject.ValueObject<SoundGroup?>,
31
+ _bpm: ValueObject.ValueObject<number?>,
32
+ _defaultCrossFadeTime: ValueObject.ValueObject<number>,
33
+ _volumeMultiplier: ValueObject.ValueObject<number>,
34
+ _layers: { [string]: LoopedSoundPlayer.LoopedSoundPlayer },
35
+ },
36
+ {} :: typeof({ __index = LayeredLoopedSoundPlayer })
37
+ ))
38
+ & SpringTransitionModel.SpringTransitionModel<number>
39
+
40
+ --[=[
41
+ Constructs a new LayeredLoopedSoundPlayer.
42
+
43
+ @param soundParent Instance? -- Optional parent for sounds
44
+ @return LayeredLoopedSoundPlayer
45
+ ]=]
46
+ function LayeredLoopedSoundPlayer.new(soundParent: Instance?): LayeredLoopedSoundPlayer
47
+ local self: LayeredLoopedSoundPlayer = setmetatable(SpringTransitionModel.new() :: any, LayeredLoopedSoundPlayer)
23
48
 
24
49
  self._layerMaid = self._maid:Add(Maid.new())
25
50
 
@@ -38,29 +63,61 @@ function LayeredLoopedSoundPlayer.new(soundParent)
38
63
  return self
39
64
  end
40
65
 
41
- function LayeredLoopedSoundPlayer:SetDefaultCrossFadeTime(crossFadeTime: ValueObject.Mountable<number>)
66
+ --[=[
67
+ Sets the default cross fade time for the LayeredLoopedSoundPlayer.
68
+ ]=]
69
+ function LayeredLoopedSoundPlayer.SetDefaultCrossFadeTime(
70
+ self: LayeredLoopedSoundPlayer,
71
+ crossFadeTime: ValueObject.Mountable<number>
72
+ ): () -> ()
42
73
  return self._defaultCrossFadeTime:Mount(crossFadeTime)
43
74
  end
44
75
 
45
- function LayeredLoopedSoundPlayer:SetVolumeMultiplier(volumeMultiplier: ValueObject.Mountable<number>)
76
+ --[=[
77
+ Sets the volume multiplier for the LayeredLoopedSoundPlayer.
78
+ ]=]
79
+ function LayeredLoopedSoundPlayer.SetVolumeMultiplier(
80
+ self: LayeredLoopedSoundPlayer,
81
+ volumeMultiplier: ValueObject.Mountable<number>
82
+ ): () -> ()
46
83
  return self._volumeMultiplier:Mount(volumeMultiplier)
47
84
  end
48
85
 
49
- function LayeredLoopedSoundPlayer:SetBPM(bpm: ValueObject.Mountable<number?>)
50
- self._bpm.Value = bpm
86
+ --[=[
87
+ Sets the BPM for syncing sound playback.
88
+ ]=]
89
+ function LayeredLoopedSoundPlayer.SetBPM(self: LayeredLoopedSoundPlayer, bpm: ValueObject.Mountable<number?>): () -> ()
90
+ return self._bpm:Mount(bpm)
51
91
  end
52
92
 
53
- function LayeredLoopedSoundPlayer:SetSoundParent(soundParent: Instance?)
93
+ --[=[
94
+ Sets the parent instance for the LayeredLoopedSoundPlayer.
95
+ ]=]
96
+ function LayeredLoopedSoundPlayer.SetSoundParent(self: LayeredLoopedSoundPlayer, soundParent: Instance?): ()
54
97
  assert(typeof(soundParent) == "Instance" or soundParent == nil, "Bad soundParent")
55
98
 
56
99
  self._soundParent.Value = soundParent
57
100
  end
58
101
 
59
- function LayeredLoopedSoundPlayer:SetSoundGroup(soundGroup: SoundGroup?)
102
+ --[=[
103
+ Sets the sound group for the LayeredLoopedSoundPlayer.
104
+ ]=]
105
+ function LayeredLoopedSoundPlayer.SetSoundGroup(
106
+ self: LayeredLoopedSoundPlayer,
107
+ soundGroup: ValueObject.Mountable<SoundGroup?>
108
+ ): () -> ()
60
109
  return self._soundGroup:Mount(soundGroup)
61
110
  end
62
111
 
63
- function LayeredLoopedSoundPlayer:Swap(layerId: string, soundId, scheduleOptions)
112
+ --[=[
113
+ Swaps the layer to play the given sound on loop schedule.
114
+ ]=]
115
+ function LayeredLoopedSoundPlayer.Swap(
116
+ self: LayeredLoopedSoundPlayer,
117
+ layerId: string,
118
+ soundId: SoundUtils.SoundId,
119
+ scheduleOptions: SoundLoopScheduleUtils.SoundLoopSchedule?
120
+ ): ()
64
121
  assert(type(layerId) == "string", "Bad layerId")
65
122
  assert(SoundUtils.isConvertableToRbxAsset(soundId) or soundId == nil, "Bad soundId")
66
123
  assert(SoundLoopScheduleUtils.isLoopedSchedule(scheduleOptions) or scheduleOptions == nil, "Bad scheduleOptions")
@@ -69,7 +126,15 @@ function LayeredLoopedSoundPlayer:Swap(layerId: string, soundId, scheduleOptions
69
126
  layer:Swap(soundId, scheduleOptions)
70
127
  end
71
128
 
72
- function LayeredLoopedSoundPlayer:SwapOnLoop(layerId, soundId, scheduleOptions)
129
+ --[=[
130
+ Swaps the layer to play on the next loop.
131
+ ]=]
132
+ function LayeredLoopedSoundPlayer.SwapOnLoop(
133
+ self: LayeredLoopedSoundPlayer,
134
+ layerId: string,
135
+ soundId: SoundUtils.SoundId,
136
+ scheduleOptions: SoundLoopScheduleUtils.SoundLoopSchedule?
137
+ ): ()
73
138
  assert(type(layerId) == "string", "Bad layerId")
74
139
  assert(SoundUtils.isConvertableToRbxAsset(soundId) or soundId == nil, "Bad soundId")
75
140
 
@@ -77,16 +142,32 @@ function LayeredLoopedSoundPlayer:SwapOnLoop(layerId, soundId, scheduleOptions)
77
142
  layer:SwapOnLoop(soundId, scheduleOptions)
78
143
  end
79
144
 
80
- function LayeredLoopedSoundPlayer:SwapToSamples(layerId, soundId, scheduleOptions)
145
+ --[=[
146
+ Swaps the layer to play from a list of samples.
147
+ ]=]
148
+ function LayeredLoopedSoundPlayer.SwapToSamples(
149
+ self: LayeredLoopedSoundPlayer,
150
+ layerId: string,
151
+ soundIdList: { SoundUtils.SoundId },
152
+ scheduleOptions: SoundLoopScheduleUtils.SoundLoopSchedule?
153
+ ): ()
81
154
  assert(type(layerId) == "string", "Bad layerId")
82
- assert(SoundUtils.isConvertableToRbxAsset(soundId) or soundId == nil, "Bad soundId")
155
+ assert(type(soundIdList) == "table", "Bad soundIdList")
83
156
  assert(SoundLoopScheduleUtils.isLoopedSchedule(scheduleOptions) or scheduleOptions == nil, "Bad scheduleOptions")
84
157
 
85
158
  local layer = self:_getOrCreateLayer(layerId)
86
- layer:SwapToSamples(soundId, scheduleOptions)
159
+ layer:SwapToSamples(soundIdList, scheduleOptions)
87
160
  end
88
161
 
89
- function LayeredLoopedSoundPlayer:SwapToChoice(layerId, soundIdList, scheduleOptions)
162
+ --[=[
163
+ Swaps the layer to play a random choice from a list of samples.
164
+ ]=]
165
+ function LayeredLoopedSoundPlayer.SwapToChoice(
166
+ self: LayeredLoopedSoundPlayer,
167
+ layerId: string,
168
+ soundIdList: { SoundUtils.SoundId },
169
+ scheduleOptions: SoundLoopScheduleUtils.SoundLoopSchedule?
170
+ ): ()
90
171
  assert(type(layerId) == "string", "Bad layerId")
91
172
  assert(type(soundIdList) == "table", "Bad soundIdList")
92
173
  assert(SoundLoopScheduleUtils.isLoopedSchedule(scheduleOptions) or scheduleOptions == nil, "Bad scheduleOptions")
@@ -95,23 +176,41 @@ function LayeredLoopedSoundPlayer:SwapToChoice(layerId, soundIdList, scheduleOpt
95
176
  layer:SwapToChoice(soundIdList, scheduleOptions)
96
177
  end
97
178
 
98
- function LayeredLoopedSoundPlayer:PlayOnce(layerId, soundIdList, scheduleOptions)
179
+ --[=[
180
+ Plays the given sound once on the layer.
181
+ ]=]
182
+ function LayeredLoopedSoundPlayer.PlayOnce(
183
+ self: LayeredLoopedSoundPlayer,
184
+ layerId: string,
185
+ soundId: SoundUtils.SoundId,
186
+ scheduleOptions: SoundLoopScheduleUtils.SoundLoopSchedule?
187
+ ): ()
99
188
  assert(type(layerId) == "string", "Bad layerId")
100
- assert(type(soundIdList) == "table", "Bad soundIdList")
101
189
  assert(SoundLoopScheduleUtils.isLoopedSchedule(scheduleOptions) or scheduleOptions == nil, "Bad scheduleOptions")
102
190
 
103
191
  local layer = self:_getOrCreateLayer(layerId)
104
- layer:PlayOnce(soundIdList, scheduleOptions)
192
+ layer:PlayOnce(soundId, scheduleOptions)
105
193
  end
106
194
 
107
- function LayeredLoopedSoundPlayer:PlayOnceOnLoop(layerId, soundId, scheduleOptions)
195
+ --[=[
196
+ Plays the given sound once on the next loop of the layer.
197
+ ]=]
198
+ function LayeredLoopedSoundPlayer.PlayOnceOnLoop(
199
+ self: LayeredLoopedSoundPlayer,
200
+ layerId: string,
201
+ soundId: SoundUtils.SoundId,
202
+ scheduleOptions: SoundLoopScheduleUtils.SoundLoopSchedule?
203
+ ): ()
108
204
  assert(type(layerId) == "string", "Bad layerId")
109
205
 
110
206
  local layer = self:_getOrCreateLayer(layerId)
111
207
  layer:PlayOnceOnLoop(soundId, scheduleOptions)
112
208
  end
113
209
 
114
- function LayeredLoopedSoundPlayer:_getOrCreateLayer(layerId)
210
+ function LayeredLoopedSoundPlayer._getOrCreateLayer(
211
+ self: LayeredLoopedSoundPlayer,
212
+ layerId: string
213
+ ): LoopedSoundPlayer.LoopedSoundPlayer
115
214
  if self._layers[layerId] then
116
215
  return self._layers[layerId]
117
216
  end
@@ -142,7 +241,7 @@ function LayeredLoopedSoundPlayer:_getOrCreateLayer(layerId)
142
241
  maid:GiveTask(Rx.combineLatest({
143
242
  visible = self:ObserveRenderStepped(),
144
243
  multiplier = self._volumeMultiplier:Observe(),
145
- }):Subscribe(function(state)
244
+ }):Subscribe(function(state: any)
146
245
  layer:SetVolumeMultiplier(state.multiplier * state.visible)
147
246
  end))
148
247
 
@@ -158,13 +257,16 @@ function LayeredLoopedSoundPlayer:_getOrCreateLayer(layerId)
158
257
  return layer
159
258
  end
160
259
 
161
- function LayeredLoopedSoundPlayer:StopLayer(layerId: string)
260
+ --[=[
261
+ Stops playback on the given layer.
262
+ ]=]
263
+ function LayeredLoopedSoundPlayer.StopLayer(self: LayeredLoopedSoundPlayer, layerId: string): ()
162
264
  assert(type(layerId) == "string", "Bad layerId")
163
265
 
164
266
  self._layerMaid[layerId] = nil
165
267
  end
166
268
 
167
- function LayeredLoopedSoundPlayer:StopAll()
269
+ function LayeredLoopedSoundPlayer.StopAll(self: LayeredLoopedSoundPlayer): ()
168
270
  self._layerMaid:DoCleaning()
169
271
  end
170
272
 
@@ -8,6 +8,7 @@ local require = require(script.Parent.loader).load(script)
8
8
  local RunService = game:GetService("RunService")
9
9
 
10
10
  local Maid = require("Maid")
11
+ local Observable = require("Observable")
11
12
  local Promise = require("Promise")
12
13
  local PromiseMaidUtils = require("PromiseMaidUtils")
13
14
  local RandomSampler = require("RandomSampler")
@@ -38,7 +39,7 @@ export type LoopedSoundPlayer =
38
39
  _volumeMultiplier: ValueObject.ValueObject<number>,
39
40
  _doSyncSoundPlayback: ValueObject.ValueObject<boolean>,
40
41
  _currentActiveSound: ValueObject.ValueObject<Sound?>,
41
- _currentSoundId: ValueObject.ValueObject<(string | number)?>,
42
+ _currentSoundId: ValueObject.ValueObject<SoundUtils.SoundId?>,
42
43
  _defaultScheduleOptions: SoundLoopScheduleUtils.SoundLoopSchedule,
43
44
  _currentLoopSchedule: ValueObject.ValueObject<SoundLoopScheduleUtils.SoundLoopSchedule>,
44
45
  },
@@ -46,7 +47,7 @@ export type LoopedSoundPlayer =
46
47
  ))
47
48
  & SpringTransitionModel.SpringTransitionModel<number>
48
49
 
49
- function LoopedSoundPlayer.new(soundId: (string | number)?, soundParent: Instance?)
50
+ function LoopedSoundPlayer.new(soundId: (string | number)?, soundParent: Instance?): LoopedSoundPlayer
50
51
  assert(soundId == nil or SoundUtils.isConvertableToRbxAsset(soundId), "Bad soundId")
51
52
 
52
53
  local self: LoopedSoundPlayer = setmetatable(SpringTransitionModel.new() :: any, LoopedSoundPlayer)
@@ -81,31 +82,59 @@ function LoopedSoundPlayer.new(soundId: (string | number)?, soundParent: Instanc
81
82
  return self
82
83
  end
83
84
 
84
- function LoopedSoundPlayer.SetCrossFadeTime(self: LoopedSoundPlayer, crossFadeTime: number)
85
+ --[=[
86
+ Sets the cross fade time for the LoopedSoundPlayer.
87
+ ]=]
88
+ function LoopedSoundPlayer.SetCrossFadeTime(
89
+ self: LoopedSoundPlayer,
90
+ crossFadeTime: ValueObject.Mountable<number>
91
+ ): () -> ()
85
92
  return self._crossFadeTime:Mount(crossFadeTime)
86
93
  end
87
94
 
88
- function LoopedSoundPlayer.SetVolumeMultiplier(self: LoopedSoundPlayer, volume: number)
95
+ --[=[
96
+ Sets the volume multiplier for the LoopedSoundPlayer.
97
+ ]=]
98
+ function LoopedSoundPlayer.SetVolumeMultiplier(self: LoopedSoundPlayer, volume: number): ()
99
+ assert(type(volume) == "number", "Bad volume")
100
+
89
101
  self._volumeMultiplier.Value = volume
90
102
  end
91
103
 
92
- function LoopedSoundPlayer.SetSoundGroup(self: LoopedSoundPlayer, soundGroup: SoundGroup?)
104
+ --[=[
105
+ Sets the sound group for the LoopedSoundPlayer.
106
+ ]=]
107
+ function LoopedSoundPlayer.SetSoundGroup(
108
+ self: LoopedSoundPlayer,
109
+ soundGroup: ValueObject.Mountable<SoundGroup?>
110
+ ): () -> ()
93
111
  return self._soundGroup:Mount(soundGroup)
94
112
  end
95
113
 
96
- function LoopedSoundPlayer.SetBPM(self: LoopedSoundPlayer, bpm: number?)
114
+ --[=[
115
+ Sets the BPM for syncing sound playback.
116
+ ]=]
117
+ function LoopedSoundPlayer.SetBPM(self: LoopedSoundPlayer, bpm: number?): ()
97
118
  assert(type(bpm) == "number" or bpm == nil, "Bad bpm")
98
119
 
99
120
  self._bpm.Value = bpm
100
121
  end
101
122
 
102
- function LoopedSoundPlayer.SetSoundParent(self: LoopedSoundPlayer, parent: Instance?)
123
+ --[=[
124
+ Sets the parent instance for the sound.
125
+ ]=]
126
+ function LoopedSoundPlayer.SetSoundParent(self: LoopedSoundPlayer, parent: Instance?): ()
103
127
  self._soundParent.Value = parent
104
128
  end
105
129
 
106
- function LoopedSoundPlayer.Swap(self: LoopedSoundPlayer, soundId, loopSchedule)
130
+ function LoopedSoundPlayer.Swap(
131
+ self: LoopedSoundPlayer,
132
+ soundId: SoundUtils.SoundId,
133
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule?
134
+ ): ()
107
135
  assert(SoundUtils.isConvertableToRbxAsset(soundId) or soundId == nil, "Bad soundId")
108
136
  loopSchedule = self:_convertToLoopedSchedule(loopSchedule)
137
+ assert(loopSchedule ~= nil, "Bad loopSchedule")
109
138
 
110
139
  local maid = Maid.new()
111
140
 
@@ -117,11 +146,14 @@ function LoopedSoundPlayer.Swap(self: LoopedSoundPlayer, soundId, loopSchedule)
117
146
  self._maid._swappingTo = maid
118
147
  end
119
148
 
120
- function LoopedSoundPlayer.SetDoSyncSoundPlayback(self: LoopedSoundPlayer, doSyncSoundPlayback: boolean)
149
+ --[=[
150
+ Sets whether to sync sound playback to BPM.
151
+ ]=]
152
+ function LoopedSoundPlayer.SetDoSyncSoundPlayback(self: LoopedSoundPlayer, doSyncSoundPlayback: boolean): ()
121
153
  self._doSyncSoundPlayback.Value = doSyncSoundPlayback
122
154
  end
123
155
 
124
- function LoopedSoundPlayer._setupRender(self: LoopedSoundPlayer)
156
+ function LoopedSoundPlayer._setupRender(self: LoopedSoundPlayer): ()
125
157
  self._maid:GiveTask(self._currentSoundId
126
158
  :ObserveBrio(function(value)
127
159
  return value ~= nil
@@ -132,17 +164,18 @@ function LoopedSoundPlayer._setupRender(self: LoopedSoundPlayer)
132
164
  end
133
165
 
134
166
  local maid = brio:ToMaid()
135
- local soundId = brio:GetValue()
167
+ local soundId: SoundUtils.SoundId = brio:GetValue() :: any
136
168
 
137
169
  maid:GiveTask(self:_renderSoundPlayer(soundId))
138
170
  end))
139
171
  end
140
172
 
141
- function LoopedSoundPlayer._renderSoundPlayer(self: LoopedSoundPlayer, soundId)
173
+ function LoopedSoundPlayer._renderSoundPlayer(self: LoopedSoundPlayer, soundId: SoundUtils.SoundId): Maid.Maid
142
174
  local maid = Maid.new()
143
175
 
144
176
  local renderMaid = Maid.new()
145
- local soundPlayer = renderMaid:Add(SimpleLoopedSoundPlayer.new(soundId))
177
+ local soundPlayer: SimpleLoopedSoundPlayer.SimpleLoopedSoundPlayer =
178
+ renderMaid:Add(SimpleLoopedSoundPlayer.new(soundId))
146
179
  soundPlayer:SetTransitionTime(self._crossFadeTime)
147
180
 
148
181
  renderMaid:GiveTask(self._soundGroup:Observe():Subscribe(function(soundGroup)
@@ -235,7 +268,11 @@ function LoopedSoundPlayer._renderSoundPlayer(self: LoopedSoundPlayer, soundId)
235
268
  return maid
236
269
  end
237
270
 
238
- function LoopedSoundPlayer._setupLoopScheduling(self: LoopedSoundPlayer, soundPlayer, loopSchedule)
271
+ function LoopedSoundPlayer._setupLoopScheduling(
272
+ self: LoopedSoundPlayer,
273
+ soundPlayer: SimpleLoopedSoundPlayer.SimpleLoopedSoundPlayer,
274
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule
275
+ ): Maid.Maid
239
276
  local maid = Maid.new()
240
277
 
241
278
  if loopSchedule.maxLoops then
@@ -270,9 +307,14 @@ function LoopedSoundPlayer._setupLoopScheduling(self: LoopedSoundPlayer, soundPl
270
307
  return maid
271
308
  end
272
309
 
273
- function LoopedSoundPlayer.SwapToSamples(self: LoopedSoundPlayer, soundIdList, loopSchedule)
310
+ function LoopedSoundPlayer.SwapToSamples(
311
+ self: LoopedSoundPlayer,
312
+ soundIdList: { SoundUtils.SoundId },
313
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule?
314
+ ): ()
274
315
  assert(type(soundIdList) == "table", "Bad soundIdList")
275
316
  loopSchedule = self:_convertToLoopedSchedule(loopSchedule)
317
+ assert(loopSchedule ~= nil, "Bad loopSchedule")
276
318
 
277
319
  local loopMaid = Maid.new()
278
320
 
@@ -289,9 +331,17 @@ function LoopedSoundPlayer.SwapToSamples(self: LoopedSoundPlayer, soundIdList, l
289
331
  self._maid._swappingTo = loopMaid
290
332
  end
291
333
 
292
- function LoopedSoundPlayer.SwapToChoice(self: LoopedSoundPlayer, soundIdList, loopSchedule)
334
+ --[=[
335
+ Swaps to a random choice from the provided list of sound IDs on loop.
336
+ ]=]
337
+ function LoopedSoundPlayer.SwapToChoice(
338
+ self: LoopedSoundPlayer,
339
+ soundIdList: { SoundUtils.SoundId },
340
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule?
341
+ ): ()
293
342
  assert(type(soundIdList) == "table", "Bad soundIdList")
294
343
  loopSchedule = self:_convertToLoopedSchedule(loopSchedule)
344
+ assert(loopSchedule ~= nil, "Bad loopSchedule")
295
345
 
296
346
  local loopMaid = Maid.new()
297
347
 
@@ -307,33 +357,64 @@ function LoopedSoundPlayer.SwapToChoice(self: LoopedSoundPlayer, soundIdList, lo
307
357
  self._maid._swappingTo = loopMaid
308
358
  end
309
359
 
310
- function LoopedSoundPlayer.PlayOnce(self: LoopedSoundPlayer, soundId, loopSchedule)
360
+ --[=[
361
+ Plays the sound once
362
+ ]=]
363
+ function LoopedSoundPlayer.PlayOnce(
364
+ self: LoopedSoundPlayer,
365
+ soundId: SoundUtils.SoundId,
366
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule?
367
+ ): ()
311
368
  assert(SoundUtils.isConvertableToRbxAsset(soundId) or soundId == nil, "Bad soundId")
312
369
  loopSchedule = self:_convertToLoopedSchedule(loopSchedule)
370
+ assert(loopSchedule ~= nil, "Bad loopSchedule")
313
371
 
314
372
  self:Swap(soundId, SoundLoopScheduleUtils.maxLoops(1, loopSchedule))
315
373
  end
316
374
 
317
- function LoopedSoundPlayer.SwapOnLoop(self: LoopedSoundPlayer, soundId, loopSchedule)
375
+ --[=[
376
+ Swaps the sound on the next loop
377
+ ]=]
378
+ function LoopedSoundPlayer.SwapOnLoop(
379
+ self: LoopedSoundPlayer,
380
+ soundId: SoundUtils.SoundId,
381
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule?
382
+ ): ()
318
383
  assert(SoundUtils.isConvertableToRbxAsset(soundId) or soundId == nil, "Bad soundId")
319
384
  loopSchedule = self:_convertToLoopedSchedule(loopSchedule)
385
+ assert(loopSchedule ~= nil, "Bad loopSchedule")
320
386
 
321
387
  self:Swap(soundId, SoundLoopScheduleUtils.onNextLoop(loopSchedule))
322
388
  end
323
389
 
324
- function LoopedSoundPlayer.PlayOnceOnLoop(self: LoopedSoundPlayer, soundId, loopSchedule)
390
+ --[=[
391
+ Plays the sound once on the next loop
392
+ ]=]
393
+ function LoopedSoundPlayer.PlayOnceOnLoop(
394
+ self: LoopedSoundPlayer,
395
+ soundId: SoundUtils.SoundId,
396
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule?
397
+ ): ()
325
398
  assert(SoundUtils.isConvertableToRbxAsset(soundId) or soundId == nil, "Bad soundId")
326
399
  loopSchedule = self:_convertToLoopedSchedule(loopSchedule)
400
+ assert(loopSchedule ~= nil, "Bad loopSchedule")
327
401
 
328
402
  self:PlayOnce(soundId, SoundLoopScheduleUtils.onNextLoop(loopSchedule))
329
403
  end
330
404
 
331
- function LoopedSoundPlayer._convertToLoopedSchedule(self: LoopedSoundPlayer, loopSchedule)
405
+ function LoopedSoundPlayer._convertToLoopedSchedule(
406
+ self: LoopedSoundPlayer,
407
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule?
408
+ ): SoundLoopScheduleUtils.SoundLoopSchedule
332
409
  assert(SoundLoopScheduleUtils.isLoopedSchedule(loopSchedule) or loopSchedule == nil, "Bad loopSchedule")
333
410
  return loopSchedule or self._defaultScheduleOptions
334
411
  end
335
412
 
336
- function LoopedSoundPlayer._scheduleFirstPlay(self: LoopedSoundPlayer, loopSchedule, callback)
413
+ function LoopedSoundPlayer._scheduleFirstPlay(
414
+ self: LoopedSoundPlayer,
415
+ loopSchedule: SoundLoopScheduleUtils.SoundLoopSchedule,
416
+ callback
417
+ ): Maid.Maid
337
418
  assert(SoundLoopScheduleUtils.isLoopedSchedule(loopSchedule), "Bad loopSchedule")
338
419
  assert(type(callback) == "function", "Bad callback")
339
420
 
@@ -374,7 +455,10 @@ function LoopedSoundPlayer._scheduleFirstPlay(self: LoopedSoundPlayer, loopSched
374
455
  return maid
375
456
  end
376
457
 
377
- function LoopedSoundPlayer.StopAfterLoop(self: LoopedSoundPlayer)
458
+ --[=[
459
+ Stops playback after the current loop finishes.
460
+ ]=]
461
+ function LoopedSoundPlayer.StopAfterLoop(self: LoopedSoundPlayer): ()
378
462
  local swapMaid = Maid.new()
379
463
 
380
464
  swapMaid:GiveTask(self._currentSoundLooped:Connect(function()
@@ -386,7 +470,10 @@ function LoopedSoundPlayer.StopAfterLoop(self: LoopedSoundPlayer)
386
470
  self._maid._swappingTo = swapMaid
387
471
  end
388
472
 
389
- function LoopedSoundPlayer._observeActiveSoundFinishLoop(self: LoopedSoundPlayer, maxWaitTime)
473
+ function LoopedSoundPlayer._observeActiveSoundFinishLoop(
474
+ self: LoopedSoundPlayer,
475
+ maxWaitTime: number
476
+ ): Observable.Observable<()>
390
477
  local startTime = os.clock()
391
478
 
392
479
  return self._currentActiveSound:Observe():Pipe({
@@ -430,6 +517,9 @@ function LoopedSoundPlayer._observeActiveSoundFinishLoop(self: LoopedSoundPlayer
430
517
  }) :: any
431
518
  end
432
519
 
520
+ --[=[
521
+ Promises that resolve when the current loop is done.
522
+ ]=]
433
523
  function LoopedSoundPlayer.PromiseLoopDone(self: LoopedSoundPlayer): Promise.Promise<()>
434
524
  local promise = self._maid:GivePromise(Promise.new())
435
525
 
@@ -442,11 +532,17 @@ function LoopedSoundPlayer.PromiseLoopDone(self: LoopedSoundPlayer): Promise.Pro
442
532
  return promise
443
533
  end
444
534
 
535
+ --[=[
536
+ Promises that never resolve, keeping the sound player alive.
537
+ ]=]
445
538
  function LoopedSoundPlayer.PromiseSustain(_self: LoopedSoundPlayer): Promise.Promise<()>
446
539
  -- Never resolve (?)
447
540
  return Promise.new()
448
541
  end
449
542
 
543
+ --[=[
544
+ Gets the currently active sound instance.
545
+ ]=]
450
546
  function LoopedSoundPlayer.GetSound(self: LoopedSoundPlayer): Sound?
451
547
  return self._currentActiveSound.Value
452
548
  end
@@ -1,4 +1,4 @@
1
- --!nonstrict
1
+ --!strict
2
2
  --[=[
3
3
  @class SimpleLoopedSoundPlayer
4
4
  ]=]
@@ -16,8 +16,19 @@ local SimpleLoopedSoundPlayer = setmetatable({}, TimedTransitionModel)
16
16
  SimpleLoopedSoundPlayer.ClassName = "SimpleLoopedSoundPlayer"
17
17
  SimpleLoopedSoundPlayer.__index = SimpleLoopedSoundPlayer
18
18
 
19
- function SimpleLoopedSoundPlayer.new(soundId)
20
- local self = setmetatable(TimedTransitionModel.new(), SimpleLoopedSoundPlayer)
19
+ export type SimpleLoopedSoundPlayer =
20
+ typeof(setmetatable(
21
+ {} :: {
22
+ Sound: Sound,
23
+ _volumeMultiplier: ValueObject.ValueObject<number>,
24
+ _maxVolume: number,
25
+ },
26
+ {} :: typeof({ __index = SimpleLoopedSoundPlayer })
27
+ ))
28
+ & TimedTransitionModel.TimedTransitionModel
29
+
30
+ function SimpleLoopedSoundPlayer.new(soundId: SoundUtils.SoundId): SimpleLoopedSoundPlayer
31
+ local self: SimpleLoopedSoundPlayer = setmetatable(TimedTransitionModel.new() :: any, SimpleLoopedSoundPlayer)
21
32
 
22
33
  self.Sound = self._maid:Add(SoundUtils.createSoundFromId(soundId))
23
34
  self.Sound.Looped = true
@@ -32,7 +43,7 @@ function SimpleLoopedSoundPlayer.new(soundId)
32
43
  self._maid:GiveTask(Rx.combineLatest({
33
44
  visible = self:ObserveRenderStepped(),
34
45
  multiplier = self._volumeMultiplier:Observe(),
35
- }):Subscribe(function(state)
46
+ }):Subscribe(function(state: any)
36
47
  self.Sound.Volume = state.visible * self._maxVolume * state.multiplier
37
48
  end))
38
49
 
@@ -45,22 +56,35 @@ function SimpleLoopedSoundPlayer.new(soundId)
45
56
  return self
46
57
  end
47
58
 
48
- function SimpleLoopedSoundPlayer:SetSoundGroup(soundGroup)
59
+ --[=[
60
+ Sets the SoundGroup of the internal Sound.
61
+ @param soundGroup SoundGroup?
62
+ ]=]
63
+ function SimpleLoopedSoundPlayer.SetSoundGroup(self: SimpleLoopedSoundPlayer, soundGroup: SoundGroup?): ()
49
64
  assert(typeof(soundGroup) == "Instance" or soundGroup == nil, "Bad soundGroup")
50
65
 
51
66
  self.Sound.SoundGroup = soundGroup
52
67
  end
53
68
 
54
- function SimpleLoopedSoundPlayer:SetVolumeMultiplier(volume)
69
+ --[=[
70
+ Sets the volume multiplier for the sound player.
71
+ ]=]
72
+ function SimpleLoopedSoundPlayer.SetVolumeMultiplier(self: SimpleLoopedSoundPlayer, volume: number): ()
55
73
  self._volumeMultiplier.Value = volume
56
74
  end
57
75
 
58
- function SimpleLoopedSoundPlayer:PromiseSustain()
76
+ --[=[
77
+ Promises indefinitely until the sound player is destroyed.
78
+ ]=]
79
+ function SimpleLoopedSoundPlayer.PromiseSustain(self: SimpleLoopedSoundPlayer): Promise.Promise<()>
59
80
  -- Never resolve
60
- return Promise.new()
81
+ return self._maid:GivePromise(Promise.new())
61
82
  end
62
83
 
63
- function SimpleLoopedSoundPlayer:PromiseLoopDone()
84
+ --[=[
85
+ Promises until the current loop is done.
86
+ ]=]
87
+ function SimpleLoopedSoundPlayer.PromiseLoopDone(self: SimpleLoopedSoundPlayer): Promise.Promise<()>
64
88
  return SoundPromiseUtils.promiseLooped(self.Sound)
65
89
  end
66
90