audio-channel-queue 1.6.0 → 1.8.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 +53 -34
- package/dist/errors.d.ts +137 -0
- package/dist/errors.js +461 -0
- package/dist/index.d.ts +10 -25
- package/dist/index.js +36 -32
- package/dist/info.d.ts +2 -1
- package/dist/info.js +8 -1
- package/dist/pause.d.ts +71 -0
- package/dist/pause.js +205 -2
- package/dist/types.d.ts +93 -20
- package/dist/types.js +20 -0
- package/dist/volume.d.ts +28 -3
- package/dist/volume.js +52 -9
- package/package.json +1 -1
- package/src/core.ts +55 -35
- package/src/errors.ts +480 -0
- package/src/index.ts +87 -83
- package/src/info.ts +8 -1
- package/src/pause.ts +411 -189
- package/src/types.ts +99 -18
- package/src/volume.ts +375 -327
package/dist/volume.js
CHANGED
|
@@ -12,18 +12,41 @@ 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 FADE_CONFIGS = {
|
|
24
|
+
[types_1.FadeType.Dramatic]: { duration: 800, pauseCurve: types_1.EasingType.EaseIn, resumeCurve: types_1.EasingType.EaseOut },
|
|
25
|
+
[types_1.FadeType.Gentle]: { duration: 800, pauseCurve: types_1.EasingType.EaseOut, resumeCurve: types_1.EasingType.EaseIn },
|
|
26
|
+
[types_1.FadeType.Linear]: { duration: 800, pauseCurve: types_1.EasingType.Linear, resumeCurve: types_1.EasingType.Linear }
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Gets the fade configuration for a specific fade type
|
|
30
|
+
* @param fadeType - The fade type to get configuration for
|
|
31
|
+
* @returns Fade configuration object
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const config = getFadeConfig('gentle');
|
|
35
|
+
* console.log(`Gentle fade duration: ${config.duration}ms`);
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
const getFadeConfig = (fadeType) => {
|
|
39
|
+
return Object.assign({}, FADE_CONFIGS[fadeType]);
|
|
40
|
+
};
|
|
41
|
+
exports.getFadeConfig = getFadeConfig;
|
|
19
42
|
/**
|
|
20
43
|
* Easing functions for smooth volume transitions
|
|
21
44
|
*/
|
|
22
45
|
const easingFunctions = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
[types_1.EasingType.Linear]: (t) => t,
|
|
47
|
+
[types_1.EasingType.EaseIn]: (t) => t * t,
|
|
48
|
+
[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
|
|
27
50
|
};
|
|
28
51
|
/**
|
|
29
52
|
* Smoothly transitions volume for a specific channel over time
|
|
@@ -37,7 +60,7 @@ const easingFunctions = {
|
|
|
37
60
|
* await transitionVolume(0, 0.2, 500, 'ease-out'); // Duck to 20% over 500ms
|
|
38
61
|
* ```
|
|
39
62
|
*/
|
|
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 =
|
|
63
|
+
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
64
|
const channel = info_1.audioChannels[channelNumber];
|
|
42
65
|
if (!channel || channel.queue.length === 0)
|
|
43
66
|
return;
|
|
@@ -55,7 +78,7 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
55
78
|
return Promise.resolve();
|
|
56
79
|
}
|
|
57
80
|
// Handle zero duration - instant change
|
|
58
|
-
if (duration
|
|
81
|
+
if (duration === 0) {
|
|
59
82
|
channel.volume = targetVolume;
|
|
60
83
|
if (channel.queue.length > 0) {
|
|
61
84
|
channel.queue[0].volume = targetVolume;
|
|
@@ -115,6 +138,7 @@ const setChannelVolume = (channelNumber, volume, transitionDuration, easing) =>
|
|
|
115
138
|
if (!info_1.audioChannels[channelNumber]) {
|
|
116
139
|
info_1.audioChannels[channelNumber] = {
|
|
117
140
|
audioCompleteCallbacks: new Set(),
|
|
141
|
+
audioErrorCallbacks: new Set(),
|
|
118
142
|
audioPauseCallbacks: new Set(),
|
|
119
143
|
audioResumeCallbacks: new Set(),
|
|
120
144
|
audioStartCallbacks: new Set(),
|
|
@@ -207,6 +231,7 @@ const setVolumeDucking = (config) => {
|
|
|
207
231
|
while (info_1.audioChannels.length <= config.priorityChannel) {
|
|
208
232
|
info_1.audioChannels.push({
|
|
209
233
|
audioCompleteCallbacks: new Set(),
|
|
234
|
+
audioErrorCallbacks: new Set(),
|
|
210
235
|
audioPauseCallbacks: new Set(),
|
|
211
236
|
audioResumeCallbacks: new Set(),
|
|
212
237
|
audioStartCallbacks: new Set(),
|
|
@@ -222,6 +247,7 @@ const setVolumeDucking = (config) => {
|
|
|
222
247
|
if (!info_1.audioChannels[index]) {
|
|
223
248
|
info_1.audioChannels[index] = {
|
|
224
249
|
audioCompleteCallbacks: new Set(),
|
|
250
|
+
audioErrorCallbacks: new Set(),
|
|
225
251
|
audioPauseCallbacks: new Set(),
|
|
226
252
|
audioResumeCallbacks: new Set(),
|
|
227
253
|
audioStartCallbacks: new Set(),
|
|
@@ -263,7 +289,7 @@ const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, vo
|
|
|
263
289
|
const config = channel.volumeConfig;
|
|
264
290
|
if (activeChannelNumber === config.priorityChannel) {
|
|
265
291
|
const duration = config.duckTransitionDuration || 250;
|
|
266
|
-
const easing = config.transitionEasing ||
|
|
292
|
+
const easing = config.transitionEasing || types_1.EasingType.EaseOut;
|
|
267
293
|
// Priority channel is active, duck other channels
|
|
268
294
|
if (channelNumber === config.priorityChannel) {
|
|
269
295
|
transitionPromises.push((0, exports.transitionVolume)(channelNumber, config.priorityVolume, duration, easing));
|
|
@@ -278,6 +304,23 @@ const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, vo
|
|
|
278
304
|
yield Promise.all(transitionPromises);
|
|
279
305
|
});
|
|
280
306
|
exports.applyVolumeDucking = applyVolumeDucking;
|
|
307
|
+
/**
|
|
308
|
+
* Fades the volume for a specific channel over time (alias for transitionVolume with improved naming)
|
|
309
|
+
* @param channelNumber - The channel number to fade
|
|
310
|
+
* @param targetVolume - Target volume level (0-1)
|
|
311
|
+
* @param duration - Fade duration in milliseconds (defaults to 250)
|
|
312
|
+
* @param easing - Easing function type (defaults to 'ease-out')
|
|
313
|
+
* @returns Promise that resolves when fade completes
|
|
314
|
+
* @example
|
|
315
|
+
* ```typescript
|
|
316
|
+
* await fadeVolume(0, 0, 800, 'ease-in'); // Fade out over 800ms
|
|
317
|
+
* await fadeVolume(0, 1, 600, 'ease-out'); // Fade in over 600ms
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
320
|
+
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) {
|
|
321
|
+
return (0, exports.transitionVolume)(channelNumber, targetVolume, duration, easing);
|
|
322
|
+
});
|
|
323
|
+
exports.fadeVolume = fadeVolume;
|
|
281
324
|
/**
|
|
282
325
|
* Restores normal volume levels when priority channel stops with smooth transitions
|
|
283
326
|
* @param stoppedChannelNumber - The channel that just stopped playing
|
|
@@ -290,7 +333,7 @@ const restoreVolumeLevels = (stoppedChannelNumber) => __awaiter(void 0, void 0,
|
|
|
290
333
|
const config = channel.volumeConfig;
|
|
291
334
|
if (stoppedChannelNumber === config.priorityChannel) {
|
|
292
335
|
const duration = config.restoreTransitionDuration || 500;
|
|
293
|
-
const easing = config.transitionEasing ||
|
|
336
|
+
const easing = config.transitionEasing || types_1.EasingType.EaseOut;
|
|
294
337
|
// Priority channel stopped, restore normal volumes
|
|
295
338
|
transitionPromises.push((0, exports.transitionVolume)(channelNumber, channel.volume || 1.0, duration, easing));
|
|
296
339
|
}
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
cleanupProgressTracking
|
|
14
14
|
} from './events';
|
|
15
15
|
import { applyVolumeDucking, restoreVolumeLevels } from './volume';
|
|
16
|
+
import { setupAudioErrorHandling, handleAudioError } from './errors';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Queues an audio file to a specific channel and starts playing if it's the first in queue
|
|
@@ -33,9 +34,11 @@ export const queueAudio = async (
|
|
|
33
34
|
channelNumber: number = 0,
|
|
34
35
|
options?: AudioQueueOptions
|
|
35
36
|
): Promise<void> => {
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
// Ensure the channel exists
|
|
38
|
+
while (audioChannels.length <= channelNumber) {
|
|
39
|
+
audioChannels.push({
|
|
38
40
|
audioCompleteCallbacks: new Set(),
|
|
41
|
+
audioErrorCallbacks: new Set(),
|
|
39
42
|
audioPauseCallbacks: new Set(),
|
|
40
43
|
audioResumeCallbacks: new Set(),
|
|
41
44
|
audioStartCallbacks: new Set(),
|
|
@@ -44,46 +47,56 @@ export const queueAudio = async (
|
|
|
44
47
|
queue: [],
|
|
45
48
|
queueChangeCallbacks: new Set(),
|
|
46
49
|
volume: 1.0
|
|
47
|
-
};
|
|
50
|
+
});
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
|
|
50
54
|
const audio: HTMLAudioElement = new Audio(audioUrl);
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
audio
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
|
|
56
|
+
// Set up comprehensive error handling
|
|
57
|
+
setupAudioErrorHandling(audio, channelNumber, audioUrl, async (error: Error) => {
|
|
58
|
+
await handleAudioError(audio, channelNumber, audioUrl, error);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Apply options if provided
|
|
62
|
+
if (options) {
|
|
63
|
+
if (typeof options.loop === 'boolean') {
|
|
64
|
+
audio.loop = options.loop;
|
|
65
|
+
}
|
|
66
|
+
if (typeof options.volume === 'number' && !isNaN(options.volume)) {
|
|
67
|
+
const clampedVolume = Math.max(0, Math.min(1, options.volume));
|
|
68
|
+
audio.volume = clampedVolume;
|
|
69
|
+
// Set channel volume to match the audio volume
|
|
70
|
+
channel.volume = clampedVolume;
|
|
71
|
+
}
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
|
|
74
|
+
// Handle priority option (same as addToFront for backward compatibility)
|
|
75
|
+
const shouldAddToFront = options?.addToFront || options?.priority;
|
|
76
|
+
|
|
77
|
+
// Add to queue based on priority/addToFront option
|
|
78
|
+
if (shouldAddToFront && channel.queue.length > 0) {
|
|
79
|
+
// Insert after currently playing track (at index 1)
|
|
80
|
+
channel.queue.splice(1, 0, audio);
|
|
81
|
+
} else if (shouldAddToFront) {
|
|
82
|
+
// If queue is empty, add to front
|
|
83
|
+
channel.queue.unshift(audio);
|
|
77
84
|
} else {
|
|
78
|
-
//
|
|
79
|
-
|
|
85
|
+
// Add to back of queue
|
|
86
|
+
channel.queue.push(audio);
|
|
80
87
|
}
|
|
81
88
|
|
|
89
|
+
// Emit queue change event
|
|
82
90
|
emitQueueChange(channelNumber, audioChannels);
|
|
83
91
|
|
|
84
|
-
if
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
// Start playing if this is the first item and channel isn't paused
|
|
93
|
+
if (channel.queue.length === 1 && !channel.isPaused) {
|
|
94
|
+
// Use setTimeout to ensure the queue change event is emitted first
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
playAudioQueue(channelNumber).catch((error: Error) => {
|
|
97
|
+
handleAudioError(audio, channelNumber, audioUrl, error);
|
|
98
|
+
});
|
|
99
|
+
}, 0);
|
|
87
100
|
}
|
|
88
101
|
};
|
|
89
102
|
|
|
@@ -188,8 +201,11 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
188
201
|
if (currentAudio.loop) {
|
|
189
202
|
// For looping audio, reset current time and continue playing
|
|
190
203
|
currentAudio.currentTime = 0;
|
|
191
|
-
|
|
192
|
-
|
|
204
|
+
try {
|
|
205
|
+
await currentAudio.play();
|
|
206
|
+
} catch (error) {
|
|
207
|
+
await handleAudioError(currentAudio, channelNumber, currentAudio.src, error as Error);
|
|
208
|
+
}
|
|
193
209
|
resolve();
|
|
194
210
|
} else {
|
|
195
211
|
// For non-looping audio, remove from queue and play next
|
|
@@ -213,7 +229,11 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
213
229
|
metadataLoaded = true;
|
|
214
230
|
}
|
|
215
231
|
|
|
216
|
-
|
|
232
|
+
// Enhanced play with error handling
|
|
233
|
+
currentAudio.play().catch(async (error: Error) => {
|
|
234
|
+
await handleAudioError(currentAudio, channelNumber, currentAudio.src, error);
|
|
235
|
+
resolve(); // Resolve to prevent hanging
|
|
236
|
+
});
|
|
217
237
|
});
|
|
218
238
|
};
|
|
219
239
|
|