audio-channel-queue 1.8.0 → 1.9.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/dist/core.js +4 -3
- package/dist/errors.js +27 -17
- package/dist/events.js +21 -14
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -2
- package/dist/info.js +8 -7
- package/dist/pause.d.ts +24 -12
- package/dist/pause.js +93 -41
- package/dist/types.d.ts +12 -3
- package/dist/types.js +6 -1
- package/dist/utils.js +4 -3
- package/dist/volume.js +37 -15
- package/package.json +8 -3
- package/src/core.ts +59 -42
- package/src/errors.ts +504 -480
- package/src/events.ts +36 -27
- package/src/index.ts +47 -43
- package/src/info.ts +23 -22
- package/src/pause.ts +168 -85
- package/src/types.ts +12 -2
- package/src/utils.ts +7 -7
- package/src/volume.ts +47 -30
package/dist/pause.js
CHANGED
|
@@ -18,22 +18,15 @@ const info_1 = require("./info");
|
|
|
18
18
|
const utils_1 = require("./utils");
|
|
19
19
|
const events_1 = require("./events");
|
|
20
20
|
const volume_1 = require("./volume");
|
|
21
|
-
/**
|
|
22
|
-
* Predefined fade configurations for different transition types
|
|
23
|
-
*/
|
|
24
|
-
const FADE_CONFIGS = {
|
|
25
|
-
[types_1.FadeType.Linear]: { duration: 800, pauseCurve: types_1.EasingType.Linear, resumeCurve: types_1.EasingType.Linear },
|
|
26
|
-
[types_1.FadeType.Gentle]: { duration: 800, pauseCurve: types_1.EasingType.EaseOut, resumeCurve: types_1.EasingType.EaseIn },
|
|
27
|
-
[types_1.FadeType.Dramatic]: { duration: 800, pauseCurve: types_1.EasingType.EaseIn, resumeCurve: types_1.EasingType.EaseOut }
|
|
28
|
-
};
|
|
29
21
|
/**
|
|
30
22
|
* Gets the current volume for a channel, accounting for synchronous state
|
|
31
23
|
* @param channelNumber - The channel number
|
|
32
24
|
* @returns Current volume level (0-1)
|
|
33
25
|
*/
|
|
34
26
|
const getChannelVolumeSync = (channelNumber) => {
|
|
27
|
+
var _a;
|
|
35
28
|
const channel = info_1.audioChannels[channelNumber];
|
|
36
|
-
return (channel === null || channel === void 0 ? void 0 : channel.volume)
|
|
29
|
+
return (_a = channel === null || channel === void 0 ? void 0 : channel.volume) !== null && _a !== void 0 ? _a : 1.0;
|
|
37
30
|
};
|
|
38
31
|
/**
|
|
39
32
|
* Sets the channel volume synchronously in internal state
|
|
@@ -53,15 +46,17 @@ const setChannelVolumeSync = (channelNumber, volume) => {
|
|
|
53
46
|
* Pauses the currently playing audio in a specific channel with smooth volume fade
|
|
54
47
|
* @param fadeType - Type of fade transition to apply
|
|
55
48
|
* @param channelNumber - The channel number to pause (defaults to 0)
|
|
49
|
+
* @param duration - Optional custom fade duration in milliseconds (uses fadeType default if not provided)
|
|
56
50
|
* @returns Promise that resolves when the pause and fade are complete
|
|
57
51
|
* @example
|
|
58
52
|
* ```typescript
|
|
59
53
|
* await pauseWithFade(FadeType.Gentle, 0); // Pause with gentle fade out over 800ms
|
|
60
|
-
* await pauseWithFade(FadeType.Dramatic, 1); // Pause with dramatic fade out over
|
|
61
|
-
* await pauseWithFade(FadeType.Linear, 2); // Linear pause with
|
|
54
|
+
* await pauseWithFade(FadeType.Dramatic, 1, 1500); // Pause with dramatic fade out over 1.5s
|
|
55
|
+
* await pauseWithFade(FadeType.Linear, 2, 500); // Linear pause with custom 500ms fade
|
|
62
56
|
* ```
|
|
63
57
|
*/
|
|
64
|
-
const pauseWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, channelNumber = 0) {
|
|
58
|
+
const pauseWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, channelNumber = 0, duration) {
|
|
59
|
+
var _a, _b, _c;
|
|
65
60
|
const channel = info_1.audioChannels[channelNumber];
|
|
66
61
|
if (!channel || channel.queue.length === 0)
|
|
67
62
|
return;
|
|
@@ -69,24 +64,49 @@ const pauseWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, func
|
|
|
69
64
|
// Don't pause if already paused or ended
|
|
70
65
|
if (currentAudio.paused || currentAudio.ended)
|
|
71
66
|
return;
|
|
72
|
-
const config =
|
|
73
|
-
const
|
|
74
|
-
//
|
|
67
|
+
const config = (0, volume_1.getFadeConfig)(fadeType);
|
|
68
|
+
const effectiveDuration = duration !== null && duration !== void 0 ? duration : config.duration;
|
|
69
|
+
// Race condition fix: Use existing fadeState originalVolume if already transitioning,
|
|
70
|
+
// otherwise capture current volume
|
|
71
|
+
let originalVolume;
|
|
72
|
+
if ((_a = channel.fadeState) === null || _a === void 0 ? void 0 : _a.isTransitioning) {
|
|
73
|
+
// We're already in any kind of transition (pause or resume), preserve original volume
|
|
74
|
+
originalVolume = channel.fadeState.originalVolume;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// First fade or no transition in progress, capture current volume
|
|
78
|
+
// But ensure we don't capture a volume of 0 during a transition
|
|
79
|
+
const currentVolume = getChannelVolumeSync(channelNumber);
|
|
80
|
+
originalVolume = currentVolume > 0 ? currentVolume : (_c = (_b = channel.fadeState) === null || _b === void 0 ? void 0 : _b.originalVolume) !== null && _c !== void 0 ? _c : 1.0;
|
|
81
|
+
}
|
|
82
|
+
// Store fade state for resumeWithFade to use (including custom duration)
|
|
75
83
|
channel.fadeState = {
|
|
76
|
-
|
|
84
|
+
customDuration: duration,
|
|
77
85
|
fadeType,
|
|
78
|
-
isPaused: true
|
|
86
|
+
isPaused: true,
|
|
87
|
+
isTransitioning: true,
|
|
88
|
+
originalVolume
|
|
79
89
|
};
|
|
80
|
-
if (
|
|
90
|
+
if (effectiveDuration === 0) {
|
|
81
91
|
// Instant pause
|
|
82
92
|
yield (0, exports.pauseChannel)(channelNumber);
|
|
93
|
+
// Reset volume to original for resume (synchronously to avoid state issues)
|
|
94
|
+
setChannelVolumeSync(channelNumber, originalVolume);
|
|
95
|
+
// Mark transition as complete for instant pause
|
|
96
|
+
if (channel.fadeState) {
|
|
97
|
+
channel.fadeState.isTransitioning = false;
|
|
98
|
+
}
|
|
83
99
|
return;
|
|
84
100
|
}
|
|
85
101
|
// Fade to 0 with pause curve, then pause
|
|
86
|
-
yield (0, volume_1.transitionVolume)(channelNumber, 0,
|
|
102
|
+
yield (0, volume_1.transitionVolume)(channelNumber, 0, effectiveDuration, config.pauseCurve);
|
|
87
103
|
yield (0, exports.pauseChannel)(channelNumber);
|
|
88
104
|
// Reset volume to original for resume (synchronously to avoid state issues)
|
|
89
105
|
setChannelVolumeSync(channelNumber, originalVolume);
|
|
106
|
+
// Mark transition as complete
|
|
107
|
+
if (channel.fadeState) {
|
|
108
|
+
channel.fadeState.isTransitioning = false;
|
|
109
|
+
}
|
|
90
110
|
});
|
|
91
111
|
exports.pauseWithFade = pauseWithFade;
|
|
92
112
|
/**
|
|
@@ -94,93 +114,122 @@ exports.pauseWithFade = pauseWithFade;
|
|
|
94
114
|
* Uses the complementary fade curve automatically based on the pause fade type, or allows override
|
|
95
115
|
* @param fadeType - Optional fade type to override the stored fade type from pause
|
|
96
116
|
* @param channelNumber - The channel number to resume (defaults to 0)
|
|
117
|
+
* @param duration - Optional custom fade duration in milliseconds (uses stored or fadeType default if not provided)
|
|
97
118
|
* @returns Promise that resolves when the resume and fade are complete
|
|
98
119
|
* @example
|
|
99
120
|
* ```typescript
|
|
100
121
|
* await resumeWithFade(); // Resume with automatically paired fade curve from pause
|
|
101
122
|
* await resumeWithFade(FadeType.Dramatic, 0); // Override with dramatic fade
|
|
102
|
-
* await resumeWithFade(FadeType.Linear); // Override with linear fade
|
|
123
|
+
* await resumeWithFade(FadeType.Linear, 0, 1000); // Override with linear fade over 1 second
|
|
103
124
|
* ```
|
|
104
125
|
*/
|
|
105
|
-
const resumeWithFade = (fadeType_1, ...args_1) => __awaiter(void 0, [fadeType_1, ...args_1], void 0, function* (fadeType, channelNumber = 0) {
|
|
126
|
+
const resumeWithFade = (fadeType_1, ...args_1) => __awaiter(void 0, [fadeType_1, ...args_1], void 0, function* (fadeType, channelNumber = 0, duration) {
|
|
106
127
|
const channel = info_1.audioChannels[channelNumber];
|
|
107
128
|
if (!channel || channel.queue.length === 0)
|
|
108
129
|
return;
|
|
109
130
|
const fadeState = channel.fadeState;
|
|
110
|
-
if (!fadeState ||
|
|
131
|
+
if (!(fadeState === null || fadeState === void 0 ? void 0 : fadeState.isPaused)) {
|
|
111
132
|
// Fall back to regular resume if no fade state
|
|
112
133
|
yield (0, exports.resumeChannel)(channelNumber);
|
|
113
134
|
return;
|
|
114
135
|
}
|
|
115
136
|
// Use provided fadeType or fall back to stored fadeType from pause
|
|
116
|
-
const effectiveFadeType = fadeType
|
|
117
|
-
const config =
|
|
118
|
-
|
|
137
|
+
const effectiveFadeType = fadeType !== null && fadeType !== void 0 ? fadeType : fadeState.fadeType;
|
|
138
|
+
const config = (0, volume_1.getFadeConfig)(effectiveFadeType);
|
|
139
|
+
// Determine effective duration: custom parameter > stored custom > fadeType default
|
|
140
|
+
let effectiveDuration;
|
|
141
|
+
if (duration !== undefined) {
|
|
142
|
+
effectiveDuration = duration;
|
|
143
|
+
}
|
|
144
|
+
else if (fadeState.customDuration !== undefined) {
|
|
145
|
+
effectiveDuration = fadeState.customDuration;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
effectiveDuration = config.duration;
|
|
149
|
+
}
|
|
150
|
+
if (effectiveDuration === 0) {
|
|
119
151
|
// Instant resume
|
|
152
|
+
const targetVolume = fadeState.originalVolume > 0 ? fadeState.originalVolume : 1.0;
|
|
153
|
+
setChannelVolumeSync(channelNumber, targetVolume);
|
|
120
154
|
yield (0, exports.resumeChannel)(channelNumber);
|
|
121
155
|
fadeState.isPaused = false;
|
|
156
|
+
fadeState.isTransitioning = false;
|
|
122
157
|
return;
|
|
123
158
|
}
|
|
159
|
+
// Race condition fix: Ensure we have a valid original volume to restore to
|
|
160
|
+
const targetVolume = fadeState.originalVolume > 0 ? fadeState.originalVolume : 1.0;
|
|
161
|
+
// Mark as transitioning to prevent volume capture during rapid toggles
|
|
162
|
+
fadeState.isTransitioning = true;
|
|
124
163
|
// Set volume to 0, resume, then fade to original with resume curve
|
|
125
164
|
setChannelVolumeSync(channelNumber, 0);
|
|
126
165
|
yield (0, exports.resumeChannel)(channelNumber);
|
|
127
|
-
|
|
166
|
+
// Use the stored original volume, not current volume, to prevent race conditions
|
|
167
|
+
yield (0, volume_1.transitionVolume)(channelNumber, targetVolume, effectiveDuration, config.resumeCurve);
|
|
128
168
|
fadeState.isPaused = false;
|
|
169
|
+
fadeState.isTransitioning = false;
|
|
129
170
|
});
|
|
130
171
|
exports.resumeWithFade = resumeWithFade;
|
|
131
172
|
/**
|
|
132
173
|
* Toggles pause/resume state for a specific channel with integrated fade
|
|
133
174
|
* @param fadeType - Type of fade transition to apply when pausing
|
|
134
175
|
* @param channelNumber - The channel number to toggle (defaults to 0)
|
|
176
|
+
* @param duration - Optional custom fade duration in milliseconds (uses fadeType default if not provided)
|
|
135
177
|
* @returns Promise that resolves when the toggle and fade are complete
|
|
136
178
|
* @example
|
|
137
179
|
* ```typescript
|
|
138
180
|
* await togglePauseWithFade(FadeType.Gentle, 0); // Toggle with gentle fade
|
|
181
|
+
* await togglePauseWithFade(FadeType.Dramatic, 0, 500); // Toggle with custom 500ms fade
|
|
139
182
|
* ```
|
|
140
183
|
*/
|
|
141
|
-
const togglePauseWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, channelNumber = 0) {
|
|
184
|
+
const togglePauseWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, channelNumber = 0, duration) {
|
|
142
185
|
const channel = info_1.audioChannels[channelNumber];
|
|
143
186
|
if (!channel || channel.queue.length === 0)
|
|
144
187
|
return;
|
|
145
188
|
const currentAudio = channel.queue[0];
|
|
146
189
|
if (currentAudio.paused) {
|
|
147
|
-
yield (0, exports.resumeWithFade)(undefined, channelNumber);
|
|
190
|
+
yield (0, exports.resumeWithFade)(undefined, channelNumber, duration);
|
|
148
191
|
}
|
|
149
192
|
else {
|
|
150
|
-
yield (0, exports.pauseWithFade)(fadeType, channelNumber);
|
|
193
|
+
yield (0, exports.pauseWithFade)(fadeType, channelNumber, duration);
|
|
151
194
|
}
|
|
152
195
|
});
|
|
153
196
|
exports.togglePauseWithFade = togglePauseWithFade;
|
|
154
197
|
/**
|
|
155
198
|
* Pauses all currently playing audio across all channels with smooth volume fade
|
|
156
199
|
* @param fadeType - Type of fade transition to apply to all channels
|
|
200
|
+
* @param duration - Optional custom fade duration in milliseconds (uses fadeType default if not provided)
|
|
157
201
|
* @returns Promise that resolves when all channels are paused and faded
|
|
158
202
|
* @example
|
|
159
203
|
* ```typescript
|
|
160
|
-
* await pauseAllWithFade(
|
|
204
|
+
* await pauseAllWithFade(FadeType.Dramatic); // Pause everything with dramatic fade
|
|
205
|
+
* await pauseAllWithFade(FadeType.Gentle, 1200); // Pause all channels with custom 1.2s fade
|
|
161
206
|
* ```
|
|
162
207
|
*/
|
|
163
|
-
const pauseAllWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle) {
|
|
208
|
+
const pauseAllWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, duration) {
|
|
164
209
|
const pausePromises = [];
|
|
165
210
|
info_1.audioChannels.forEach((_channel, index) => {
|
|
166
|
-
pausePromises.push((0, exports.pauseWithFade)(fadeType, index));
|
|
211
|
+
pausePromises.push((0, exports.pauseWithFade)(fadeType, index, duration));
|
|
167
212
|
});
|
|
168
213
|
yield Promise.all(pausePromises);
|
|
169
214
|
});
|
|
170
215
|
exports.pauseAllWithFade = pauseAllWithFade;
|
|
171
216
|
/**
|
|
172
217
|
* Resumes all currently paused audio across all channels with smooth volume fade
|
|
173
|
-
* Uses automatically paired fade curves based on each channel's pause fade type
|
|
218
|
+
* Uses automatically paired fade curves based on each channel's pause fade type, or allows override
|
|
219
|
+
* @param fadeType - Optional fade type to override stored fade types for all channels
|
|
220
|
+
* @param duration - Optional custom fade duration in milliseconds (uses stored or fadeType default if not provided)
|
|
174
221
|
* @returns Promise that resolves when all channels are resumed and faded
|
|
175
222
|
* @example
|
|
176
223
|
* ```typescript
|
|
177
224
|
* await resumeAllWithFade(); // Resume everything with paired fade curves
|
|
225
|
+
* await resumeAllWithFade(FadeType.Gentle, 800); // Override all channels with gentle fade over 800ms
|
|
226
|
+
* await resumeAllWithFade(undefined, 600); // Use stored fade types with custom 600ms duration
|
|
178
227
|
* ```
|
|
179
228
|
*/
|
|
180
|
-
const resumeAllWithFade = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
229
|
+
const resumeAllWithFade = (fadeType, duration) => __awaiter(void 0, void 0, void 0, function* () {
|
|
181
230
|
const resumePromises = [];
|
|
182
231
|
info_1.audioChannels.forEach((_channel, index) => {
|
|
183
|
-
resumePromises.push((0, exports.resumeWithFade)(
|
|
232
|
+
resumePromises.push((0, exports.resumeWithFade)(fadeType, index, duration));
|
|
184
233
|
});
|
|
185
234
|
yield Promise.all(resumePromises);
|
|
186
235
|
});
|
|
@@ -190,13 +239,15 @@ exports.resumeAllWithFade = resumeAllWithFade;
|
|
|
190
239
|
* If any channels are playing, all will be paused with fade
|
|
191
240
|
* If all channels are paused, all will be resumed with fade
|
|
192
241
|
* @param fadeType - Type of fade transition to apply when pausing
|
|
242
|
+
* @param duration - Optional custom fade duration in milliseconds (uses fadeType default if not provided)
|
|
193
243
|
* @returns Promise that resolves when all toggles and fades are complete
|
|
194
244
|
* @example
|
|
195
245
|
* ```typescript
|
|
196
|
-
* await togglePauseAllWithFade(
|
|
246
|
+
* await togglePauseAllWithFade(FadeType.Gentle); // Global toggle with gentle fade
|
|
247
|
+
* await togglePauseAllWithFade(FadeType.Dramatic, 600); // Global toggle with custom 600ms fade
|
|
197
248
|
* ```
|
|
198
249
|
*/
|
|
199
|
-
const togglePauseAllWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle) {
|
|
250
|
+
const togglePauseAllWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, duration) {
|
|
200
251
|
let hasPlayingChannel = false;
|
|
201
252
|
// Check if any channel is currently playing
|
|
202
253
|
for (let i = 0; i < info_1.audioChannels.length; i++) {
|
|
@@ -212,10 +263,10 @@ const togglePauseAllWithFade = (...args_1) => __awaiter(void 0, [...args_1], voi
|
|
|
212
263
|
// If any channel is playing, pause all with fade
|
|
213
264
|
// If no channels are playing, resume all with fade
|
|
214
265
|
if (hasPlayingChannel) {
|
|
215
|
-
yield (0, exports.pauseAllWithFade)(fadeType);
|
|
266
|
+
yield (0, exports.pauseAllWithFade)(fadeType, duration);
|
|
216
267
|
}
|
|
217
268
|
else {
|
|
218
|
-
yield (0, exports.resumeAllWithFade)();
|
|
269
|
+
yield (0, exports.resumeAllWithFade)(fadeType, duration);
|
|
219
270
|
}
|
|
220
271
|
});
|
|
221
272
|
exports.togglePauseAllWithFade = togglePauseAllWithFade;
|
|
@@ -335,8 +386,9 @@ exports.resumeAllChannels = resumeAllChannels;
|
|
|
335
386
|
* ```
|
|
336
387
|
*/
|
|
337
388
|
const isChannelPaused = (channelNumber = 0) => {
|
|
389
|
+
var _a;
|
|
338
390
|
const channel = info_1.audioChannels[channelNumber];
|
|
339
|
-
return (channel === null || channel === void 0 ? void 0 : channel.isPaused)
|
|
391
|
+
return (_a = channel === null || channel === void 0 ? void 0 : channel.isPaused) !== null && _a !== void 0 ? _a : false;
|
|
340
392
|
};
|
|
341
393
|
exports.isChannelPaused = isChannelPaused;
|
|
342
394
|
/**
|
|
@@ -351,7 +403,7 @@ exports.isChannelPaused = isChannelPaused;
|
|
|
351
403
|
* ```
|
|
352
404
|
*/
|
|
353
405
|
const getAllChannelsPauseState = () => {
|
|
354
|
-
return info_1.audioChannels.map((channel) => (channel === null || channel === void 0 ? void 0 : channel.isPaused)
|
|
406
|
+
return info_1.audioChannels.map((channel) => { var _a; return (_a = channel === null || channel === void 0 ? void 0 : channel.isPaused) !== null && _a !== void 0 ? _a : false; });
|
|
355
407
|
};
|
|
356
408
|
exports.getAllChannelsPauseState = getAllChannelsPauseState;
|
|
357
409
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Type definitions for the audio-channel-queue package
|
|
3
3
|
*/
|
|
4
|
+
/**
|
|
5
|
+
* Symbol used as a key for global (channel-wide) progress callbacks
|
|
6
|
+
* This avoids the need for `null as any` type assertions
|
|
7
|
+
*/
|
|
8
|
+
export declare const GLOBAL_PROGRESS_KEY: unique symbol;
|
|
4
9
|
/**
|
|
5
10
|
* Array of HTMLAudioElement objects representing an audio queue
|
|
6
11
|
*/
|
|
@@ -8,9 +13,9 @@ export type AudioQueue = HTMLAudioElement[];
|
|
|
8
13
|
/**
|
|
9
14
|
* Basic audio queue channel structure
|
|
10
15
|
*/
|
|
11
|
-
export
|
|
16
|
+
export interface AudioQueueChannel {
|
|
12
17
|
queue: AudioQueue;
|
|
13
|
-
}
|
|
18
|
+
}
|
|
14
19
|
/**
|
|
15
20
|
* Volume ducking configuration for channels
|
|
16
21
|
*/
|
|
@@ -208,7 +213,7 @@ export interface ExtendedAudioQueueChannel {
|
|
|
208
213
|
audioStartCallbacks: Set<AudioStartCallback>;
|
|
209
214
|
fadeState?: ChannelFadeState;
|
|
210
215
|
isPaused?: boolean;
|
|
211
|
-
progressCallbacks: Map<HTMLAudioElement |
|
|
216
|
+
progressCallbacks: Map<HTMLAudioElement | typeof GLOBAL_PROGRESS_KEY, Set<ProgressCallback>>;
|
|
212
217
|
queue: HTMLAudioElement[];
|
|
213
218
|
queueChangeCallbacks: Set<QueueChangeCallback>;
|
|
214
219
|
retryConfig?: RetryConfig;
|
|
@@ -253,4 +258,8 @@ export interface ChannelFadeState {
|
|
|
253
258
|
fadeType: FadeType;
|
|
254
259
|
/** Whether the channel is currently paused due to fade */
|
|
255
260
|
isPaused: boolean;
|
|
261
|
+
/** Custom duration in milliseconds if specified (overrides fade type default) */
|
|
262
|
+
customDuration?: number;
|
|
263
|
+
/** Whether the channel is currently transitioning (during any fade operation) to prevent capturing intermediate volumes during rapid pause/resume toggles */
|
|
264
|
+
isTransitioning?: boolean;
|
|
256
265
|
}
|
package/dist/types.js
CHANGED
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
* @fileoverview Type definitions for the audio-channel-queue package
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.FadeType = exports.EasingType = void 0;
|
|
6
|
+
exports.FadeType = exports.EasingType = exports.GLOBAL_PROGRESS_KEY = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Symbol used as a key for global (channel-wide) progress callbacks
|
|
9
|
+
* This avoids the need for `null as any` type assertions
|
|
10
|
+
*/
|
|
11
|
+
exports.GLOBAL_PROGRESS_KEY = Symbol('global-progress-callbacks');
|
|
7
12
|
/**
|
|
8
13
|
* Easing function types for volume transitions
|
|
9
14
|
*/
|
package/dist/utils.js
CHANGED
|
@@ -56,7 +56,7 @@ const getAudioInfoFromElement = (audio, channelNumber, audioChannels) => {
|
|
|
56
56
|
const isPlaying = !audio.paused && !audio.ended && audio.readyState > 2;
|
|
57
57
|
// Calculate remainingInQueue if channel context is provided
|
|
58
58
|
let remainingInQueue = 0;
|
|
59
|
-
if (channelNumber !== undefined && audioChannels
|
|
59
|
+
if (channelNumber !== undefined && (audioChannels === null || audioChannels === void 0 ? void 0 : audioChannels[channelNumber])) {
|
|
60
60
|
const channel = audioChannels[channelNumber];
|
|
61
61
|
remainingInQueue = Math.max(0, channel.queue.length - 1); // Exclude current playing audio
|
|
62
62
|
}
|
|
@@ -86,6 +86,7 @@ exports.getAudioInfoFromElement = getAudioInfoFromElement;
|
|
|
86
86
|
* ```
|
|
87
87
|
*/
|
|
88
88
|
const createQueueSnapshot = (channelNumber, audioChannels) => {
|
|
89
|
+
var _a, _b;
|
|
89
90
|
const channel = audioChannels[channelNumber];
|
|
90
91
|
if (!channel)
|
|
91
92
|
return null;
|
|
@@ -100,10 +101,10 @@ const createQueueSnapshot = (channelNumber, audioChannels) => {
|
|
|
100
101
|
return {
|
|
101
102
|
channelNumber,
|
|
102
103
|
currentIndex: 0, // Current playing is always index 0 in our queue structure
|
|
103
|
-
isPaused: channel.isPaused
|
|
104
|
+
isPaused: (_a = channel.isPaused) !== null && _a !== void 0 ? _a : false,
|
|
104
105
|
items,
|
|
105
106
|
totalItems: channel.queue.length,
|
|
106
|
-
volume: channel.volume
|
|
107
|
+
volume: (_b = channel.volume) !== null && _b !== void 0 ? _b : 1.0
|
|
107
108
|
};
|
|
108
109
|
};
|
|
109
110
|
exports.createQueueSnapshot = createQueueSnapshot;
|
package/dist/volume.js
CHANGED
|
@@ -20,10 +20,22 @@ const activeTransitions = new Map();
|
|
|
20
20
|
/**
|
|
21
21
|
* Predefined fade configurations for different transition types
|
|
22
22
|
*/
|
|
23
|
-
const
|
|
24
|
-
[types_1.FadeType.Dramatic]: {
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const fadeConfigs = {
|
|
24
|
+
[types_1.FadeType.Dramatic]: {
|
|
25
|
+
duration: 800,
|
|
26
|
+
pauseCurve: types_1.EasingType.EaseIn,
|
|
27
|
+
resumeCurve: types_1.EasingType.EaseOut
|
|
28
|
+
},
|
|
29
|
+
[types_1.FadeType.Gentle]: {
|
|
30
|
+
duration: 800,
|
|
31
|
+
pauseCurve: types_1.EasingType.EaseOut,
|
|
32
|
+
resumeCurve: types_1.EasingType.EaseIn
|
|
33
|
+
},
|
|
34
|
+
[types_1.FadeType.Linear]: {
|
|
35
|
+
duration: 800,
|
|
36
|
+
pauseCurve: types_1.EasingType.Linear,
|
|
37
|
+
resumeCurve: types_1.EasingType.Linear
|
|
38
|
+
}
|
|
27
39
|
};
|
|
28
40
|
/**
|
|
29
41
|
* Gets the fade configuration for a specific fade type
|
|
@@ -36,7 +48,7 @@ const FADE_CONFIGS = {
|
|
|
36
48
|
* ```
|
|
37
49
|
*/
|
|
38
50
|
const getFadeConfig = (fadeType) => {
|
|
39
|
-
return Object.assign({},
|
|
51
|
+
return Object.assign({}, fadeConfigs[fadeType]);
|
|
40
52
|
};
|
|
41
53
|
exports.getFadeConfig = getFadeConfig;
|
|
42
54
|
/**
|
|
@@ -46,7 +58,7 @@ const easingFunctions = {
|
|
|
46
58
|
[types_1.EasingType.Linear]: (t) => t,
|
|
47
59
|
[types_1.EasingType.EaseIn]: (t) => t * t,
|
|
48
60
|
[types_1.EasingType.EaseOut]: (t) => t * (2 - t),
|
|
49
|
-
[types_1.EasingType.EaseInOut]: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
|
|
61
|
+
[types_1.EasingType.EaseInOut]: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)
|
|
50
62
|
};
|
|
51
63
|
/**
|
|
52
64
|
* Smoothly transitions volume for a specific channel over time
|
|
@@ -69,7 +81,14 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
69
81
|
const volumeDelta = targetVolume - startVolume;
|
|
70
82
|
// Cancel any existing transition for this channel
|
|
71
83
|
if (activeTransitions.has(channelNumber)) {
|
|
72
|
-
|
|
84
|
+
const transitionId = activeTransitions.get(channelNumber);
|
|
85
|
+
if (transitionId) {
|
|
86
|
+
// Handle both requestAnimationFrame and setTimeout IDs
|
|
87
|
+
if (typeof cancelAnimationFrame !== 'undefined') {
|
|
88
|
+
cancelAnimationFrame(transitionId);
|
|
89
|
+
}
|
|
90
|
+
clearTimeout(transitionId);
|
|
91
|
+
}
|
|
73
92
|
activeTransitions.delete(channelNumber);
|
|
74
93
|
}
|
|
75
94
|
// If no change needed, resolve immediately
|
|
@@ -92,7 +111,7 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
92
111
|
const elapsed = performance.now() - startTime;
|
|
93
112
|
const progress = Math.min(elapsed / duration, 1);
|
|
94
113
|
const easedProgress = easingFn(progress);
|
|
95
|
-
const currentVolume = startVolume +
|
|
114
|
+
const currentVolume = startVolume + volumeDelta * easedProgress;
|
|
96
115
|
const clampedVolume = Math.max(0, Math.min(1, currentVolume));
|
|
97
116
|
// Apply volume to both channel config and current audio
|
|
98
117
|
channel.volume = clampedVolume;
|
|
@@ -177,8 +196,9 @@ exports.setChannelVolume = setChannelVolume;
|
|
|
177
196
|
* ```
|
|
178
197
|
*/
|
|
179
198
|
const getChannelVolume = (channelNumber = 0) => {
|
|
199
|
+
var _a;
|
|
180
200
|
const channel = info_1.audioChannels[channelNumber];
|
|
181
|
-
return (channel === null || channel === void 0 ? void 0 : channel.volume)
|
|
201
|
+
return (_a = channel === null || channel === void 0 ? void 0 : channel.volume) !== null && _a !== void 0 ? _a : 1.0;
|
|
182
202
|
};
|
|
183
203
|
exports.getChannelVolume = getChannelVolume;
|
|
184
204
|
/**
|
|
@@ -193,7 +213,7 @@ exports.getChannelVolume = getChannelVolume;
|
|
|
193
213
|
* ```
|
|
194
214
|
*/
|
|
195
215
|
const getAllChannelsVolume = () => {
|
|
196
|
-
return info_1.audioChannels.map((channel) => (channel === null || channel === void 0 ? void 0 : channel.volume)
|
|
216
|
+
return info_1.audioChannels.map((channel) => { var _a; return (_a = channel === null || channel === void 0 ? void 0 : channel.volume) !== null && _a !== void 0 ? _a : 1.0; });
|
|
197
217
|
};
|
|
198
218
|
exports.getAllChannelsVolume = getAllChannelsVolume;
|
|
199
219
|
/**
|
|
@@ -285,11 +305,12 @@ exports.clearVolumeDucking = clearVolumeDucking;
|
|
|
285
305
|
const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
286
306
|
const transitionPromises = [];
|
|
287
307
|
info_1.audioChannels.forEach((channel, channelNumber) => {
|
|
308
|
+
var _a, _b;
|
|
288
309
|
if (channel === null || channel === void 0 ? void 0 : channel.volumeConfig) {
|
|
289
310
|
const config = channel.volumeConfig;
|
|
290
311
|
if (activeChannelNumber === config.priorityChannel) {
|
|
291
|
-
const duration = config.duckTransitionDuration
|
|
292
|
-
const easing = config.transitionEasing
|
|
312
|
+
const duration = (_a = config.duckTransitionDuration) !== null && _a !== void 0 ? _a : 250;
|
|
313
|
+
const easing = (_b = config.transitionEasing) !== null && _b !== void 0 ? _b : types_1.EasingType.EaseOut;
|
|
293
314
|
// Priority channel is active, duck other channels
|
|
294
315
|
if (channelNumber === config.priorityChannel) {
|
|
295
316
|
transitionPromises.push((0, exports.transitionVolume)(channelNumber, config.priorityVolume, duration, easing));
|
|
@@ -329,13 +350,14 @@ exports.fadeVolume = fadeVolume;
|
|
|
329
350
|
const restoreVolumeLevels = (stoppedChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
330
351
|
const transitionPromises = [];
|
|
331
352
|
info_1.audioChannels.forEach((channel, channelNumber) => {
|
|
353
|
+
var _a, _b, _c;
|
|
332
354
|
if (channel === null || channel === void 0 ? void 0 : channel.volumeConfig) {
|
|
333
355
|
const config = channel.volumeConfig;
|
|
334
356
|
if (stoppedChannelNumber === config.priorityChannel) {
|
|
335
|
-
const duration = config.restoreTransitionDuration
|
|
336
|
-
const easing = config.transitionEasing
|
|
357
|
+
const duration = (_a = config.restoreTransitionDuration) !== null && _a !== void 0 ? _a : 500;
|
|
358
|
+
const easing = (_b = config.transitionEasing) !== null && _b !== void 0 ? _b : types_1.EasingType.EaseOut;
|
|
337
359
|
// Priority channel stopped, restore normal volumes
|
|
338
|
-
transitionPromises.push((0, exports.transitionVolume)(channelNumber, channel.volume
|
|
360
|
+
transitionPromises.push((0, exports.transitionVolume)(channelNumber, (_c = channel.volume) !== null && _c !== void 0 ? _c : 1.0, duration, easing));
|
|
339
361
|
}
|
|
340
362
|
}
|
|
341
363
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audio-channel-queue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Allows you to queue audio files to different playback channels.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
"prepare": "npm run build",
|
|
14
14
|
"test": "jest",
|
|
15
15
|
"test:watch": "jest --watch",
|
|
16
|
-
"test:coverage": "jest --coverage"
|
|
16
|
+
"test:coverage": "jest --coverage",
|
|
17
|
+
"lint": "npx eslint src/**/*.ts __tests__/**/*.ts",
|
|
18
|
+
"lint:fix": "npx eslint src/**/*.ts __tests__/**/*.ts --fix",
|
|
19
|
+
"format": "npx prettier --write src/**/*.ts __tests__/**/*.ts",
|
|
20
|
+
"format:check": "npx prettier --check src/**/*.ts __tests__/**/*.ts"
|
|
17
21
|
},
|
|
18
22
|
"repository": {
|
|
19
23
|
"type": "git",
|
|
@@ -39,6 +43,7 @@
|
|
|
39
43
|
"jest": "^29.7.0",
|
|
40
44
|
"jest-environment-jsdom": "^29.7.0",
|
|
41
45
|
"ts-jest": "^29.2.5",
|
|
42
|
-
"typescript": "^5.6.2"
|
|
46
|
+
"typescript": "^5.6.2",
|
|
47
|
+
"typescript-eslint": "^8.34.1"
|
|
43
48
|
}
|
|
44
49
|
}
|