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/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) || 1.0;
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 800ms
61
- * await pauseWithFade(FadeType.Linear, 2); // Linear pause with 800ms fade
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 = FADE_CONFIGS[fadeType];
73
- const originalVolume = getChannelVolumeSync(channelNumber);
74
- // Store fade state for resumeWithFade to use
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
- originalVolume,
84
+ customDuration: duration,
77
85
  fadeType,
78
- isPaused: true
86
+ isPaused: true,
87
+ isTransitioning: true,
88
+ originalVolume
79
89
  };
80
- if (config.duration === 0) {
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, config.duration, config.pauseCurve);
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 on default channel
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 || !fadeState.isPaused) {
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 || fadeState.fadeType;
117
- const config = FADE_CONFIGS[effectiveFadeType];
118
- if (config.duration === 0) {
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
- yield (0, volume_1.transitionVolume)(channelNumber, fadeState.originalVolume, config.duration, config.resumeCurve);
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('dramatic'); // Pause everything with dramatic fade
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)(undefined, index));
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('gentle'); // Global toggle with gentle fade
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) || false;
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) || false);
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 type AudioQueueChannel = {
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 | null, Set<ProgressCallback>>;
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 && audioChannels[channelNumber]) {
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 || false,
104
+ isPaused: (_a = channel.isPaused) !== null && _a !== void 0 ? _a : false,
104
105
  items,
105
106
  totalItems: channel.queue.length,
106
- volume: channel.volume || 1.0
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 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 }
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({}, FADE_CONFIGS[fadeType]);
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
- clearTimeout(activeTransitions.get(channelNumber));
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 + (volumeDelta * easedProgress);
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) || 1.0;
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) || 1.0);
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 || 250;
292
- const easing = config.transitionEasing || types_1.EasingType.EaseOut;
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 || 500;
336
- const easing = config.transitionEasing || types_1.EasingType.EaseOut;
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 || 1.0, duration, easing));
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.8.0",
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
  }