@quenty/soundgroup 1.26.0 → 1.26.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,17 @@
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
+ ## [1.26.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/soundgroup@1.26.0...@quenty/soundgroup@1.26.1) (2026-01-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Update the sound group package to include sound volume multipliers ([bf9591f](https://github.com/Quenty/NevermoreEngine/commit/bf9591fe2d08dbefae47f8c449b7bbf20c01fdfe))
12
+
13
+
14
+
15
+
16
+
6
17
  # [1.26.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/soundgroup@1.25.8...@quenty/soundgroup@1.26.0) (2026-01-04)
7
18
 
8
19
  **Note:** Version bump only for package @quenty/soundgroup
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/soundgroup",
3
- "version": "1.26.0",
3
+ "version": "1.26.1",
4
4
  "description": "Service and utility methods to working with sound groups and sounds in Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -26,20 +26,24 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "@quenty/baseobject": "^10.9.1",
29
+ "@quenty/binder": "^14.26.1",
29
30
  "@quenty/brio": "^14.21.0",
30
- "@quenty/counter": "^7.22.0",
31
+ "@quenty/counter": "^7.22.1",
31
32
  "@quenty/instanceutils": "^13.21.0",
32
33
  "@quenty/loader": "^10.9.1",
33
34
  "@quenty/maid": "^3.5.1",
34
- "@quenty/observablecollection": "^12.25.0",
35
+ "@quenty/observablecollection": "^12.25.1",
36
+ "@quenty/promise": "^10.12.4",
37
+ "@quenty/rogue-properties": "^11.31.1",
35
38
  "@quenty/rx": "^13.21.0",
36
39
  "@quenty/servicebag": "^11.13.4",
37
40
  "@quenty/signal": "^7.11.3",
38
41
  "@quenty/table": "^3.9.0",
39
- "@quenty/valueobject": "^13.22.0"
42
+ "@quenty/tie": "^10.27.1",
43
+ "@quenty/valueobject": "^13.22.1"
40
44
  },
41
45
  "publishConfig": {
42
46
  "access": "public"
43
47
  },
44
- "gitHead": "22144a14aa443a177ff307edd3813a838f8c1d5f"
48
+ "gitHead": "5232cd2c58ca0dcdf591dd8ae78995211da2f3e2"
45
49
  }
@@ -16,6 +16,10 @@ function SoundGroupServiceClient:Init(serviceBag: ServiceBag.ServiceBag)
16
16
  self._serviceBag = assert(serviceBag, "No serviceBag")
17
17
  self._maid = Maid.new()
18
18
 
19
+ -- External
20
+ self._serviceBag:GetService(require("TieRealmService"))
21
+ self._serviceBag:GetService(require("RoguePropertyService"))
22
+
19
23
  -- Internal
20
24
  self._serviceBag:GetService(require("SoundEffectService"))
21
25
  end
@@ -17,6 +17,10 @@ function SoundGroupService:Init(serviceBag: ServiceBag.ServiceBag)
17
17
  self._serviceBag = assert(serviceBag, "No serviceBag")
18
18
  self._maid = Maid.new()
19
19
 
20
+ -- External
21
+ self._serviceBag:GetService(require("TieRealmService"))
22
+ self._serviceBag:GetService(require("RoguePropertyService"))
23
+
20
24
  -- Internal
21
25
  self._soundEffectService = self._serviceBag:GetService(require("SoundEffectService"))
22
26
  end
@@ -1,4 +1,4 @@
1
- --!nonstrict
1
+ --!strict
2
2
  --[=[
3
3
  Handles applying global volume and effects to specific sounds in a group based upon a path.
4
4
 
@@ -11,25 +11,46 @@ local RunService = game:GetService("RunService")
11
11
  local SoundService = game:GetService("SoundService")
12
12
 
13
13
  local Maid = require("Maid")
14
+ local Promise = require("Promise")
14
15
  local ServiceBag = require("ServiceBag")
16
+ local SoundEffectsList = require("SoundEffectsList")
15
17
  local SoundEffectsRegistry = require("SoundEffectsRegistry")
16
18
  local SoundGroupPathUtils = require("SoundGroupPathUtils")
17
19
  local SoundGroupTracker = require("SoundGroupTracker")
20
+ local SoundGroupVolume = require("SoundGroupVolume")
18
21
  local WellKnownSoundGroups = require("WellKnownSoundGroups")
19
22
 
20
23
  local SoundEffectService = {}
21
24
  SoundEffectService.ServiceName = "SoundEffectService"
22
25
 
23
- function SoundEffectService:Init(serviceBag: ServiceBag.ServiceBag)
24
- assert(not self._serviceBag, "Already initialized")
26
+ export type SoundEffectService = typeof(setmetatable(
27
+ {} :: {
28
+ _serviceBag: ServiceBag.ServiceBag,
29
+ _maid: Maid.Maid,
30
+ _soundEffectsRegister: any, -- SoundEffectsRegistry.SoundEffectsRegistry,
31
+ _tracker: any, -- SoundGroupTracker.SoundGroupTracker,
32
+ },
33
+ {} :: typeof({ __index = SoundEffectService })
34
+ ))
35
+
36
+ function SoundEffectService.Init(self: SoundEffectService, serviceBag: ServiceBag.ServiceBag)
37
+ assert(not (self :: any)._serviceBag, "Already initialized")
25
38
  self._serviceBag = assert(serviceBag, "No serviceBag")
26
39
  self._maid = Maid.new()
27
40
 
41
+ -- External
42
+ self._serviceBag:GetService(require("TieRealmService"))
43
+ self._serviceBag:GetService(require("RoguePropertyService"))
44
+
45
+ -- State
28
46
  self._soundEffectsRegister = self._maid:Add(SoundEffectsRegistry.new())
29
47
  self._tracker = self._maid:Add(SoundGroupTracker.new(SoundService))
48
+
49
+ -- Binders
50
+ self._serviceBag:GetService(require("SoundGroupVolume"))
30
51
  end
31
52
 
32
- function SoundEffectService:Start()
53
+ function SoundEffectService.Start(self: SoundEffectService): ()
33
54
  self:_setupEffectApplication()
34
55
  end
35
56
 
@@ -38,14 +59,42 @@ end
38
59
  @param sound Sound
39
60
  @param soundGroupPath string? -- Optional
40
61
  ]=]
41
- function SoundEffectService:RegisterSFX(sound: Sound, soundGroupPath: string?)
62
+ function SoundEffectService.RegisterSFX(self: SoundEffectService, sound: Sound, soundGroupPath: string): ()
42
63
  assert(typeof(sound) == "Instance" and sound:IsA("Sound"), "Bad sound")
43
64
  assert(SoundGroupPathUtils.isSoundGroupPath(soundGroupPath) or soundGroupPath == nil, "Bad soundGroupPath")
44
65
 
45
66
  sound.SoundGroup = self:GetOrCreateSoundGroup(soundGroupPath or WellKnownSoundGroups.SFX)
46
67
  end
47
68
 
48
- function SoundEffectService:GetOrCreateSoundGroup(soundGroupPath: string): SoundGroup
69
+ --[=[
70
+ Creates a NumberValue multiplier for the given sound group path.
71
+
72
+ Destroy (or unparent) the returned NumberValue to remove the multiplier.
73
+
74
+ @param soundGroupPath string
75
+ @return Promise<NumberValue>
76
+ ]=]
77
+ function SoundEffectService:PromiseCreateVolumeMultiplier(soundGroupPath: string): Promise.Promise<NumberValue>
78
+ local soundGroup = self:GetOrCreateSoundGroup(soundGroupPath)
79
+ if soundGroup == nil then
80
+ return Promise.rejected("Failed to get or create sound group for path: " .. soundGroupPath)
81
+ end
82
+
83
+ local soundGroupVolumeBinder = self._serviceBag:GetService(require("SoundGroupVolume"))
84
+ soundGroupVolumeBinder:Tag(soundGroup)
85
+
86
+ return soundGroupVolumeBinder:Promise(soundGroup):Then(function(soundGroupVolume)
87
+ return soundGroupVolume:CreateMultiplier()
88
+ end)
89
+ end
90
+
91
+ --[=[
92
+ Returns the SoundGroup for the given path, creating it if it does not exist.
93
+
94
+ @param soundGroupPath string
95
+ @return SoundGroup
96
+ ]=]
97
+ function SoundEffectService.GetOrCreateSoundGroup(self: SoundEffectService, soundGroupPath: string): SoundGroup
49
98
  assert(SoundGroupPathUtils.isSoundGroupPath(soundGroupPath), "Bad soundGroupPath")
50
99
 
51
100
  local found = self:GetSoundGroup(soundGroupPath)
@@ -54,10 +103,21 @@ function SoundEffectService:GetOrCreateSoundGroup(soundGroupPath: string): Sound
54
103
  end
55
104
 
56
105
  -- Handle deferred mode
57
- return SoundGroupPathUtils.findOrCreateSoundGroup(soundGroupPath)
106
+ found = SoundGroupPathUtils.findOrCreateSoundGroup(soundGroupPath)
107
+ assert(found, "Failed to create sound group for path")
108
+
109
+ SoundGroupVolume:Tag(found)
110
+
111
+ return found
58
112
  end
59
113
 
60
- function SoundEffectService:GetSoundGroup(soundGroupPath: string): SoundGroup
114
+ --[=[
115
+ Returns the SoundGroup for the given path, or nil if it does not exist.
116
+
117
+ @param soundGroupPath string
118
+ @return SoundGroup
119
+ ]=]
120
+ function SoundEffectService.GetSoundGroup(self: SoundEffectService, soundGroupPath: string): SoundGroup?
61
121
  assert(SoundGroupPathUtils.isSoundGroupPath(soundGroupPath), "Bad soundGroupPath")
62
122
 
63
123
  if not self._tracker then
@@ -71,27 +131,41 @@ function SoundEffectService:GetSoundGroup(soundGroupPath: string): SoundGroup
71
131
 
72
132
  local found = self._tracker:GetFirstSoundGroup(soundGroupPath)
73
133
  if found then
134
+ SoundGroupVolume:Tag(found)
74
135
  return found
75
136
  end
76
137
 
77
- return SoundGroupPathUtils.findOrCreateSoundGroup(soundGroupPath)
138
+ found = SoundGroupPathUtils.findSoundGroup(soundGroupPath)
139
+ if found then
140
+ SoundGroupVolume:Tag(found)
141
+ end
142
+
143
+ return found
78
144
  end
79
145
 
80
- function SoundEffectService:PushEffect(soundGroupPath: string, effect)
146
+ function SoundEffectService.PushEffect(
147
+ self: SoundEffectService,
148
+ soundGroupPath: string,
149
+ effect: SoundEffectsList.SoundEffectApplier
150
+ ): ()
81
151
  assert(SoundGroupPathUtils.isSoundGroupPath(soundGroupPath), "Bad soundGroupPath")
82
152
  assert(type(effect) == "function", "Bad effect")
83
153
 
84
154
  return self._soundEffectsRegister:PushEffect(soundGroupPath, effect)
85
155
  end
86
156
 
87
- function SoundEffectService:ApplyEffects(soundGroupPath, instance)
157
+ function SoundEffectService.ApplyEffects(
158
+ self: SoundEffectService,
159
+ soundGroupPath: string,
160
+ instance: SoundGroup | Sound
161
+ ): () -> ()
88
162
  assert(SoundGroupPathUtils.isSoundGroupPath(soundGroupPath), "Bad soundGroupPath")
89
163
  assert(typeof(instance) == "Instance" and (instance:IsA("SoundGroup") or instance:IsA("Sound")), "Bad instance")
90
164
 
91
165
  return self._soundEffectsRegister:ApplyEffects(soundGroupPath, instance)
92
166
  end
93
167
 
94
- function SoundEffectService:_setupEffectApplication()
168
+ function SoundEffectService._setupEffectApplication(self: SoundEffectService): ()
95
169
  self._maid:GiveTask(self._tracker:ObserveSoundGroupsBrio():Subscribe(function(brio)
96
170
  if brio:IsDead() then
97
171
  return
@@ -102,7 +176,7 @@ function SoundEffectService:_setupEffectApplication()
102
176
  if soundGroupPath then
103
177
  maid._currentEffects = self._soundEffectsRegister:ApplyEffects(soundGroupPath, soundGroup)
104
178
  else
105
- maid._currentEffects = nil
179
+ maid._currentEffects = nil :: any
106
180
  end
107
181
  end))
108
182
  end))
@@ -122,7 +196,7 @@ function SoundEffectService:_setupEffectApplication()
122
196
  end))
123
197
  end
124
198
 
125
- function SoundEffectService:Destroy()
199
+ function SoundEffectService.Destroy(self: SoundEffectService): ()
126
200
  self._maid:DoCleaning()
127
201
  end
128
202
 
@@ -0,0 +1,65 @@
1
+ --!strict
2
+ local SoundGroupVolumeInterface = require(script.Parent.SoundGroupVolumeInterface)
3
+ --[=[
4
+ @class SoundGroupVolume
5
+ ]=]
6
+
7
+ local require = require(script.Parent.loader).load(script)
8
+
9
+ local BaseObject = require("BaseObject")
10
+ local Binder = require("Binder")
11
+ local ServiceBag = require("ServiceBag")
12
+ local SoundGroupVolumeProperties = require("SoundGroupVolumeProperties")
13
+ local TieRealms = require("TieRealms")
14
+
15
+ local SoundGroupVolume = setmetatable({}, BaseObject)
16
+ SoundGroupVolume.ClassName = "SoundGroupVolume"
17
+ SoundGroupVolume.__index = SoundGroupVolume
18
+
19
+ export type SoundGroupVolume =
20
+ typeof(setmetatable(
21
+ {} :: {
22
+ _obj: SoundGroup,
23
+ _serviceBag: ServiceBag.ServiceBag,
24
+ _tieRealmService: any,
25
+ _properties: any,
26
+ RenderedVolume: any,
27
+ },
28
+ {} :: typeof({ __index = SoundGroupVolume })
29
+ ))
30
+ & BaseObject.BaseObject
31
+
32
+ function SoundGroupVolume.new(instance: SoundGroup, serviceBag: ServiceBag.ServiceBag): SoundGroupVolume
33
+ local self: SoundGroupVolume = setmetatable(BaseObject.new(instance) :: any, SoundGroupVolume)
34
+
35
+ self._serviceBag = assert(serviceBag, "No serviceBag")
36
+ self._tieRealmService = self._serviceBag:GetService(require("TieRealmService"))
37
+
38
+ self._properties = SoundGroupVolumeProperties:Get(self._serviceBag, self._obj)
39
+ self.RenderedVolume = self._properties.Volume
40
+
41
+ self._maid:GiveTask(SoundGroupVolumeInterface:Implement(self._obj, self, self._tieRealmService:GetTieRealm()))
42
+
43
+ -- Assign our volume to the current volume if we're on the server
44
+ if self._tieRealmService:GetTieRealm() == TieRealms.SERVER then
45
+ self._properties.Volume:SetBaseValue(self._obj.Volume)
46
+ end
47
+
48
+ self._maid:GiveTask(self._properties.Volume:Observe():Subscribe(function(volume)
49
+ self._obj.Volume = volume
50
+ end))
51
+
52
+ return self
53
+ end
54
+
55
+ --[=[
56
+ Creates a volume multiplier for this sound group volume.
57
+
58
+ @param amount number?
59
+ @return NumberValue
60
+ ]=]
61
+ function SoundGroupVolume.CreateMultiplier(self: SoundGroupVolume, amount: number?): NumberValue
62
+ return self._properties.Volume:CreateMultiplier(amount or 1)
63
+ end
64
+
65
+ return Binder.new("SoundGroupVolume", SoundGroupVolume :: any) :: Binder.Binder<SoundGroupVolume>
@@ -0,0 +1,12 @@
1
+ --!strict
2
+ --[=[
3
+ @class SoundGroupVolumeInterface
4
+ ]=]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local TieDefinition = require("TieDefinition")
9
+
10
+ return TieDefinition.new("SoundGroupVolume", {
11
+ CreateMultiplier = TieDefinition.Types.METHOD,
12
+ })
@@ -0,0 +1,12 @@
1
+ --!strict
2
+ --[=[
3
+ @class SoundGroupVolumeProperties
4
+ ]=]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local RoguePropertyTableDefinition = require("RoguePropertyTableDefinition")
9
+
10
+ return RoguePropertyTableDefinition.new("SoundGroupVolume", {
11
+ Volume = 1,
12
+ })