aqualink 2.18.1 → 2.19.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.
@@ -1,244 +1,351 @@
1
- 'use strict'
2
-
3
-
4
- const FILTER_DEFAULTS = Object.freeze({
5
- karaoke: Object.freeze({ level: 1, monoLevel: 1, filterBand: 220, filterWidth: 100 }),
6
- timescale: Object.freeze({ speed: 1, pitch: 1, rate: 1 }),
7
- tremolo: Object.freeze({ frequency: 2, depth: 0.5 }),
8
- vibrato: Object.freeze({ frequency: 2, depth: 0.5 }),
9
- rotation: Object.freeze({ rotationHz: 0 }),
10
- distortion: Object.freeze({ sinOffset: 0, sinScale: 1, cosOffset: 0, cosScale: 1, tanOffset: 0, tanScale: 1, offset: 0, scale: 1 }),
11
- channelMix: Object.freeze({ leftToLeft: 1, leftToRight: 0, rightToLeft: 0, rightToRight: 1 }),
12
- lowPass: Object.freeze({ smoothing: 20 })
13
- })
14
-
15
- const FILTER_KEYS = Object.freeze(
16
- Object.fromEntries(
17
- Object.entries(FILTER_DEFAULTS).map(([k, v]) => [k, Object.freeze(Object.keys(v))])
18
- )
19
- )
20
-
21
- const EMPTY_ARRAY = Object.freeze([])
22
-
23
- const _utils = Object.freeze({
24
- shallowEqual(current, defaults, override, keys) {
25
- if (!current) return false
26
- for (let i = 0; i < keys.length; i++) {
27
- const k = keys[i]
28
- const expected = k in override ? override[k] : defaults[k]
29
- if (current[k] !== expected) return false
30
- }
31
- return true
32
- },
33
-
34
- equalizerEqual(a, b) {
35
- if (a === b) return true
36
- const lenA = a?.length || 0
37
- const lenB = b?.length || 0
38
- if (lenA !== lenB) return false
39
- for (let i = 0; i < lenA; i++) {
40
- const x = a[i], y = b[i]
41
- if (x.band !== y.band || x.gain !== y.gain) return false
42
- }
43
- return true
44
- },
45
-
46
- eqIsEmpty(arr) {
47
- return !arr || arr.length === 0
48
- },
49
-
50
- makeEqArray(len, gain) {
51
- const out = new Array(len)
52
- for (let i = 0; i < len; i++) out[i] = { band: i, gain }
53
- return out
54
- }
55
- })
56
-
57
-
58
- class Filters {
59
- constructor(player, options = {}) {
60
- if (!player) throw new Error('Player instance is required')
61
- this.player = player
62
- this._pendingUpdate = false
63
- this._dirty = new Set()
64
-
65
-
66
- this.filters = {
67
- volume: options.volume ?? 1,
68
- equalizer: options.equalizer ?? EMPTY_ARRAY,
69
- karaoke: options.karaoke ?? null,
70
- timescale: options.timescale ?? null,
71
- tremolo: options.tremolo ?? null,
72
- vibrato: options.vibrato ?? null,
73
- rotation: options.rotation ?? null,
74
- distortion: options.distortion ?? null,
75
- channelMix: options.channelMix ?? null,
76
- lowPass: options.lowPass ?? null
77
- }
78
-
79
- this.presets = {
80
- bassboost: options.bassboost ?? null,
81
- slowmode: options.slowmode ?? null,
82
- nightcore: options.nightcore ?? null,
83
- vaporwave: options.vaporwave ?? null,
84
- _8d: options._8d ?? null
85
- }
86
- }
87
-
88
- destroy() {
89
- this._pendingUpdate = false
90
- this.player = null
91
- }
92
-
93
- _setFilter(filterName, enabled, options = {}) {
94
- const current = this.filters[filterName]
95
- if (!enabled) {
96
- if (current === null) return this
97
- this.filters[filterName] = null
98
- return this._scheduleUpdate()
99
- }
100
-
101
- const defaults = FILTER_DEFAULTS[filterName]
102
- const keys = FILTER_KEYS[filterName]
103
- if (current && _utils.shallowEqual(current, defaults, options, keys)) return this
104
-
105
- this.filters[filterName] = Object.assign({}, defaults, options)
106
- this._dirty.add(filterName)
107
- return this._scheduleUpdate()
108
- }
109
-
110
- _scheduleUpdate() {
111
- if (this._pendingUpdate || !this.player) return this
112
- this._pendingUpdate = true
113
- queueMicrotask(() => {
114
- this._pendingUpdate = false
115
- if (this.player) {
116
- this.updateFilters().catch(() => {
117
- })
118
- }
119
- })
120
- return this
121
- }
122
-
123
- setEqualizer(bands) {
124
- const next = bands ?? EMPTY_ARRAY
125
- if (_utils.equalizerEqual(this.filters.equalizer, next)) return this
126
- this.filters.equalizer = next
127
- this._dirty.add('equalizer')
128
- return this._scheduleUpdate()
129
- }
130
-
131
- setKaraoke(enabled, options = {}) { return this._setFilter('karaoke', enabled, options) }
132
- setTimescale(enabled, options = {}) { return this._setFilter('timescale', enabled, options) }
133
- setTremolo(enabled, options = {}) { return this._setFilter('tremolo', enabled, options) }
134
- setVibrato(enabled, options = {}) { return this._setFilter('vibrato', enabled, options) }
135
- setRotation(enabled, options = {}) { return this._setFilter('rotation', enabled, options) }
136
- setDistortion(enabled, options = {}) { return this._setFilter('distortion', enabled, options) }
137
- setChannelMix(enabled, options = {}) { return this._setFilter('channelMix', enabled, options) }
138
- setLowPass(enabled, options = {}) { return this._setFilter('lowPass', enabled, options) }
139
-
140
- setBassboost(enabled, options = {}) {
141
- if (!enabled) {
142
- if (this.presets.bassboost === null && _utils.eqIsEmpty(this.filters.equalizer)) return this
143
- this.presets.bassboost = null
144
- return this.setEqualizer(EMPTY_ARRAY)
145
- }
146
-
147
- const value = options.value ?? 5
148
- if (value < 0 || value > 5) throw new Error('Bassboost value must be between 0 and 5')
149
- if (this.presets.bassboost === value) return this
150
-
151
- this.presets.bassboost = value
152
- const gain = (value - 1) * (1.25 / 9) - 0.25
153
-
154
- const current = Array.isArray(this.filters.equalizer) ? [...this.filters.equalizer] : []
155
- const bands = _utils.makeEqArray(13, gain)
156
-
157
- for (const b of bands) {
158
- const idx = current.findIndex(e => e.band === b.band)
159
- if (idx !== -1) current[idx] = b
160
- else current.push(b)
161
- }
162
-
163
- return this.setEqualizer(current)
164
- }
165
-
166
- setSlowmode(enabled, options = {}) {
167
- const rate = enabled ? options.rate ?? 0.8 : 1
168
- if (this.presets.slowmode === enabled && this.filters.timescale?.rate === rate) return this
169
- this.presets.slowmode = enabled
170
- return this.setTimescale(enabled, { rate })
171
- }
172
-
173
- setNightcore(enabled, options = {}) {
174
- const rate = enabled ? options.rate ?? 1.5 : 1
175
- if (this.presets.nightcore === enabled && this.filters.timescale?.rate === rate) return this
176
- this.presets.nightcore = enabled
177
- return this.setTimescale(enabled, { rate })
178
- }
179
-
180
- setVaporwave(enabled, options = {}) {
181
- const pitch = enabled ? options.pitch ?? 0.5 : 1
182
- if (this.presets.vaporwave === enabled && this.filters.timescale?.pitch === pitch) return this
183
- this.presets.vaporwave = enabled
184
- return this.setTimescale(enabled, { pitch })
185
- }
186
-
187
- set8D(enabled, options = {}) {
188
- const rotationHz = enabled ? options.rotationHz ?? 0.2 : 0
189
- if (this.presets._8d === enabled && this.filters.rotation?.rotationHz === rotationHz) return this
190
- this.presets._8d = enabled
191
- return this.setRotation(enabled, { rotationHz })
192
- }
193
-
194
- async clearFilters() {
195
- const f = this.filters
196
- let changed = false
197
-
198
- if (f.volume !== 1) {
199
- f.volume = 1
200
- this._dirty.add('volume')
201
- changed = true
202
- }
203
- if (!_utils.eqIsEmpty(f.equalizer)) {
204
- f.equalizer = EMPTY_ARRAY
205
- this._dirty.add('equalizer')
206
- changed = true
207
- }
208
-
209
- const filterNames = Object.keys(FILTER_DEFAULTS)
210
- for (let i = 0; i < filterNames.length; i++) {
211
- const key = filterNames[i]
212
- if (f[key] !== null) {
213
- f[key] = null
214
- this._dirty.add(key)
215
- changed = true
216
- }
217
- }
218
-
219
- for (const key in this.presets) {
220
- if (this.presets[key] !== null) this.presets[key] = null
221
- }
222
-
223
- return changed ? this.updateFilters() : this
224
- }
225
-
226
- async updateFilters() {
227
- if (!this.player || !this._dirty.size) return this
228
-
229
- const payload = {}
230
- for (const key of this._dirty) {
231
- payload[key] = this.filters[key]
232
- }
233
-
234
- this._dirty.clear()
235
-
236
- await this.player.nodes.rest.updatePlayer({
237
- guildId: this.player.guildId,
238
- data: { filters: payload }
239
- })
240
- return this
241
- }
242
- }
243
-
244
- module.exports = Filters
1
+ const FILTER_DEFAULTS = Object.freeze({
2
+ karaoke: Object.freeze({
3
+ level: 1,
4
+ monoLevel: 1,
5
+ filterBand: 220,
6
+ filterWidth: 100
7
+ }),
8
+ timescale: Object.freeze({ speed: 1, pitch: 1, rate: 1 }),
9
+ tremolo: Object.freeze({ frequency: 2, depth: 0.5 }),
10
+ vibrato: Object.freeze({ frequency: 2, depth: 0.5 }),
11
+ rotation: Object.freeze({ rotationHz: 0 }),
12
+ distortion: Object.freeze({
13
+ sinOffset: 0,
14
+ sinScale: 1,
15
+ cosOffset: 0,
16
+ cosScale: 1,
17
+ tanOffset: 0,
18
+ tanScale: 1,
19
+ offset: 0,
20
+ scale: 1
21
+ }),
22
+ channelMix: Object.freeze({
23
+ leftToLeft: 1,
24
+ leftToRight: 0,
25
+ rightToLeft: 0,
26
+ rightToRight: 1
27
+ }),
28
+ lowPass: Object.freeze({ smoothing: 20 })
29
+ })
30
+
31
+ const FILTER_KEYS = Object.freeze(
32
+ Object.fromEntries(
33
+ Object.entries(FILTER_DEFAULTS).map(([k, v]) => [
34
+ k,
35
+ Object.freeze(Object.keys(v))
36
+ ])
37
+ )
38
+ )
39
+
40
+ const EMPTY_ARRAY = Object.freeze([])
41
+
42
+ const FILTER_POOL_SIZE = 16
43
+ const filterPool = {
44
+ pools: Object.fromEntries(Object.keys(FILTER_DEFAULTS).map(k => [k, []])),
45
+
46
+ acquire(type) {
47
+ const pool = this.pools[type]
48
+ if (pool && pool.length > 0) {
49
+ return pool.pop()
50
+ }
51
+ return { ...FILTER_DEFAULTS[type] }
52
+ },
53
+
54
+ release(type, obj) {
55
+ if (!obj || !this.pools[type]) return
56
+ const pool = this.pools[type]
57
+ if (pool.length < FILTER_POOL_SIZE) {
58
+ pool.push(obj)
59
+ }
60
+ }
61
+ }
62
+
63
+ const _utils = Object.freeze({
64
+ shallowEqual(current, defaults, override, keys) {
65
+ if (!current) return false
66
+ for (let i = 0; i < keys.length; i++) {
67
+ const k = keys[i]
68
+ const expected = k in override ? override[k] : defaults[k]
69
+ if (current[k] !== expected) return false
70
+ }
71
+ return true
72
+ },
73
+
74
+ equalizerEqual(a, b) {
75
+ if (a === b) return true
76
+ const lenA = a?.length || 0
77
+ const lenB = b?.length || 0
78
+ if (lenA !== lenB) return false
79
+ for (let i = 0; i < lenA; i++) {
80
+ const x = a[i],
81
+ y = b[i]
82
+ if (x.band !== y.band || x.gain !== y.gain) return false
83
+ }
84
+ return true
85
+ },
86
+
87
+ eqIsEmpty(arr) {
88
+ return !arr || arr.length === 0
89
+ },
90
+
91
+ makeEqArray(len, gain) {
92
+ const out = new Array(len)
93
+ for (let i = 0; i < len; i++) out[i] = { band: i, gain }
94
+ return out
95
+ },
96
+
97
+ mutateFilter(target, defaults, options, keys) {
98
+ let changed = false
99
+ for (let i = 0; i < keys.length; i++) {
100
+ const k = keys[i]
101
+ const newVal = k in options ? options[k] : defaults[k]
102
+ if (target[k] !== newVal) {
103
+ target[k] = newVal
104
+ changed = true
105
+ }
106
+ }
107
+ return changed
108
+ }
109
+ })
110
+
111
+ class Filters {
112
+ constructor(player, options = {}) {
113
+ if (!player) throw new Error('Player instance is required')
114
+ this.player = player
115
+ this._pendingUpdate = false
116
+ this._dirty = new Set()
117
+
118
+ this.filters = {
119
+ volume: options.volume ?? 1,
120
+ equalizer: options.equalizer ?? EMPTY_ARRAY,
121
+ karaoke: options.karaoke ?? null,
122
+ timescale: options.timescale ?? null,
123
+ tremolo: options.tremolo ?? null,
124
+ vibrato: options.vibrato ?? null,
125
+ rotation: options.rotation ?? null,
126
+ distortion: options.distortion ?? null,
127
+ channelMix: options.channelMix ?? null,
128
+ lowPass: options.lowPass ?? null
129
+ }
130
+
131
+ this.presets = {
132
+ bassboost: options.bassboost ?? null,
133
+ slowmode: options.slowmode ?? null,
134
+ nightcore: options.nightcore ?? null,
135
+ vaporwave: options.vaporwave ?? null,
136
+ _8d: options._8d ?? null
137
+ }
138
+ }
139
+
140
+ destroy() {
141
+ for (const [key, value] of Object.entries(this.filters)) {
142
+ if (value && typeof value === 'object' && key !== 'equalizer') {
143
+ filterPool.release(key, value)
144
+ }
145
+ }
146
+ this._pendingUpdate = false
147
+ this.player = null
148
+ }
149
+
150
+ _setFilter(filterName, enabled, options = {}) {
151
+ const current = this.filters[filterName]
152
+ if (!enabled) {
153
+ if (current === null) return this
154
+ filterPool.release(filterName, current)
155
+ this.filters[filterName] = null
156
+ this._dirty.add(filterName)
157
+ return this._scheduleUpdate()
158
+ }
159
+
160
+ const defaults = FILTER_DEFAULTS[filterName]
161
+ const keys = FILTER_KEYS[filterName]
162
+
163
+ if (current) {
164
+ if (_utils.shallowEqual(current, defaults, options, keys)) {
165
+ return this
166
+ }
167
+ _utils.mutateFilter(current, defaults, options, keys)
168
+ this._dirty.add(filterName)
169
+ return this._scheduleUpdate()
170
+ }
171
+
172
+ const newFilter = filterPool.acquire(filterName)
173
+ _utils.mutateFilter(newFilter, defaults, options, keys)
174
+ this.filters[filterName] = newFilter
175
+ this._dirty.add(filterName)
176
+ return this._scheduleUpdate()
177
+ }
178
+
179
+ _scheduleUpdate() {
180
+ if (this._pendingUpdate || !this.player) return this
181
+ this._pendingUpdate = true
182
+ queueMicrotask(() => {
183
+ this._pendingUpdate = false
184
+ if (this.player) {
185
+ this.updateFilters().catch(() => {})
186
+ }
187
+ })
188
+ return this
189
+ }
190
+
191
+ setEqualizer(bands) {
192
+ const next = bands ?? EMPTY_ARRAY
193
+ if (_utils.equalizerEqual(this.filters.equalizer, next)) return this
194
+ this.filters.equalizer = next
195
+ this._dirty.add('equalizer')
196
+ return this._scheduleUpdate()
197
+ }
198
+
199
+ setKaraoke(enabled, options = {}) {
200
+ return this._setFilter('karaoke', enabled, options)
201
+ }
202
+ setTimescale(enabled, options = {}) {
203
+ return this._setFilter('timescale', enabled, options)
204
+ }
205
+ setTremolo(enabled, options = {}) {
206
+ return this._setFilter('tremolo', enabled, options)
207
+ }
208
+ setVibrato(enabled, options = {}) {
209
+ return this._setFilter('vibrato', enabled, options)
210
+ }
211
+ setRotation(enabled, options = {}) {
212
+ return this._setFilter('rotation', enabled, options)
213
+ }
214
+ setDistortion(enabled, options = {}) {
215
+ return this._setFilter('distortion', enabled, options)
216
+ }
217
+ setChannelMix(enabled, options = {}) {
218
+ return this._setFilter('channelMix', enabled, options)
219
+ }
220
+ setLowPass(enabled, options = {}) {
221
+ return this._setFilter('lowPass', enabled, options)
222
+ }
223
+
224
+ setBassboost(enabled, options = {}) {
225
+ if (!enabled) {
226
+ if (
227
+ this.presets.bassboost === null &&
228
+ _utils.eqIsEmpty(this.filters.equalizer)
229
+ )
230
+ return this
231
+ this.presets.bassboost = null
232
+ return this.setEqualizer(EMPTY_ARRAY)
233
+ }
234
+
235
+ const value = options.value ?? 5
236
+ if (value < 0 || value > 5)
237
+ throw new Error('Bassboost value must be between 0 and 5')
238
+ if (this.presets.bassboost === value) return this
239
+
240
+ this.presets.bassboost = value
241
+ const gain = (value - 1) * (1.25 / 9) - 0.25
242
+
243
+ const current = Array.isArray(this.filters.equalizer)
244
+ ? [...this.filters.equalizer]
245
+ : []
246
+ const bands = _utils.makeEqArray(13, gain)
247
+
248
+ for (const b of bands) {
249
+ const idx = current.findIndex((e) => e.band === b.band)
250
+ if (idx !== -1) current[idx] = b
251
+ else current.push(b)
252
+ }
253
+
254
+ return this.setEqualizer(current)
255
+ }
256
+
257
+ setSlowmode(enabled, options = {}) {
258
+ const rate = enabled ? (options.rate ?? 0.8) : 1
259
+ if (
260
+ this.presets.slowmode === enabled &&
261
+ this.filters.timescale?.rate === rate
262
+ )
263
+ return this
264
+ this.presets.slowmode = enabled
265
+ return this.setTimescale(enabled, { rate })
266
+ }
267
+
268
+ setNightcore(enabled, options = {}) {
269
+ const rate = enabled ? (options.rate ?? 1.5) : 1
270
+ if (
271
+ this.presets.nightcore === enabled &&
272
+ this.filters.timescale?.rate === rate
273
+ )
274
+ return this
275
+ this.presets.nightcore = enabled
276
+ return this.setTimescale(enabled, { rate })
277
+ }
278
+
279
+ setVaporwave(enabled, options = {}) {
280
+ const pitch = enabled ? (options.pitch ?? 0.5) : 1
281
+ if (
282
+ this.presets.vaporwave === enabled &&
283
+ this.filters.timescale?.pitch === pitch
284
+ )
285
+ return this
286
+ this.presets.vaporwave = enabled
287
+ return this.setTimescale(enabled, { pitch })
288
+ }
289
+
290
+ set8D(enabled, options = {}) {
291
+ const rotationHz = enabled ? (options.rotationHz ?? 0.2) : 0
292
+ if (
293
+ this.presets._8d === enabled &&
294
+ this.filters.rotation?.rotationHz === rotationHz
295
+ )
296
+ return this
297
+ this.presets._8d = enabled
298
+ return this.setRotation(enabled, { rotationHz })
299
+ }
300
+
301
+ async clearFilters() {
302
+ const f = this.filters
303
+ let changed = false
304
+
305
+ if (f.volume !== 1) {
306
+ f.volume = 1
307
+ this._dirty.add('volume')
308
+ changed = true
309
+ }
310
+ if (!_utils.eqIsEmpty(f.equalizer)) {
311
+ f.equalizer = EMPTY_ARRAY
312
+ this._dirty.add('equalizer')
313
+ changed = true
314
+ }
315
+
316
+ const filterNames = Object.keys(FILTER_DEFAULTS)
317
+ for (let i = 0; i < filterNames.length; i++) {
318
+ const key = filterNames[i]
319
+ if (f[key] !== null) {
320
+ f[key] = null
321
+ this._dirty.add(key)
322
+ changed = true
323
+ }
324
+ }
325
+
326
+ for (const key in this.presets) {
327
+ if (this.presets[key] !== null) this.presets[key] = null
328
+ }
329
+
330
+ return changed ? this.updateFilters() : this
331
+ }
332
+
333
+ async updateFilters() {
334
+ if (!this.player || !this._dirty.size) return this
335
+
336
+ const payload = {}
337
+ for (const key of this._dirty) {
338
+ payload[key] = this.filters[key]
339
+ }
340
+
341
+ this._dirty.clear()
342
+
343
+ await this.player.nodes.rest.updatePlayer({
344
+ guildId: this.player.guildId,
345
+ data: { filters: payload }
346
+ })
347
+ return this
348
+ }
349
+ }
350
+
351
+ module.exports = Filters