audio-channel-queue 1.12.1-beta.0 → 1.12.1-beta.2
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/README.md +56 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -2
- package/dist/volume.d.ts +25 -0
- package/dist/volume.js +59 -8
- package/package.json +5 -2
- package/src/index.ts +2 -0
- package/src/volume.ts +63 -7
package/README.md
CHANGED
|
@@ -5,20 +5,20 @@ The purpose of this package is to help manage the playback of audio files.
|
|
|
5
5
|
|
|
6
6
|
📚 [Docs](https://tonycarpenter21.github.io/audio-queue-docs/)
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Key Features
|
|
9
9
|
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
10
|
+
- **Multi-channel queue management** - Independent audio queues for concurrent playback
|
|
11
|
+
- **Pause/Resume functionality** - Full playback control for individual channels or all channels
|
|
12
|
+
- **Volume control with ducking** - Dynamic volume management, global volume control, and automatic background audio reduction
|
|
13
|
+
- **Loop support** - Seamless audio looping for background music and ambient sounds
|
|
14
|
+
- **Priority queueing** - Add urgent audio to the front of any queue
|
|
15
|
+
- **Real-time progress tracking** - Comprehensive playback monitoring and metadata
|
|
16
|
+
- **Event-driven architecture** - Extensive callback system for UI integration
|
|
17
|
+
- **TypeScript support** - Full type definitions and IntelliSense support
|
|
18
|
+
- **Zero dependencies** - Lightweight and self-contained
|
|
19
|
+
- **Backward compatible** - All existing APIs continue to work
|
|
20
20
|
|
|
21
|
-
This package offers TypeScript support
|
|
21
|
+
This package offers TypeScript support, has zero dependencies, and is released under the MIT license.
|
|
22
22
|
|
|
23
23
|
To preview this package and see how it works with visualized code examples, check out the demo that can be found here: [Audio Channel Queue Demo](https://tonycarpenter21.github.io/audio-queue-demo/). (A link to the demo repo can be found here: [Audio Channel Queue Demo Repo](https://github.com/tonycarpenter21/audio-queue-demo).)
|
|
24
24
|
|
|
@@ -32,18 +32,18 @@ Documentation can be found [here](https://tonycarpenter21.github.io/audio-queue-
|
|
|
32
32
|
|
|
33
33
|
This package is designed for **browser environments** and uses the Web Audio API (`HTMLAudioElement`). It is **not** intended for Node.js server-side use.
|
|
34
34
|
|
|
35
|
-
###
|
|
35
|
+
### **Supported Browsers:**
|
|
36
36
|
- **Chrome 51+** (June 2016)
|
|
37
37
|
- **Firefox 54+** (June 2017)
|
|
38
38
|
- **Safari 10+** (September 2016)
|
|
39
39
|
- **Edge 15+** (April 2017)
|
|
40
40
|
- **Mobile browsers** with HTML5 audio support
|
|
41
41
|
|
|
42
|
-
###
|
|
42
|
+
### **Development Requirements:**
|
|
43
43
|
- **Node.js 14+** (for building and testing only)
|
|
44
44
|
- **TypeScript 4.5+** (included in devDependencies)
|
|
45
45
|
|
|
46
|
-
###
|
|
46
|
+
### **Not Supported:**
|
|
47
47
|
- Node.js server environments (no HTMLAudioElement)
|
|
48
48
|
- Internet Explorer (lacks ES6 support)
|
|
49
49
|
- Web Workers (no DOM access)
|
|
@@ -197,6 +197,47 @@ await setAllChannelsVolume(0.6); // Set all channels to 60% volume
|
|
|
197
197
|
await setAllChannelsVolume(0.0); // Mute all channels
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
+
### Global Volume Control
|
|
201
|
+
```typescript
|
|
202
|
+
// Set a global volume multiplier that affects all channels while preserving their relative levels.
|
|
203
|
+
setGlobalVolume(volume);
|
|
204
|
+
await setGlobalVolume(0.5); // Set global volume to 50%
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Get the current global volume level.
|
|
209
|
+
getGlobalVolume();
|
|
210
|
+
const globalVol = getGlobalVolume(); // Returns current global volume (0-1)
|
|
211
|
+
console.log(`Global volume: ${(getGlobalVolume() * 100).toFixed(0)}%`);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**How Global Volume Works:**
|
|
215
|
+
The global volume acts as a **global volume multiplier** - it scales all channel volumes proportionally while preserving their individual settings. This is perfect for implementing a global volume slider in your UI.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// Example: Setting up different audio types with a global volume control
|
|
219
|
+
await setChannelVolume(0, 0.3); // SFX channel at 30%
|
|
220
|
+
await setChannelVolume(1, 0.7); // Music channel at 70%
|
|
221
|
+
await setChannelVolume(2, 0.5); // Voice channel at 50%
|
|
222
|
+
|
|
223
|
+
// User adjusts global volume slider to 50%
|
|
224
|
+
await setGlobalVolume(0.5);
|
|
225
|
+
|
|
226
|
+
// Actual playback volumes become:
|
|
227
|
+
// - SFX: 30% × 50% = 15%
|
|
228
|
+
// - Music: 70% × 50% = 35%
|
|
229
|
+
// - Voice: 50% × 50% = 25%
|
|
230
|
+
|
|
231
|
+
// Channel volumes remain at their original settings (0.3, 0.7, 0.5)
|
|
232
|
+
// So when global volume is increased, ratios are preserved
|
|
233
|
+
await setGlobalVolume(1.0); // Back to full volume
|
|
234
|
+
// - SFX: 30% × 100% = 30%
|
|
235
|
+
// - Music: 70% × 100% = 70%
|
|
236
|
+
// - Voice: 50% × 100% = 50%
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Note:** `setAllChannelsVolume()` sets all channels to the **same** volume, while `setGlobalVolume()` **multiplies** all channels by the same amount, preserving their relative differences.
|
|
240
|
+
|
|
200
241
|
### Volume Ducking (Background Audio Reduction)
|
|
201
242
|
```typescript
|
|
202
243
|
// Automatically reduce other channels' volume when priority audio plays.
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { queueAudio, queueAudioPriority, stopCurrentAudioInChannel, stopAllAudio
|
|
|
7
7
|
export { clearQueueAfterCurrent, getQueueItemInfo, getQueueLength, removeQueuedItem, reorderQueue, swapQueueItems } from './queue-manipulation';
|
|
8
8
|
export { getErrorRecovery, getRetryConfig, offAudioError, onAudioError, retryFailedAudio, setErrorRecovery, setRetryConfig } from './errors';
|
|
9
9
|
export { getAllChannelsPauseState, isChannelPaused, pauseAllChannels, pauseAllWithFade, pauseChannel, pauseWithFade, resumeAllChannels, resumeAllWithFade, resumeChannel, resumeWithFade, togglePauseAllChannels, togglePauseAllWithFade, togglePauseChannel, togglePauseWithFade } from './pause';
|
|
10
|
-
export { cancelAllVolumeTransitions, cancelVolumeTransition, clearVolumeDucking, getAllChannelsVolume, getChannelVolume, getFadeConfig, setAllChannelsVolume, setChannelVolume, setVolumeDucking, transitionVolume } from './volume';
|
|
10
|
+
export { cancelAllVolumeTransitions, cancelVolumeTransition, clearVolumeDucking, getAllChannelsVolume, getChannelVolume, getFadeConfig, getGlobalVolume, setAllChannelsVolume, setChannelVolume, setGlobalVolume, setVolumeDucking, transitionVolume } from './volume';
|
|
11
11
|
export { cleanupWebAudioNodes, createWebAudioNodes, getAudioContext, getWebAudioConfig, getWebAudioSupport, getWebAudioVolume, isIOSDevice, isWebAudioSupported, resumeAudioContext, setWebAudioConfig, setWebAudioVolume, shouldUseWebAudio } from './web-audio';
|
|
12
12
|
export { getAllChannelsInfo, getCurrentAudioInfo, getQueueSnapshot, offAudioComplete, offAudioPause, offAudioProgress, offAudioResume, offAudioStart, offQueueChange, onAudioComplete, onAudioPause, onAudioProgress, onAudioResume, onAudioStart, onQueueChange } from './info';
|
|
13
13
|
export { audioChannels } from './info';
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* volume management with ducking, progress tracking, and comprehensive event system
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.
|
|
9
|
-
exports.GLOBAL_PROGRESS_KEY = exports.TimerType = exports.MAX_CHANNELS = exports.FadeType = exports.EasingType = exports.AudioErrorType = exports.validateAudioUrl = exports.sanitizeForDisplay = exports.getAudioInfoFromElement = exports.extractFileName = exports.createQueueSnapshot = exports.cleanWebpackFilename = exports.audioChannels = exports.onQueueChange = exports.onAudioStart = exports.onAudioResume = exports.onAudioProgress = exports.onAudioPause = exports.onAudioComplete = exports.offQueueChange = exports.offAudioStart = exports.offAudioResume = exports.offAudioProgress = exports.offAudioPause = exports.offAudioComplete = exports.getQueueSnapshot = exports.getCurrentAudioInfo = exports.getAllChannelsInfo = exports.shouldUseWebAudio = exports.setWebAudioVolume = exports.setWebAudioConfig = exports.resumeAudioContext = exports.isWebAudioSupported = exports.isIOSDevice = exports.getWebAudioVolume = exports.getWebAudioSupport = exports.getWebAudioConfig = exports.getAudioContext = void 0;
|
|
8
|
+
exports.transitionVolume = exports.setVolumeDucking = exports.setGlobalVolume = exports.setChannelVolume = exports.setAllChannelsVolume = exports.getGlobalVolume = exports.getFadeConfig = exports.getChannelVolume = exports.getAllChannelsVolume = exports.clearVolumeDucking = exports.cancelVolumeTransition = exports.cancelAllVolumeTransitions = exports.togglePauseWithFade = exports.togglePauseChannel = exports.togglePauseAllWithFade = exports.togglePauseAllChannels = exports.resumeWithFade = exports.resumeChannel = exports.resumeAllWithFade = exports.resumeAllChannels = exports.pauseWithFade = exports.pauseChannel = exports.pauseAllWithFade = exports.pauseAllChannels = exports.isChannelPaused = exports.getAllChannelsPauseState = exports.setRetryConfig = exports.setErrorRecovery = exports.retryFailedAudio = exports.onAudioError = exports.offAudioError = exports.getRetryConfig = exports.getErrorRecovery = exports.swapQueueItems = exports.reorderQueue = exports.removeQueuedItem = exports.getQueueLength = exports.getQueueItemInfo = exports.clearQueueAfterCurrent = exports.setChannelQueueLimit = exports.getQueueConfig = exports.setQueueConfig = exports.destroyAllChannels = exports.destroyChannel = exports.playAudioQueue = exports.stopAllAudio = exports.stopAllAudioInChannel = exports.stopCurrentAudioInChannel = exports.queueAudioPriority = exports.queueAudio = void 0;
|
|
9
|
+
exports.GLOBAL_PROGRESS_KEY = exports.TimerType = exports.MAX_CHANNELS = exports.FadeType = exports.EasingType = exports.AudioErrorType = exports.validateAudioUrl = exports.sanitizeForDisplay = exports.getAudioInfoFromElement = exports.extractFileName = exports.createQueueSnapshot = exports.cleanWebpackFilename = exports.audioChannels = exports.onQueueChange = exports.onAudioStart = exports.onAudioResume = exports.onAudioProgress = exports.onAudioPause = exports.onAudioComplete = exports.offQueueChange = exports.offAudioStart = exports.offAudioResume = exports.offAudioProgress = exports.offAudioPause = exports.offAudioComplete = exports.getQueueSnapshot = exports.getCurrentAudioInfo = exports.getAllChannelsInfo = exports.shouldUseWebAudio = exports.setWebAudioVolume = exports.setWebAudioConfig = exports.resumeAudioContext = exports.isWebAudioSupported = exports.isIOSDevice = exports.getWebAudioVolume = exports.getWebAudioSupport = exports.getWebAudioConfig = exports.getAudioContext = exports.createWebAudioNodes = exports.cleanupWebAudioNodes = void 0;
|
|
10
10
|
// Core queue management functions
|
|
11
11
|
var core_1 = require("./core");
|
|
12
12
|
Object.defineProperty(exports, "queueAudio", { enumerable: true, get: function () { return core_1.queueAudio; } });
|
|
@@ -61,8 +61,10 @@ Object.defineProperty(exports, "clearVolumeDucking", { enumerable: true, get: fu
|
|
|
61
61
|
Object.defineProperty(exports, "getAllChannelsVolume", { enumerable: true, get: function () { return volume_1.getAllChannelsVolume; } });
|
|
62
62
|
Object.defineProperty(exports, "getChannelVolume", { enumerable: true, get: function () { return volume_1.getChannelVolume; } });
|
|
63
63
|
Object.defineProperty(exports, "getFadeConfig", { enumerable: true, get: function () { return volume_1.getFadeConfig; } });
|
|
64
|
+
Object.defineProperty(exports, "getGlobalVolume", { enumerable: true, get: function () { return volume_1.getGlobalVolume; } });
|
|
64
65
|
Object.defineProperty(exports, "setAllChannelsVolume", { enumerable: true, get: function () { return volume_1.setAllChannelsVolume; } });
|
|
65
66
|
Object.defineProperty(exports, "setChannelVolume", { enumerable: true, get: function () { return volume_1.setChannelVolume; } });
|
|
67
|
+
Object.defineProperty(exports, "setGlobalVolume", { enumerable: true, get: function () { return volume_1.setGlobalVolume; } });
|
|
66
68
|
Object.defineProperty(exports, "setVolumeDucking", { enumerable: true, get: function () { return volume_1.setVolumeDucking; } });
|
|
67
69
|
Object.defineProperty(exports, "transitionVolume", { enumerable: true, get: function () { return volume_1.transitionVolume; } });
|
|
68
70
|
// Web Audio API support functions
|
package/dist/volume.d.ts
CHANGED
|
@@ -74,6 +74,31 @@ export declare const getAllChannelsVolume: () => number[];
|
|
|
74
74
|
* ```
|
|
75
75
|
*/
|
|
76
76
|
export declare const setAllChannelsVolume: (volume: number) => Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Sets the global volume multiplier that affects all channels
|
|
79
|
+
* This acts as a global volume control - individual channel volumes are multiplied by this value
|
|
80
|
+
* @param volume - Global volume level (0-1, will be clamped to this range)
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* // Set channel-specific volumes
|
|
84
|
+
* await setChannelVolume(0, 0.8); // SFX at 80%
|
|
85
|
+
* await setChannelVolume(1, 0.6); // Music at 60%
|
|
86
|
+
*
|
|
87
|
+
* // Apply global volume of 50% - all channels play at half their set volume
|
|
88
|
+
* await setGlobalVolume(0.5); // SFX now plays at 40%, music at 30%
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare const setGlobalVolume: (volume: number) => Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Gets the current global volume multiplier
|
|
94
|
+
* @returns Current global volume level (0-1), defaults to 1.0
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const globalVol = getGlobalVolume();
|
|
98
|
+
* console.log(`Global volume is ${globalVol * 100}%`);
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare const getGlobalVolume: () => number;
|
|
77
102
|
/**
|
|
78
103
|
* Configures volume ducking for channels. When the priority channel plays audio,
|
|
79
104
|
* all other channels will be automatically reduced to the ducking volume level
|
package/dist/volume.js
CHANGED
|
@@ -12,7 +12,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
12
12
|
});
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.cleanupWebAudioForAudio = exports.initializeWebAudioForAudio = exports.cancelAllVolumeTransitions = exports.cancelVolumeTransition = exports.restoreVolumeLevels = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = exports.getFadeConfig = void 0;
|
|
15
|
+
exports.cleanupWebAudioForAudio = exports.initializeWebAudioForAudio = exports.cancelAllVolumeTransitions = exports.cancelVolumeTransition = exports.restoreVolumeLevels = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.getGlobalVolume = exports.setGlobalVolume = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = exports.getFadeConfig = void 0;
|
|
16
16
|
const types_1 = require("./types");
|
|
17
17
|
const info_1 = require("./info");
|
|
18
18
|
const web_audio_1 = require("./web-audio");
|
|
@@ -20,6 +20,11 @@ const web_audio_1 = require("./web-audio");
|
|
|
20
20
|
const activeTransitions = new Map();
|
|
21
21
|
// Track which timer type was used for each channel
|
|
22
22
|
const timerTypes = new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Global volume multiplier that affects all channels
|
|
25
|
+
* Acts as a global volume control (0-1)
|
|
26
|
+
*/
|
|
27
|
+
let globalVolume = 1.0;
|
|
23
28
|
/**
|
|
24
29
|
* Global volume ducking configuration
|
|
25
30
|
* Stores the volume ducking settings that apply to all channels
|
|
@@ -261,6 +266,48 @@ const setAllChannelsVolume = (volume) => __awaiter(void 0, void 0, void 0, funct
|
|
|
261
266
|
yield Promise.all(promises);
|
|
262
267
|
});
|
|
263
268
|
exports.setAllChannelsVolume = setAllChannelsVolume;
|
|
269
|
+
/**
|
|
270
|
+
* Sets the global volume multiplier that affects all channels
|
|
271
|
+
* This acts as a global volume control - individual channel volumes are multiplied by this value
|
|
272
|
+
* @param volume - Global volume level (0-1, will be clamped to this range)
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* // Set channel-specific volumes
|
|
276
|
+
* await setChannelVolume(0, 0.8); // SFX at 80%
|
|
277
|
+
* await setChannelVolume(1, 0.6); // Music at 60%
|
|
278
|
+
*
|
|
279
|
+
* // Apply global volume of 50% - all channels play at half their set volume
|
|
280
|
+
* await setGlobalVolume(0.5); // SFX now plays at 40%, music at 30%
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
const setGlobalVolume = (volume) => __awaiter(void 0, void 0, void 0, function* () {
|
|
284
|
+
// Clamp to valid range
|
|
285
|
+
globalVolume = Math.max(0, Math.min(1, volume));
|
|
286
|
+
// Update all currently playing audio to reflect the new global volume
|
|
287
|
+
// Note: setVolumeForAudio internally multiplies channel.volume by globalVolume
|
|
288
|
+
const updatePromises = [];
|
|
289
|
+
info_1.audioChannels.forEach((channel, channelNumber) => {
|
|
290
|
+
if (channel && channel.queue.length > 0) {
|
|
291
|
+
const currentAudio = channel.queue[0];
|
|
292
|
+
updatePromises.push(setVolumeForAudio(currentAudio, channel.volume, channelNumber));
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
yield Promise.all(updatePromises);
|
|
296
|
+
});
|
|
297
|
+
exports.setGlobalVolume = setGlobalVolume;
|
|
298
|
+
/**
|
|
299
|
+
* Gets the current global volume multiplier
|
|
300
|
+
* @returns Current global volume level (0-1), defaults to 1.0
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* const globalVol = getGlobalVolume();
|
|
304
|
+
* console.log(`Global volume is ${globalVol * 100}%`);
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
const getGlobalVolume = () => {
|
|
308
|
+
return globalVolume;
|
|
309
|
+
};
|
|
310
|
+
exports.getGlobalVolume = getGlobalVolume;
|
|
264
311
|
/**
|
|
265
312
|
* Configures volume ducking for channels. When the priority channel plays audio,
|
|
266
313
|
* all other channels will be automatically reduced to the ducking volume level
|
|
@@ -401,6 +448,8 @@ exports.restoreVolumeLevels = restoreVolumeLevels;
|
|
|
401
448
|
* @internal
|
|
402
449
|
*/
|
|
403
450
|
const transitionAudioVolume = (audio_1, targetVolume_1, ...args_1) => __awaiter(void 0, [audio_1, targetVolume_1, ...args_1], void 0, function* (audio, targetVolume, duration = 250, easing = types_1.EasingType.EaseOut, channelNumber) {
|
|
451
|
+
// Apply global volume multiplier
|
|
452
|
+
const actualTargetVolume = targetVolume * globalVolume;
|
|
404
453
|
// Try to use Web Audio API if available and channel number is provided
|
|
405
454
|
if (channelNumber !== undefined) {
|
|
406
455
|
const channel = info_1.audioChannels[channelNumber];
|
|
@@ -408,23 +457,23 @@ const transitionAudioVolume = (audio_1, targetVolume_1, ...args_1) => __awaiter(
|
|
|
408
457
|
const nodes = channel.webAudioNodes.get(audio);
|
|
409
458
|
if (nodes) {
|
|
410
459
|
// Use Web Audio API for smooth transitions
|
|
411
|
-
(0, web_audio_1.setWebAudioVolume)(nodes.gainNode,
|
|
460
|
+
(0, web_audio_1.setWebAudioVolume)(nodes.gainNode, actualTargetVolume, duration);
|
|
412
461
|
// Also update the audio element's volume property for consistency
|
|
413
|
-
audio.volume =
|
|
462
|
+
audio.volume = actualTargetVolume;
|
|
414
463
|
return;
|
|
415
464
|
}
|
|
416
465
|
}
|
|
417
466
|
}
|
|
418
467
|
// Fallback to standard HTMLAudioElement volume control with manual transition
|
|
419
468
|
const startVolume = audio.volume;
|
|
420
|
-
const volumeDelta =
|
|
469
|
+
const volumeDelta = actualTargetVolume - startVolume;
|
|
421
470
|
// If no change needed, resolve immediately
|
|
422
471
|
if (Math.abs(volumeDelta) < 0.001) {
|
|
423
472
|
return Promise.resolve();
|
|
424
473
|
}
|
|
425
474
|
// Handle zero or negative duration - instant change
|
|
426
475
|
if (duration <= 0) {
|
|
427
|
-
audio.volume =
|
|
476
|
+
audio.volume = actualTargetVolume;
|
|
428
477
|
return Promise.resolve();
|
|
429
478
|
}
|
|
430
479
|
const startTime = performance.now();
|
|
@@ -522,23 +571,25 @@ const initializeWebAudioForChannel = (channelNumber) => __awaiter(void 0, void 0
|
|
|
522
571
|
/**
|
|
523
572
|
* Sets volume for an audio element using the appropriate method (Web Audio API or standard)
|
|
524
573
|
* @param audio - The audio element to set volume for
|
|
525
|
-
* @param volume -
|
|
574
|
+
* @param volume - Channel volume level (0-1) - will be multiplied by global volume
|
|
526
575
|
* @param channelNumber - The channel number this audio belongs to
|
|
527
576
|
* @param transitionDuration - Optional transition duration in milliseconds
|
|
528
577
|
* @internal
|
|
529
578
|
*/
|
|
530
579
|
const setVolumeForAudio = (audio, volume, channelNumber, transitionDuration) => __awaiter(void 0, void 0, void 0, function* () {
|
|
531
580
|
const channel = info_1.audioChannels[channelNumber];
|
|
581
|
+
// Apply global volume multiplier to the channel volume
|
|
582
|
+
const actualVolume = volume * globalVolume;
|
|
532
583
|
// Use Web Audio API if available and initialized
|
|
533
584
|
if ((channel === null || channel === void 0 ? void 0 : channel.webAudioContext) && channel.webAudioNodes) {
|
|
534
585
|
const nodes = channel.webAudioNodes.get(audio);
|
|
535
586
|
if (nodes) {
|
|
536
|
-
(0, web_audio_1.setWebAudioVolume)(nodes.gainNode,
|
|
587
|
+
(0, web_audio_1.setWebAudioVolume)(nodes.gainNode, actualVolume, transitionDuration);
|
|
537
588
|
return;
|
|
538
589
|
}
|
|
539
590
|
}
|
|
540
591
|
// Fallback to standard HTMLAudioElement volume control
|
|
541
|
-
audio.volume =
|
|
592
|
+
audio.volume = actualVolume;
|
|
542
593
|
});
|
|
543
594
|
/**
|
|
544
595
|
* Initializes Web Audio API nodes for a new audio element
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audio-channel-queue",
|
|
3
|
-
"version": "1.12.1-beta.
|
|
3
|
+
"version": "1.12.1-beta.2",
|
|
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",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
|
-
"prepare": "
|
|
13
|
+
"prepare": "husky",
|
|
14
14
|
"test": "jest",
|
|
15
15
|
"test:watch": "jest --watch",
|
|
16
16
|
"test:coverage": "jest --coverage",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"publish:minor": "npm version minor && npm run publish:release",
|
|
27
27
|
"publish:major": "npm version major && npm run publish:release",
|
|
28
28
|
"publish:release": "npm run validate && npm run build && npm publish",
|
|
29
|
+
"publish:beta": "npm version prerelease --preid=beta && npm publish --tag beta",
|
|
29
30
|
"publish:dry": "npm run validate && npm run build && npm publish --dry-run"
|
|
30
31
|
},
|
|
31
32
|
"repository": {
|
|
@@ -49,8 +50,10 @@
|
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@types/jest": "^29.5.13",
|
|
53
|
+
"husky": "^9.1.7",
|
|
52
54
|
"jest": "^29.7.0",
|
|
53
55
|
"jest-environment-jsdom": "^29.7.0",
|
|
56
|
+
"lint-staged": "^16.2.7",
|
|
54
57
|
"rimraf": "^6.0.1",
|
|
55
58
|
"ts-jest": "^29.2.5",
|
|
56
59
|
"typescript": "^5.6.2",
|
package/src/index.ts
CHANGED
package/src/volume.ts
CHANGED
|
@@ -26,6 +26,12 @@ const activeTransitions: Map<number, number> = new Map();
|
|
|
26
26
|
// Track which timer type was used for each channel
|
|
27
27
|
const timerTypes: Map<number, TimerType> = new Map();
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Global volume multiplier that affects all channels
|
|
31
|
+
* Acts as a global volume control (0-1)
|
|
32
|
+
*/
|
|
33
|
+
let globalVolume: number = 1.0;
|
|
34
|
+
|
|
29
35
|
/**
|
|
30
36
|
* Global volume ducking configuration
|
|
31
37
|
* Stores the volume ducking settings that apply to all channels
|
|
@@ -293,6 +299,50 @@ export const setAllChannelsVolume = async (volume: number): Promise<void> => {
|
|
|
293
299
|
await Promise.all(promises);
|
|
294
300
|
};
|
|
295
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Sets the global volume multiplier that affects all channels
|
|
304
|
+
* This acts as a global volume control - individual channel volumes are multiplied by this value
|
|
305
|
+
* @param volume - Global volume level (0-1, will be clamped to this range)
|
|
306
|
+
* @example
|
|
307
|
+
* ```typescript
|
|
308
|
+
* // Set channel-specific volumes
|
|
309
|
+
* await setChannelVolume(0, 0.8); // SFX at 80%
|
|
310
|
+
* await setChannelVolume(1, 0.6); // Music at 60%
|
|
311
|
+
*
|
|
312
|
+
* // Apply global volume of 50% - all channels play at half their set volume
|
|
313
|
+
* await setGlobalVolume(0.5); // SFX now plays at 40%, music at 30%
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
export const setGlobalVolume = async (volume: number): Promise<void> => {
|
|
317
|
+
// Clamp to valid range
|
|
318
|
+
globalVolume = Math.max(0, Math.min(1, volume));
|
|
319
|
+
|
|
320
|
+
// Update all currently playing audio to reflect the new global volume
|
|
321
|
+
// Note: setVolumeForAudio internally multiplies channel.volume by globalVolume
|
|
322
|
+
const updatePromises: Promise<void>[] = [];
|
|
323
|
+
audioChannels.forEach((channel: ExtendedAudioQueueChannel, channelNumber: number) => {
|
|
324
|
+
if (channel && channel.queue.length > 0) {
|
|
325
|
+
const currentAudio: HTMLAudioElement = channel.queue[0];
|
|
326
|
+
updatePromises.push(setVolumeForAudio(currentAudio, channel.volume, channelNumber));
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
await Promise.all(updatePromises);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Gets the current global volume multiplier
|
|
335
|
+
* @returns Current global volume level (0-1), defaults to 1.0
|
|
336
|
+
* @example
|
|
337
|
+
* ```typescript
|
|
338
|
+
* const globalVol = getGlobalVolume();
|
|
339
|
+
* console.log(`Global volume is ${globalVol * 100}%`);
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
export const getGlobalVolume = (): number => {
|
|
343
|
+
return globalVolume;
|
|
344
|
+
};
|
|
345
|
+
|
|
296
346
|
/**
|
|
297
347
|
* Configures volume ducking for channels. When the priority channel plays audio,
|
|
298
348
|
* all other channels will be automatically reduced to the ducking volume level
|
|
@@ -457,6 +507,9 @@ const transitionAudioVolume = async (
|
|
|
457
507
|
easing: EasingType = EasingType.EaseOut,
|
|
458
508
|
channelNumber?: number
|
|
459
509
|
): Promise<void> => {
|
|
510
|
+
// Apply global volume multiplier
|
|
511
|
+
const actualTargetVolume: number = targetVolume * globalVolume;
|
|
512
|
+
|
|
460
513
|
// Try to use Web Audio API if available and channel number is provided
|
|
461
514
|
if (channelNumber !== undefined) {
|
|
462
515
|
const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
|
|
@@ -464,9 +517,9 @@ const transitionAudioVolume = async (
|
|
|
464
517
|
const nodes = channel.webAudioNodes.get(audio);
|
|
465
518
|
if (nodes) {
|
|
466
519
|
// Use Web Audio API for smooth transitions
|
|
467
|
-
setWebAudioVolume(nodes.gainNode,
|
|
520
|
+
setWebAudioVolume(nodes.gainNode, actualTargetVolume, duration);
|
|
468
521
|
// Also update the audio element's volume property for consistency
|
|
469
|
-
audio.volume =
|
|
522
|
+
audio.volume = actualTargetVolume;
|
|
470
523
|
return;
|
|
471
524
|
}
|
|
472
525
|
}
|
|
@@ -474,7 +527,7 @@ const transitionAudioVolume = async (
|
|
|
474
527
|
|
|
475
528
|
// Fallback to standard HTMLAudioElement volume control with manual transition
|
|
476
529
|
const startVolume: number = audio.volume;
|
|
477
|
-
const volumeDelta: number =
|
|
530
|
+
const volumeDelta: number = actualTargetVolume - startVolume;
|
|
478
531
|
|
|
479
532
|
// If no change needed, resolve immediately
|
|
480
533
|
if (Math.abs(volumeDelta) < 0.001) {
|
|
@@ -483,7 +536,7 @@ const transitionAudioVolume = async (
|
|
|
483
536
|
|
|
484
537
|
// Handle zero or negative duration - instant change
|
|
485
538
|
if (duration <= 0) {
|
|
486
|
-
audio.volume =
|
|
539
|
+
audio.volume = actualTargetVolume;
|
|
487
540
|
return Promise.resolve();
|
|
488
541
|
}
|
|
489
542
|
|
|
@@ -594,7 +647,7 @@ const initializeWebAudioForChannel = async (channelNumber: number): Promise<void
|
|
|
594
647
|
/**
|
|
595
648
|
* Sets volume for an audio element using the appropriate method (Web Audio API or standard)
|
|
596
649
|
* @param audio - The audio element to set volume for
|
|
597
|
-
* @param volume -
|
|
650
|
+
* @param volume - Channel volume level (0-1) - will be multiplied by global volume
|
|
598
651
|
* @param channelNumber - The channel number this audio belongs to
|
|
599
652
|
* @param transitionDuration - Optional transition duration in milliseconds
|
|
600
653
|
* @internal
|
|
@@ -607,17 +660,20 @@ const setVolumeForAudio = async (
|
|
|
607
660
|
): Promise<void> => {
|
|
608
661
|
const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
|
|
609
662
|
|
|
663
|
+
// Apply global volume multiplier to the channel volume
|
|
664
|
+
const actualVolume: number = volume * globalVolume;
|
|
665
|
+
|
|
610
666
|
// Use Web Audio API if available and initialized
|
|
611
667
|
if (channel?.webAudioContext && channel.webAudioNodes) {
|
|
612
668
|
const nodes = channel.webAudioNodes.get(audio);
|
|
613
669
|
if (nodes) {
|
|
614
|
-
setWebAudioVolume(nodes.gainNode,
|
|
670
|
+
setWebAudioVolume(nodes.gainNode, actualVolume, transitionDuration);
|
|
615
671
|
return;
|
|
616
672
|
}
|
|
617
673
|
}
|
|
618
674
|
|
|
619
675
|
// Fallback to standard HTMLAudioElement volume control
|
|
620
|
-
audio.volume =
|
|
676
|
+
audio.volume = actualVolume;
|
|
621
677
|
};
|
|
622
678
|
|
|
623
679
|
/**
|