audio-channel-queue 1.7.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 +6 -5
- package/dist/index.js +16 -1
- package/dist/info.js +8 -7
- package/dist/pause.d.ts +83 -0
- package/dist/pause.js +258 -3
- package/dist/types.d.ts +53 -4
- package/dist/types.js +25 -0
- package/dist/utils.js +4 -3
- package/dist/volume.d.ts +28 -3
- package/dist/volume.js +77 -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 +60 -39
- package/src/info.ts +23 -22
- package/src/pause.ts +328 -23
- package/src/types.ts +57 -3
- package/src/utils.ts +7 -7
- package/src/volume.ts +92 -30
package/dist/pause.js
CHANGED
|
@@ -12,10 +12,264 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
12
12
|
});
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.togglePauseAllChannels = exports.getAllChannelsPauseState = exports.isChannelPaused = exports.resumeAllChannels = exports.pauseAllChannels = exports.togglePauseChannel = exports.resumeChannel = exports.pauseChannel = void 0;
|
|
15
|
+
exports.togglePauseAllChannels = exports.getAllChannelsPauseState = exports.isChannelPaused = exports.resumeAllChannels = exports.pauseAllChannels = exports.togglePauseChannel = exports.resumeChannel = exports.pauseChannel = exports.togglePauseAllWithFade = exports.resumeAllWithFade = exports.pauseAllWithFade = exports.togglePauseWithFade = exports.resumeWithFade = exports.pauseWithFade = void 0;
|
|
16
|
+
const types_1 = require("./types");
|
|
16
17
|
const info_1 = require("./info");
|
|
17
18
|
const utils_1 = require("./utils");
|
|
18
19
|
const events_1 = require("./events");
|
|
20
|
+
const volume_1 = require("./volume");
|
|
21
|
+
/**
|
|
22
|
+
* Gets the current volume for a channel, accounting for synchronous state
|
|
23
|
+
* @param channelNumber - The channel number
|
|
24
|
+
* @returns Current volume level (0-1)
|
|
25
|
+
*/
|
|
26
|
+
const getChannelVolumeSync = (channelNumber) => {
|
|
27
|
+
var _a;
|
|
28
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
29
|
+
return (_a = channel === null || channel === void 0 ? void 0 : channel.volume) !== null && _a !== void 0 ? _a : 1.0;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Sets the channel volume synchronously in internal state
|
|
33
|
+
* @param channelNumber - The channel number
|
|
34
|
+
* @param volume - Volume level (0-1)
|
|
35
|
+
*/
|
|
36
|
+
const setChannelVolumeSync = (channelNumber, volume) => {
|
|
37
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
38
|
+
if (channel) {
|
|
39
|
+
channel.volume = volume;
|
|
40
|
+
if (channel.queue.length > 0) {
|
|
41
|
+
channel.queue[0].volume = volume;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Pauses the currently playing audio in a specific channel with smooth volume fade
|
|
47
|
+
* @param fadeType - Type of fade transition to apply
|
|
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)
|
|
50
|
+
* @returns Promise that resolves when the pause and fade are complete
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* await pauseWithFade(FadeType.Gentle, 0); // Pause with gentle fade out over 800ms
|
|
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
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
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;
|
|
60
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
61
|
+
if (!channel || channel.queue.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
const currentAudio = channel.queue[0];
|
|
64
|
+
// Don't pause if already paused or ended
|
|
65
|
+
if (currentAudio.paused || currentAudio.ended)
|
|
66
|
+
return;
|
|
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)
|
|
83
|
+
channel.fadeState = {
|
|
84
|
+
customDuration: duration,
|
|
85
|
+
fadeType,
|
|
86
|
+
isPaused: true,
|
|
87
|
+
isTransitioning: true,
|
|
88
|
+
originalVolume
|
|
89
|
+
};
|
|
90
|
+
if (effectiveDuration === 0) {
|
|
91
|
+
// Instant pause
|
|
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
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Fade to 0 with pause curve, then pause
|
|
102
|
+
yield (0, volume_1.transitionVolume)(channelNumber, 0, effectiveDuration, config.pauseCurve);
|
|
103
|
+
yield (0, exports.pauseChannel)(channelNumber);
|
|
104
|
+
// Reset volume to original for resume (synchronously to avoid state issues)
|
|
105
|
+
setChannelVolumeSync(channelNumber, originalVolume);
|
|
106
|
+
// Mark transition as complete
|
|
107
|
+
if (channel.fadeState) {
|
|
108
|
+
channel.fadeState.isTransitioning = false;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
exports.pauseWithFade = pauseWithFade;
|
|
112
|
+
/**
|
|
113
|
+
* Resumes the currently paused audio in a specific channel with smooth volume fade
|
|
114
|
+
* Uses the complementary fade curve automatically based on the pause fade type, or allows override
|
|
115
|
+
* @param fadeType - Optional fade type to override the stored fade type from pause
|
|
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)
|
|
118
|
+
* @returns Promise that resolves when the resume and fade are complete
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* await resumeWithFade(); // Resume with automatically paired fade curve from pause
|
|
122
|
+
* await resumeWithFade(FadeType.Dramatic, 0); // Override with dramatic fade
|
|
123
|
+
* await resumeWithFade(FadeType.Linear, 0, 1000); // Override with linear fade over 1 second
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
const resumeWithFade = (fadeType_1, ...args_1) => __awaiter(void 0, [fadeType_1, ...args_1], void 0, function* (fadeType, channelNumber = 0, duration) {
|
|
127
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
128
|
+
if (!channel || channel.queue.length === 0)
|
|
129
|
+
return;
|
|
130
|
+
const fadeState = channel.fadeState;
|
|
131
|
+
if (!(fadeState === null || fadeState === void 0 ? void 0 : fadeState.isPaused)) {
|
|
132
|
+
// Fall back to regular resume if no fade state
|
|
133
|
+
yield (0, exports.resumeChannel)(channelNumber);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Use provided fadeType or fall back to stored fadeType from pause
|
|
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) {
|
|
151
|
+
// Instant resume
|
|
152
|
+
const targetVolume = fadeState.originalVolume > 0 ? fadeState.originalVolume : 1.0;
|
|
153
|
+
setChannelVolumeSync(channelNumber, targetVolume);
|
|
154
|
+
yield (0, exports.resumeChannel)(channelNumber);
|
|
155
|
+
fadeState.isPaused = false;
|
|
156
|
+
fadeState.isTransitioning = false;
|
|
157
|
+
return;
|
|
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;
|
|
163
|
+
// Set volume to 0, resume, then fade to original with resume curve
|
|
164
|
+
setChannelVolumeSync(channelNumber, 0);
|
|
165
|
+
yield (0, exports.resumeChannel)(channelNumber);
|
|
166
|
+
// Use the stored original volume, not current volume, to prevent race conditions
|
|
167
|
+
yield (0, volume_1.transitionVolume)(channelNumber, targetVolume, effectiveDuration, config.resumeCurve);
|
|
168
|
+
fadeState.isPaused = false;
|
|
169
|
+
fadeState.isTransitioning = false;
|
|
170
|
+
});
|
|
171
|
+
exports.resumeWithFade = resumeWithFade;
|
|
172
|
+
/**
|
|
173
|
+
* Toggles pause/resume state for a specific channel with integrated fade
|
|
174
|
+
* @param fadeType - Type of fade transition to apply when pausing
|
|
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)
|
|
177
|
+
* @returns Promise that resolves when the toggle and fade are complete
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* await togglePauseWithFade(FadeType.Gentle, 0); // Toggle with gentle fade
|
|
181
|
+
* await togglePauseWithFade(FadeType.Dramatic, 0, 500); // Toggle with custom 500ms fade
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
const togglePauseWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, channelNumber = 0, duration) {
|
|
185
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
186
|
+
if (!channel || channel.queue.length === 0)
|
|
187
|
+
return;
|
|
188
|
+
const currentAudio = channel.queue[0];
|
|
189
|
+
if (currentAudio.paused) {
|
|
190
|
+
yield (0, exports.resumeWithFade)(undefined, channelNumber, duration);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
yield (0, exports.pauseWithFade)(fadeType, channelNumber, duration);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
exports.togglePauseWithFade = togglePauseWithFade;
|
|
197
|
+
/**
|
|
198
|
+
* Pauses all currently playing audio across all channels with smooth volume fade
|
|
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)
|
|
201
|
+
* @returns Promise that resolves when all channels are paused and faded
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* await pauseAllWithFade(FadeType.Dramatic); // Pause everything with dramatic fade
|
|
205
|
+
* await pauseAllWithFade(FadeType.Gentle, 1200); // Pause all channels with custom 1.2s fade
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
const pauseAllWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, duration) {
|
|
209
|
+
const pausePromises = [];
|
|
210
|
+
info_1.audioChannels.forEach((_channel, index) => {
|
|
211
|
+
pausePromises.push((0, exports.pauseWithFade)(fadeType, index, duration));
|
|
212
|
+
});
|
|
213
|
+
yield Promise.all(pausePromises);
|
|
214
|
+
});
|
|
215
|
+
exports.pauseAllWithFade = pauseAllWithFade;
|
|
216
|
+
/**
|
|
217
|
+
* Resumes all currently paused audio across all channels with smooth volume fade
|
|
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)
|
|
221
|
+
* @returns Promise that resolves when all channels are resumed and faded
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
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
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
const resumeAllWithFade = (fadeType, duration) => __awaiter(void 0, void 0, void 0, function* () {
|
|
230
|
+
const resumePromises = [];
|
|
231
|
+
info_1.audioChannels.forEach((_channel, index) => {
|
|
232
|
+
resumePromises.push((0, exports.resumeWithFade)(fadeType, index, duration));
|
|
233
|
+
});
|
|
234
|
+
yield Promise.all(resumePromises);
|
|
235
|
+
});
|
|
236
|
+
exports.resumeAllWithFade = resumeAllWithFade;
|
|
237
|
+
/**
|
|
238
|
+
* Toggles pause/resume state for all channels with integrated fade
|
|
239
|
+
* If any channels are playing, all will be paused with fade
|
|
240
|
+
* If all channels are paused, all will be resumed with fade
|
|
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)
|
|
243
|
+
* @returns Promise that resolves when all toggles and fades are complete
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* await togglePauseAllWithFade(FadeType.Gentle); // Global toggle with gentle fade
|
|
247
|
+
* await togglePauseAllWithFade(FadeType.Dramatic, 600); // Global toggle with custom 600ms fade
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
const togglePauseAllWithFade = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (fadeType = types_1.FadeType.Gentle, duration) {
|
|
251
|
+
let hasPlayingChannel = false;
|
|
252
|
+
// Check if any channel is currently playing
|
|
253
|
+
for (let i = 0; i < info_1.audioChannels.length; i++) {
|
|
254
|
+
const channel = info_1.audioChannels[i];
|
|
255
|
+
if (channel && channel.queue.length > 0) {
|
|
256
|
+
const currentAudio = channel.queue[0];
|
|
257
|
+
if (!currentAudio.paused && !currentAudio.ended) {
|
|
258
|
+
hasPlayingChannel = true;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// If any channel is playing, pause all with fade
|
|
264
|
+
// If no channels are playing, resume all with fade
|
|
265
|
+
if (hasPlayingChannel) {
|
|
266
|
+
yield (0, exports.pauseAllWithFade)(fadeType, duration);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
yield (0, exports.resumeAllWithFade)(fadeType, duration);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
exports.togglePauseAllWithFade = togglePauseAllWithFade;
|
|
19
273
|
/**
|
|
20
274
|
* Pauses the currently playing audio in a specific channel
|
|
21
275
|
* @param channelNumber - The channel number to pause (defaults to 0)
|
|
@@ -132,8 +386,9 @@ exports.resumeAllChannels = resumeAllChannels;
|
|
|
132
386
|
* ```
|
|
133
387
|
*/
|
|
134
388
|
const isChannelPaused = (channelNumber = 0) => {
|
|
389
|
+
var _a;
|
|
135
390
|
const channel = info_1.audioChannels[channelNumber];
|
|
136
|
-
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;
|
|
137
392
|
};
|
|
138
393
|
exports.isChannelPaused = isChannelPaused;
|
|
139
394
|
/**
|
|
@@ -148,7 +403,7 @@ exports.isChannelPaused = isChannelPaused;
|
|
|
148
403
|
* ```
|
|
149
404
|
*/
|
|
150
405
|
const getAllChannelsPauseState = () => {
|
|
151
|
-
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; });
|
|
152
407
|
};
|
|
153
408
|
exports.getAllChannelsPauseState = getAllChannelsPauseState;
|
|
154
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
|
*/
|
|
@@ -26,7 +31,7 @@ export interface VolumeConfig {
|
|
|
26
31
|
/** Duration in milliseconds for volume restore transition (defaults to 500ms) */
|
|
27
32
|
restoreTransitionDuration?: number;
|
|
28
33
|
/** Easing function for volume transitions (defaults to 'ease-out') */
|
|
29
|
-
transitionEasing?:
|
|
34
|
+
transitionEasing?: EasingType;
|
|
30
35
|
}
|
|
31
36
|
/**
|
|
32
37
|
* Audio file configuration for queueing
|
|
@@ -206,11 +211,55 @@ export interface ExtendedAudioQueueChannel {
|
|
|
206
211
|
audioPauseCallbacks: Set<AudioPauseCallback>;
|
|
207
212
|
audioResumeCallbacks: Set<AudioResumeCallback>;
|
|
208
213
|
audioStartCallbacks: Set<AudioStartCallback>;
|
|
214
|
+
fadeState?: ChannelFadeState;
|
|
209
215
|
isPaused?: boolean;
|
|
210
|
-
progressCallbacks: Map<HTMLAudioElement |
|
|
216
|
+
progressCallbacks: Map<HTMLAudioElement | typeof GLOBAL_PROGRESS_KEY, Set<ProgressCallback>>;
|
|
211
217
|
queue: HTMLAudioElement[];
|
|
212
218
|
queueChangeCallbacks: Set<QueueChangeCallback>;
|
|
213
219
|
retryConfig?: RetryConfig;
|
|
214
220
|
volume?: number;
|
|
215
221
|
volumeConfig?: VolumeConfig;
|
|
216
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Easing function types for volume transitions
|
|
225
|
+
*/
|
|
226
|
+
export declare enum EasingType {
|
|
227
|
+
Linear = "linear",
|
|
228
|
+
EaseIn = "ease-in",
|
|
229
|
+
EaseOut = "ease-out",
|
|
230
|
+
EaseInOut = "ease-in-out"
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Fade type for pause/resume operations with integrated volume transitions
|
|
234
|
+
*/
|
|
235
|
+
export declare enum FadeType {
|
|
236
|
+
Linear = "linear",
|
|
237
|
+
Gentle = "gentle",
|
|
238
|
+
Dramatic = "dramatic"
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Configuration for fade transitions
|
|
242
|
+
*/
|
|
243
|
+
export interface FadeConfig {
|
|
244
|
+
/** Duration in milliseconds for the fade transition */
|
|
245
|
+
duration: number;
|
|
246
|
+
/** Easing curve to use when pausing (fading out) */
|
|
247
|
+
pauseCurve: EasingType;
|
|
248
|
+
/** Easing curve to use when resuming (fading in) */
|
|
249
|
+
resumeCurve: EasingType;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Internal fade state tracking for pause/resume with fade functionality
|
|
253
|
+
*/
|
|
254
|
+
export interface ChannelFadeState {
|
|
255
|
+
/** The original volume level before fading began */
|
|
256
|
+
originalVolume: number;
|
|
257
|
+
/** The type of fade being used */
|
|
258
|
+
fadeType: FadeType;
|
|
259
|
+
/** Whether the channel is currently paused due to fade */
|
|
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;
|
|
265
|
+
}
|
package/dist/types.js
CHANGED
|
@@ -3,3 +3,28 @@
|
|
|
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 = 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');
|
|
12
|
+
/**
|
|
13
|
+
* Easing function types for volume transitions
|
|
14
|
+
*/
|
|
15
|
+
var EasingType;
|
|
16
|
+
(function (EasingType) {
|
|
17
|
+
EasingType["Linear"] = "linear";
|
|
18
|
+
EasingType["EaseIn"] = "ease-in";
|
|
19
|
+
EasingType["EaseOut"] = "ease-out";
|
|
20
|
+
EasingType["EaseInOut"] = "ease-in-out";
|
|
21
|
+
})(EasingType || (exports.EasingType = EasingType = {}));
|
|
22
|
+
/**
|
|
23
|
+
* Fade type for pause/resume operations with integrated volume transitions
|
|
24
|
+
*/
|
|
25
|
+
var FadeType;
|
|
26
|
+
(function (FadeType) {
|
|
27
|
+
FadeType["Linear"] = "linear";
|
|
28
|
+
FadeType["Gentle"] = "gentle";
|
|
29
|
+
FadeType["Dramatic"] = "dramatic";
|
|
30
|
+
})(FadeType || (exports.FadeType = FadeType = {}));
|
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.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Volume management functions for the audio-channel-queue package
|
|
3
3
|
*/
|
|
4
|
-
import { VolumeConfig } from './types';
|
|
4
|
+
import { VolumeConfig, FadeType, FadeConfig, EasingType } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Gets the fade configuration for a specific fade type
|
|
7
|
+
* @param fadeType - The fade type to get configuration for
|
|
8
|
+
* @returns Fade configuration object
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const config = getFadeConfig('gentle');
|
|
12
|
+
* console.log(`Gentle fade duration: ${config.duration}ms`);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare const getFadeConfig: (fadeType: FadeType) => FadeConfig;
|
|
5
16
|
/**
|
|
6
17
|
* Smoothly transitions volume for a specific channel over time
|
|
7
18
|
* @param channelNumber - The channel number to transition
|
|
@@ -14,7 +25,7 @@ import { VolumeConfig } from './types';
|
|
|
14
25
|
* await transitionVolume(0, 0.2, 500, 'ease-out'); // Duck to 20% over 500ms
|
|
15
26
|
* ```
|
|
16
27
|
*/
|
|
17
|
-
export declare const transitionVolume: (channelNumber: number, targetVolume: number, duration?: number, easing?:
|
|
28
|
+
export declare const transitionVolume: (channelNumber: number, targetVolume: number, duration?: number, easing?: EasingType) => Promise<void>;
|
|
18
29
|
/**
|
|
19
30
|
* Sets the volume for a specific channel with optional smooth transition
|
|
20
31
|
* @param channelNumber - The channel number to set volume for
|
|
@@ -27,7 +38,7 @@ export declare const transitionVolume: (channelNumber: number, targetVolume: num
|
|
|
27
38
|
* setChannelVolume(0, 0.5, 300, 'ease-out'); // Smooth transition over 300ms
|
|
28
39
|
* ```
|
|
29
40
|
*/
|
|
30
|
-
export declare const setChannelVolume: (channelNumber: number, volume: number, transitionDuration?: number, easing?:
|
|
41
|
+
export declare const setChannelVolume: (channelNumber: number, volume: number, transitionDuration?: number, easing?: EasingType) => Promise<void>;
|
|
31
42
|
/**
|
|
32
43
|
* Gets the current volume for a specific channel
|
|
33
44
|
* @param channelNumber - The channel number to get volume for (defaults to 0)
|
|
@@ -90,6 +101,20 @@ export declare const clearVolumeDucking: () => void;
|
|
|
90
101
|
* @internal
|
|
91
102
|
*/
|
|
92
103
|
export declare const applyVolumeDucking: (activeChannelNumber: number) => Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Fades the volume for a specific channel over time (alias for transitionVolume with improved naming)
|
|
106
|
+
* @param channelNumber - The channel number to fade
|
|
107
|
+
* @param targetVolume - Target volume level (0-1)
|
|
108
|
+
* @param duration - Fade duration in milliseconds (defaults to 250)
|
|
109
|
+
* @param easing - Easing function type (defaults to 'ease-out')
|
|
110
|
+
* @returns Promise that resolves when fade completes
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* await fadeVolume(0, 0, 800, 'ease-in'); // Fade out over 800ms
|
|
114
|
+
* await fadeVolume(0, 1, 600, 'ease-out'); // Fade in over 600ms
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare const fadeVolume: (channelNumber: number, targetVolume: number, duration?: number, easing?: EasingType) => Promise<void>;
|
|
93
118
|
/**
|
|
94
119
|
* Restores normal volume levels when priority channel stops with smooth transitions
|
|
95
120
|
* @param stoppedChannelNumber - The channel that just stopped playing
|
package/dist/volume.js
CHANGED
|
@@ -12,18 +12,53 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
12
12
|
});
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.restoreVolumeLevels = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = void 0;
|
|
15
|
+
exports.restoreVolumeLevels = exports.fadeVolume = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = exports.getFadeConfig = void 0;
|
|
16
|
+
const types_1 = require("./types");
|
|
16
17
|
const info_1 = require("./info");
|
|
17
18
|
// Store active volume transitions to handle interruptions
|
|
18
19
|
const activeTransitions = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* Predefined fade configurations for different transition types
|
|
22
|
+
*/
|
|
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
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Gets the fade configuration for a specific fade type
|
|
42
|
+
* @param fadeType - The fade type to get configuration for
|
|
43
|
+
* @returns Fade configuration object
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const config = getFadeConfig('gentle');
|
|
47
|
+
* console.log(`Gentle fade duration: ${config.duration}ms`);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
const getFadeConfig = (fadeType) => {
|
|
51
|
+
return Object.assign({}, fadeConfigs[fadeType]);
|
|
52
|
+
};
|
|
53
|
+
exports.getFadeConfig = getFadeConfig;
|
|
19
54
|
/**
|
|
20
55
|
* Easing functions for smooth volume transitions
|
|
21
56
|
*/
|
|
22
57
|
const easingFunctions = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
58
|
+
[types_1.EasingType.Linear]: (t) => t,
|
|
59
|
+
[types_1.EasingType.EaseIn]: (t) => t * t,
|
|
60
|
+
[types_1.EasingType.EaseOut]: (t) => t * (2 - t),
|
|
61
|
+
[types_1.EasingType.EaseInOut]: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)
|
|
27
62
|
};
|
|
28
63
|
/**
|
|
29
64
|
* Smoothly transitions volume for a specific channel over time
|
|
@@ -37,7 +72,7 @@ const easingFunctions = {
|
|
|
37
72
|
* await transitionVolume(0, 0.2, 500, 'ease-out'); // Duck to 20% over 500ms
|
|
38
73
|
* ```
|
|
39
74
|
*/
|
|
40
|
-
const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __awaiter(void 0, [channelNumber_1, targetVolume_1, ...args_1], void 0, function* (channelNumber, targetVolume, duration = 250, easing =
|
|
75
|
+
const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __awaiter(void 0, [channelNumber_1, targetVolume_1, ...args_1], void 0, function* (channelNumber, targetVolume, duration = 250, easing = types_1.EasingType.EaseOut) {
|
|
41
76
|
const channel = info_1.audioChannels[channelNumber];
|
|
42
77
|
if (!channel || channel.queue.length === 0)
|
|
43
78
|
return;
|
|
@@ -46,7 +81,14 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
46
81
|
const volumeDelta = targetVolume - startVolume;
|
|
47
82
|
// Cancel any existing transition for this channel
|
|
48
83
|
if (activeTransitions.has(channelNumber)) {
|
|
49
|
-
|
|
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
|
+
}
|
|
50
92
|
activeTransitions.delete(channelNumber);
|
|
51
93
|
}
|
|
52
94
|
// If no change needed, resolve immediately
|
|
@@ -69,7 +111,7 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
69
111
|
const elapsed = performance.now() - startTime;
|
|
70
112
|
const progress = Math.min(elapsed / duration, 1);
|
|
71
113
|
const easedProgress = easingFn(progress);
|
|
72
|
-
const currentVolume = startVolume +
|
|
114
|
+
const currentVolume = startVolume + volumeDelta * easedProgress;
|
|
73
115
|
const clampedVolume = Math.max(0, Math.min(1, currentVolume));
|
|
74
116
|
// Apply volume to both channel config and current audio
|
|
75
117
|
channel.volume = clampedVolume;
|
|
@@ -154,8 +196,9 @@ exports.setChannelVolume = setChannelVolume;
|
|
|
154
196
|
* ```
|
|
155
197
|
*/
|
|
156
198
|
const getChannelVolume = (channelNumber = 0) => {
|
|
199
|
+
var _a;
|
|
157
200
|
const channel = info_1.audioChannels[channelNumber];
|
|
158
|
-
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;
|
|
159
202
|
};
|
|
160
203
|
exports.getChannelVolume = getChannelVolume;
|
|
161
204
|
/**
|
|
@@ -170,7 +213,7 @@ exports.getChannelVolume = getChannelVolume;
|
|
|
170
213
|
* ```
|
|
171
214
|
*/
|
|
172
215
|
const getAllChannelsVolume = () => {
|
|
173
|
-
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; });
|
|
174
217
|
};
|
|
175
218
|
exports.getAllChannelsVolume = getAllChannelsVolume;
|
|
176
219
|
/**
|
|
@@ -262,11 +305,12 @@ exports.clearVolumeDucking = clearVolumeDucking;
|
|
|
262
305
|
const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
263
306
|
const transitionPromises = [];
|
|
264
307
|
info_1.audioChannels.forEach((channel, channelNumber) => {
|
|
308
|
+
var _a, _b;
|
|
265
309
|
if (channel === null || channel === void 0 ? void 0 : channel.volumeConfig) {
|
|
266
310
|
const config = channel.volumeConfig;
|
|
267
311
|
if (activeChannelNumber === config.priorityChannel) {
|
|
268
|
-
const duration = config.duckTransitionDuration
|
|
269
|
-
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;
|
|
270
314
|
// Priority channel is active, duck other channels
|
|
271
315
|
if (channelNumber === config.priorityChannel) {
|
|
272
316
|
transitionPromises.push((0, exports.transitionVolume)(channelNumber, config.priorityVolume, duration, easing));
|
|
@@ -281,6 +325,23 @@ const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, vo
|
|
|
281
325
|
yield Promise.all(transitionPromises);
|
|
282
326
|
});
|
|
283
327
|
exports.applyVolumeDucking = applyVolumeDucking;
|
|
328
|
+
/**
|
|
329
|
+
* Fades the volume for a specific channel over time (alias for transitionVolume with improved naming)
|
|
330
|
+
* @param channelNumber - The channel number to fade
|
|
331
|
+
* @param targetVolume - Target volume level (0-1)
|
|
332
|
+
* @param duration - Fade duration in milliseconds (defaults to 250)
|
|
333
|
+
* @param easing - Easing function type (defaults to 'ease-out')
|
|
334
|
+
* @returns Promise that resolves when fade completes
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* await fadeVolume(0, 0, 800, 'ease-in'); // Fade out over 800ms
|
|
338
|
+
* await fadeVolume(0, 1, 600, 'ease-out'); // Fade in over 600ms
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
const fadeVolume = (channelNumber_1, targetVolume_1, ...args_1) => __awaiter(void 0, [channelNumber_1, targetVolume_1, ...args_1], void 0, function* (channelNumber, targetVolume, duration = 250, easing = types_1.EasingType.EaseOut) {
|
|
342
|
+
return (0, exports.transitionVolume)(channelNumber, targetVolume, duration, easing);
|
|
343
|
+
});
|
|
344
|
+
exports.fadeVolume = fadeVolume;
|
|
284
345
|
/**
|
|
285
346
|
* Restores normal volume levels when priority channel stops with smooth transitions
|
|
286
347
|
* @param stoppedChannelNumber - The channel that just stopped playing
|
|
@@ -289,13 +350,14 @@ exports.applyVolumeDucking = applyVolumeDucking;
|
|
|
289
350
|
const restoreVolumeLevels = (stoppedChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
290
351
|
const transitionPromises = [];
|
|
291
352
|
info_1.audioChannels.forEach((channel, channelNumber) => {
|
|
353
|
+
var _a, _b, _c;
|
|
292
354
|
if (channel === null || channel === void 0 ? void 0 : channel.volumeConfig) {
|
|
293
355
|
const config = channel.volumeConfig;
|
|
294
356
|
if (stoppedChannelNumber === config.priorityChannel) {
|
|
295
|
-
const duration = config.restoreTransitionDuration
|
|
296
|
-
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;
|
|
297
359
|
// Priority channel stopped, restore normal volumes
|
|
298
|
-
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));
|
|
299
361
|
}
|
|
300
362
|
}
|
|
301
363
|
});
|