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/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) || false;
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) || 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; });
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 type AudioQueueChannel = {
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?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
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 | null, Set<ProgressCallback>>;
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 && 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.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?: "linear" | "ease-in" | "ease-out" | "ease-in-out") => Promise<void>;
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?: "linear" | "ease-in" | "ease-out" | "ease-in-out") => Promise<void>;
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
- linear: (t) => t,
24
- 'ease-in': (t) => t * t,
25
- 'ease-out': (t) => t * (2 - t),
26
- 'ease-in-out': (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
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 = 'ease-out') {
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
- 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
+ }
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 + (volumeDelta * easedProgress);
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) || 1.0;
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) || 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; });
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 || 250;
269
- const easing = config.transitionEasing || 'ease-out';
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 || 500;
296
- const easing = config.transitionEasing || 'ease-out';
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 || 1.0, duration, easing));
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
  });