audio-channel-queue 1.5.0 → 1.7.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/types.d.ts CHANGED
@@ -11,6 +11,36 @@ export type AudioQueue = HTMLAudioElement[];
11
11
  export type AudioQueueChannel = {
12
12
  queue: AudioQueue;
13
13
  };
14
+ /**
15
+ * Volume ducking configuration for channels
16
+ */
17
+ export interface VolumeConfig {
18
+ /** The channel number that should have priority */
19
+ priorityChannel: number;
20
+ /** Volume level for the priority channel (0-1) */
21
+ priorityVolume: number;
22
+ /** Volume level for all other channels when priority channel is active (0-1) */
23
+ duckingVolume: number;
24
+ /** Duration in milliseconds for volume duck transition (defaults to 250ms) */
25
+ duckTransitionDuration?: number;
26
+ /** Duration in milliseconds for volume restore transition (defaults to 500ms) */
27
+ restoreTransitionDuration?: number;
28
+ /** Easing function for volume transitions (defaults to 'ease-out') */
29
+ transitionEasing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
30
+ }
31
+ /**
32
+ * Audio file configuration for queueing
33
+ */
34
+ export interface AudioQueueOptions {
35
+ /** Whether to add the audio to the front of the queue (defaults to false) */
36
+ addToFront?: boolean;
37
+ /** Whether the audio should loop when it finishes */
38
+ loop?: boolean;
39
+ /** Whether to add the audio with priority (same as addToFront) */
40
+ priority?: boolean;
41
+ /** Volume level for this specific audio file (0-1, defaults to channel volume) */
42
+ volume?: number;
43
+ }
14
44
  /**
15
45
  * Comprehensive audio information interface providing metadata about currently playing audio
16
46
  */
@@ -21,12 +51,20 @@ export interface AudioInfo {
21
51
  duration: number;
22
52
  /** Extracted filename from the source URL */
23
53
  fileName: string;
54
+ /** Whether the audio is set to loop */
55
+ isLooping: boolean;
56
+ /** Whether the audio is currently paused */
57
+ isPaused: boolean;
24
58
  /** Whether the audio is currently playing */
25
59
  isPlaying: boolean;
26
60
  /** Playback progress as a decimal (0-1) */
27
61
  progress: number;
62
+ /** Number of audio files remaining in the queue after current */
63
+ remainingInQueue: number;
28
64
  /** Audio file source URL */
29
65
  src: string;
66
+ /** Current volume level (0-1) */
67
+ volume: number;
30
68
  }
31
69
  /**
32
70
  * Information provided when an audio file completes playback
@@ -64,8 +102,12 @@ export interface QueueItem {
64
102
  fileName: string;
65
103
  /** Whether this item is currently playing */
66
104
  isCurrentlyPlaying: boolean;
105
+ /** Whether this item is set to loop */
106
+ isLooping: boolean;
67
107
  /** Audio file source URL */
68
108
  src: string;
109
+ /** Volume level for this item (0-1) */
110
+ volume: number;
69
111
  }
70
112
  /**
71
113
  * Complete snapshot of a queue's current state
@@ -75,10 +117,14 @@ export interface QueueSnapshot {
75
117
  channelNumber: number;
76
118
  /** Zero-based index of the currently playing item */
77
119
  currentIndex: number;
120
+ /** Whether the current audio is paused */
121
+ isPaused: boolean;
78
122
  /** Array of audio items in the queue with their metadata */
79
123
  items: QueueItem[];
80
124
  /** Total number of items in the queue */
81
125
  totalItems: number;
126
+ /** Current volume level for the channel (0-1) */
127
+ volume: number;
82
128
  }
83
129
  /**
84
130
  * Callback function type for audio progress updates
@@ -101,15 +147,70 @@ export type AudioStartCallback = (audioInfo: AudioStartInfo) => void;
101
147
  */
102
148
  export type AudioCompleteCallback = (audioInfo: AudioCompleteInfo) => void;
103
149
  /**
104
- * Extended audio queue channel with event callback management
105
- */
106
- export type ExtendedAudioQueueChannel = AudioQueueChannel & {
107
- /** Set of callbacks for audio completion events */
108
- audioCompleteCallbacks?: Set<AudioCompleteCallback>;
109
- /** Set of callbacks for audio start events */
110
- audioStartCallbacks?: Set<AudioStartCallback>;
111
- /** Map of audio elements to their progress callback sets */
112
- progressCallbacks?: Map<HTMLAudioElement, Set<ProgressCallback>>;
113
- /** Set of callbacks for queue change events */
114
- queueChangeCallbacks?: Set<QueueChangeCallback>;
115
- };
150
+ * Callback function type for audio pause notifications
151
+ * @param channelNumber Channel that was paused
152
+ * @param audioInfo Information about the audio that was paused
153
+ */
154
+ export type AudioPauseCallback = (channelNumber: number, audioInfo: AudioInfo) => void;
155
+ /**
156
+ * Callback function type for audio resume notifications
157
+ * @param channelNumber Channel that was resumed
158
+ * @param audioInfo Information about the audio that was resumed
159
+ */
160
+ export type AudioResumeCallback = (channelNumber: number, audioInfo: AudioInfo) => void;
161
+ /**
162
+ * Information about an audio error that occurred
163
+ */
164
+ export interface AudioErrorInfo {
165
+ channelNumber: number;
166
+ src: string;
167
+ fileName: string;
168
+ error: Error;
169
+ errorType: 'network' | 'decode' | 'unsupported' | 'permission' | 'abort' | 'timeout' | 'unknown';
170
+ timestamp: number;
171
+ retryAttempt?: number;
172
+ remainingInQueue: number;
173
+ }
174
+ /**
175
+ * Configuration for automatic retry behavior when audio fails to load or play
176
+ */
177
+ export interface RetryConfig {
178
+ enabled: boolean;
179
+ maxRetries: number;
180
+ baseDelay: number;
181
+ exponentialBackoff: boolean;
182
+ timeoutMs: number;
183
+ fallbackUrls?: string[];
184
+ skipOnFailure: boolean;
185
+ }
186
+ /**
187
+ * Configuration options for error recovery mechanisms
188
+ */
189
+ export interface ErrorRecoveryOptions {
190
+ autoRetry: boolean;
191
+ showUserFeedback: boolean;
192
+ logErrorsToAnalytics: boolean;
193
+ preserveQueueOnError: boolean;
194
+ fallbackToNextTrack: boolean;
195
+ }
196
+ /**
197
+ * Callback function type for audio error events
198
+ */
199
+ export type AudioErrorCallback = (errorInfo: AudioErrorInfo) => void;
200
+ /**
201
+ * Extended audio queue channel with error handling capabilities
202
+ */
203
+ export interface ExtendedAudioQueueChannel {
204
+ audioCompleteCallbacks: Set<AudioCompleteCallback>;
205
+ audioErrorCallbacks: Set<AudioErrorCallback>;
206
+ audioPauseCallbacks: Set<AudioPauseCallback>;
207
+ audioResumeCallbacks: Set<AudioResumeCallback>;
208
+ audioStartCallbacks: Set<AudioStartCallback>;
209
+ isPaused?: boolean;
210
+ progressCallbacks: Map<HTMLAudioElement | null, Set<ProgressCallback>>;
211
+ queue: HTMLAudioElement[];
212
+ queueChangeCallbacks: Set<QueueChangeCallback>;
213
+ retryConfig?: RetryConfig;
214
+ volume?: number;
215
+ volumeConfig?: VolumeConfig;
216
+ }
package/dist/utils.d.ts CHANGED
@@ -16,15 +16,21 @@ export declare const extractFileName: (url: string) => string;
16
16
  /**
17
17
  * Extracts comprehensive audio information from an HTMLAudioElement
18
18
  * @param audio - The HTML audio element to extract information from
19
+ * @param channelNumber - Optional channel number to include remaining queue info
20
+ * @param audioChannels - Optional audio channels array to calculate remainingInQueue
19
21
  * @returns AudioInfo object with current playback state or null if audio is invalid
20
22
  * @example
21
23
  * ```typescript
22
24
  * const audioElement = new Audio('song.mp3');
23
25
  * const info = getAudioInfoFromElement(audioElement);
24
26
  * console.log(info?.progress); // Current progress as decimal (0-1)
27
+ *
28
+ * // With channel context for remainingInQueue
29
+ * const infoWithQueue = getAudioInfoFromElement(audioElement, 0, audioChannels);
30
+ * console.log(infoWithQueue?.remainingInQueue); // Number of items left in queue
25
31
  * ```
26
32
  */
27
- export declare const getAudioInfoFromElement: (audio: HTMLAudioElement) => AudioInfo | null;
33
+ export declare const getAudioInfoFromElement: (audio: HTMLAudioElement, channelNumber?: number, audioChannels?: ExtendedAudioQueueChannel[]) => AudioInfo | null;
28
34
  /**
29
35
  * Creates a complete snapshot of a queue's current state
30
36
  * @param channelNumber - The channel number to create a snapshot for
package/dist/utils.js CHANGED
@@ -33,28 +33,44 @@ exports.extractFileName = extractFileName;
33
33
  /**
34
34
  * Extracts comprehensive audio information from an HTMLAudioElement
35
35
  * @param audio - The HTML audio element to extract information from
36
+ * @param channelNumber - Optional channel number to include remaining queue info
37
+ * @param audioChannels - Optional audio channels array to calculate remainingInQueue
36
38
  * @returns AudioInfo object with current playback state or null if audio is invalid
37
39
  * @example
38
40
  * ```typescript
39
41
  * const audioElement = new Audio('song.mp3');
40
42
  * const info = getAudioInfoFromElement(audioElement);
41
43
  * console.log(info?.progress); // Current progress as decimal (0-1)
44
+ *
45
+ * // With channel context for remainingInQueue
46
+ * const infoWithQueue = getAudioInfoFromElement(audioElement, 0, audioChannels);
47
+ * console.log(infoWithQueue?.remainingInQueue); // Number of items left in queue
42
48
  * ```
43
49
  */
44
- const getAudioInfoFromElement = (audio) => {
50
+ const getAudioInfoFromElement = (audio, channelNumber, audioChannels) => {
45
51
  if (!audio)
46
52
  return null;
47
53
  const duration = isNaN(audio.duration) ? 0 : audio.duration * 1000; // Convert to milliseconds
48
54
  const currentTime = isNaN(audio.currentTime) ? 0 : audio.currentTime * 1000; // Convert to milliseconds
49
55
  const progress = duration > 0 ? Math.min(currentTime / duration, 1) : 0;
50
56
  const isPlaying = !audio.paused && !audio.ended && audio.readyState > 2;
57
+ // Calculate remainingInQueue if channel context is provided
58
+ let remainingInQueue = 0;
59
+ if (channelNumber !== undefined && audioChannels && audioChannels[channelNumber]) {
60
+ const channel = audioChannels[channelNumber];
61
+ remainingInQueue = Math.max(0, channel.queue.length - 1); // Exclude current playing audio
62
+ }
51
63
  return {
52
64
  currentTime,
53
65
  duration,
54
66
  fileName: (0, exports.extractFileName)(audio.src),
67
+ isLooping: audio.loop,
68
+ isPaused: audio.paused && !audio.ended,
55
69
  isPlaying,
56
70
  progress,
57
- src: audio.src
71
+ remainingInQueue,
72
+ src: audio.src,
73
+ volume: audio.volume
58
74
  };
59
75
  };
60
76
  exports.getAudioInfoFromElement = getAudioInfoFromElement;
@@ -77,13 +93,17 @@ const createQueueSnapshot = (channelNumber, audioChannels) => {
77
93
  duration: isNaN(audio.duration) ? 0 : audio.duration * 1000,
78
94
  fileName: (0, exports.extractFileName)(audio.src),
79
95
  isCurrentlyPlaying: index === 0 && !audio.paused && !audio.ended,
80
- src: audio.src
96
+ isLooping: audio.loop,
97
+ src: audio.src,
98
+ volume: audio.volume
81
99
  }));
82
100
  return {
83
101
  channelNumber,
84
102
  currentIndex: 0, // Current playing is always index 0 in our queue structure
103
+ isPaused: channel.isPaused || false,
85
104
  items,
86
- totalItems: channel.queue.length
105
+ totalItems: channel.queue.length,
106
+ volume: channel.volume || 1.0
87
107
  };
88
108
  };
89
109
  exports.createQueueSnapshot = createQueueSnapshot;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @fileoverview Volume management functions for the audio-channel-queue package
3
+ */
4
+ import { VolumeConfig } from './types';
5
+ /**
6
+ * Smoothly transitions volume for a specific channel over time
7
+ * @param channelNumber - The channel number to transition
8
+ * @param targetVolume - Target volume level (0-1)
9
+ * @param duration - Transition duration in milliseconds
10
+ * @param easing - Easing function type
11
+ * @returns Promise that resolves when transition completes
12
+ * @example
13
+ * ```typescript
14
+ * await transitionVolume(0, 0.2, 500, 'ease-out'); // Duck to 20% over 500ms
15
+ * ```
16
+ */
17
+ export declare const transitionVolume: (channelNumber: number, targetVolume: number, duration?: number, easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out") => Promise<void>;
18
+ /**
19
+ * Sets the volume for a specific channel with optional smooth transition
20
+ * @param channelNumber - The channel number to set volume for
21
+ * @param volume - Volume level (0-1)
22
+ * @param transitionDuration - Optional transition duration in milliseconds
23
+ * @param easing - Optional easing function
24
+ * @example
25
+ * ```typescript
26
+ * setChannelVolume(0, 0.5); // Set channel 0 to 50%
27
+ * setChannelVolume(0, 0.5, 300, 'ease-out'); // Smooth transition over 300ms
28
+ * ```
29
+ */
30
+ export declare const setChannelVolume: (channelNumber: number, volume: number, transitionDuration?: number, easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out") => Promise<void>;
31
+ /**
32
+ * Gets the current volume for a specific channel
33
+ * @param channelNumber - The channel number to get volume for (defaults to 0)
34
+ * @returns Current volume level (0-1) or 1.0 if channel doesn't exist
35
+ * @example
36
+ * ```typescript
37
+ * const volume = getChannelVolume(0);
38
+ * const defaultChannelVolume = getChannelVolume(); // Gets channel 0
39
+ * console.log(`Channel 0 volume: ${volume * 100}%`);
40
+ * ```
41
+ */
42
+ export declare const getChannelVolume: (channelNumber?: number) => number;
43
+ /**
44
+ * Gets the volume levels for all channels
45
+ * @returns Array of volume levels (0-1) for each channel
46
+ * @example
47
+ * ```typescript
48
+ * const volumes = getAllChannelsVolume();
49
+ * volumes.forEach((volume, index) => {
50
+ * console.log(`Channel ${index}: ${volume * 100}%`);
51
+ * });
52
+ * ```
53
+ */
54
+ export declare const getAllChannelsVolume: () => number[];
55
+ /**
56
+ * Sets volume for all channels to the same level
57
+ * @param volume - Volume level (0-1) to apply to all channels
58
+ * @example
59
+ * ```typescript
60
+ * await setAllChannelsVolume(0.6); // Set all channels to 60% volume
61
+ * ```
62
+ */
63
+ export declare const setAllChannelsVolume: (volume: number) => Promise<void>;
64
+ /**
65
+ * Configures volume ducking for channels. When the priority channel plays audio,
66
+ * all other channels will be automatically reduced to the ducking volume level
67
+ * @param config - Volume ducking configuration
68
+ * @example
69
+ * ```typescript
70
+ * // When channel 1 plays, reduce all other channels to 20% volume
71
+ * setVolumeDucking({
72
+ * priorityChannel: 1,
73
+ * priorityVolume: 1.0,
74
+ * duckingVolume: 0.2
75
+ * });
76
+ * ```
77
+ */
78
+ export declare const setVolumeDucking: (config: VolumeConfig) => void;
79
+ /**
80
+ * Removes volume ducking configuration from all channels
81
+ * @example
82
+ * ```typescript
83
+ * clearVolumeDucking(); // Remove all volume ducking effects
84
+ * ```
85
+ */
86
+ export declare const clearVolumeDucking: () => void;
87
+ /**
88
+ * Applies volume ducking effects based on current playback state with smooth transitions
89
+ * @param activeChannelNumber - The channel that just started playing
90
+ * @internal
91
+ */
92
+ export declare const applyVolumeDucking: (activeChannelNumber: number) => Promise<void>;
93
+ /**
94
+ * Restores normal volume levels when priority channel stops with smooth transitions
95
+ * @param stoppedChannelNumber - The channel that just stopped playing
96
+ * @internal
97
+ */
98
+ export declare const restoreVolumeLevels: (stoppedChannelNumber: number) => Promise<void>;
package/dist/volume.js ADDED
@@ -0,0 +1,305 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Volume management functions for the audio-channel-queue package
4
+ */
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
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;
16
+ const info_1 = require("./info");
17
+ // Store active volume transitions to handle interruptions
18
+ const activeTransitions = new Map();
19
+ /**
20
+ * Easing functions for smooth volume transitions
21
+ */
22
+ 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
27
+ };
28
+ /**
29
+ * Smoothly transitions volume for a specific channel over time
30
+ * @param channelNumber - The channel number to transition
31
+ * @param targetVolume - Target volume level (0-1)
32
+ * @param duration - Transition duration in milliseconds
33
+ * @param easing - Easing function type
34
+ * @returns Promise that resolves when transition completes
35
+ * @example
36
+ * ```typescript
37
+ * await transitionVolume(0, 0.2, 500, 'ease-out'); // Duck to 20% over 500ms
38
+ * ```
39
+ */
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') {
41
+ const channel = info_1.audioChannels[channelNumber];
42
+ if (!channel || channel.queue.length === 0)
43
+ return;
44
+ const currentAudio = channel.queue[0];
45
+ const startVolume = currentAudio.volume;
46
+ const volumeDelta = targetVolume - startVolume;
47
+ // Cancel any existing transition for this channel
48
+ if (activeTransitions.has(channelNumber)) {
49
+ clearTimeout(activeTransitions.get(channelNumber));
50
+ activeTransitions.delete(channelNumber);
51
+ }
52
+ // If no change needed, resolve immediately
53
+ if (Math.abs(volumeDelta) < 0.001) {
54
+ channel.volume = targetVolume;
55
+ return Promise.resolve();
56
+ }
57
+ // Handle zero duration - instant change
58
+ if (duration === 0) {
59
+ channel.volume = targetVolume;
60
+ if (channel.queue.length > 0) {
61
+ channel.queue[0].volume = targetVolume;
62
+ }
63
+ return Promise.resolve();
64
+ }
65
+ const startTime = performance.now();
66
+ const easingFn = easingFunctions[easing];
67
+ return new Promise((resolve) => {
68
+ const updateVolume = () => {
69
+ const elapsed = performance.now() - startTime;
70
+ const progress = Math.min(elapsed / duration, 1);
71
+ const easedProgress = easingFn(progress);
72
+ const currentVolume = startVolume + (volumeDelta * easedProgress);
73
+ const clampedVolume = Math.max(0, Math.min(1, currentVolume));
74
+ // Apply volume to both channel config and current audio
75
+ channel.volume = clampedVolume;
76
+ if (channel.queue.length > 0) {
77
+ channel.queue[0].volume = clampedVolume;
78
+ }
79
+ if (progress >= 1) {
80
+ // Transition complete
81
+ activeTransitions.delete(channelNumber);
82
+ resolve();
83
+ }
84
+ else {
85
+ // Use requestAnimationFrame in browser, setTimeout in tests
86
+ if (typeof requestAnimationFrame !== 'undefined') {
87
+ const rafId = requestAnimationFrame(updateVolume);
88
+ activeTransitions.set(channelNumber, rafId);
89
+ }
90
+ else {
91
+ // In test environment, use shorter intervals
92
+ const timeoutId = setTimeout(updateVolume, 1);
93
+ activeTransitions.set(channelNumber, timeoutId);
94
+ }
95
+ }
96
+ };
97
+ updateVolume();
98
+ });
99
+ });
100
+ exports.transitionVolume = transitionVolume;
101
+ /**
102
+ * Sets the volume for a specific channel with optional smooth transition
103
+ * @param channelNumber - The channel number to set volume for
104
+ * @param volume - Volume level (0-1)
105
+ * @param transitionDuration - Optional transition duration in milliseconds
106
+ * @param easing - Optional easing function
107
+ * @example
108
+ * ```typescript
109
+ * setChannelVolume(0, 0.5); // Set channel 0 to 50%
110
+ * setChannelVolume(0, 0.5, 300, 'ease-out'); // Smooth transition over 300ms
111
+ * ```
112
+ */
113
+ const setChannelVolume = (channelNumber, volume, transitionDuration, easing) => __awaiter(void 0, void 0, void 0, function* () {
114
+ const clampedVolume = Math.max(0, Math.min(1, volume));
115
+ if (!info_1.audioChannels[channelNumber]) {
116
+ info_1.audioChannels[channelNumber] = {
117
+ audioCompleteCallbacks: new Set(),
118
+ audioErrorCallbacks: new Set(),
119
+ audioPauseCallbacks: new Set(),
120
+ audioResumeCallbacks: new Set(),
121
+ audioStartCallbacks: new Set(),
122
+ isPaused: false,
123
+ progressCallbacks: new Map(),
124
+ queue: [],
125
+ queueChangeCallbacks: new Set(),
126
+ volume: clampedVolume
127
+ };
128
+ return;
129
+ }
130
+ if (transitionDuration && transitionDuration > 0) {
131
+ // Smooth transition
132
+ yield (0, exports.transitionVolume)(channelNumber, clampedVolume, transitionDuration, easing);
133
+ }
134
+ else {
135
+ // Instant change (backward compatibility)
136
+ info_1.audioChannels[channelNumber].volume = clampedVolume;
137
+ const channel = info_1.audioChannels[channelNumber];
138
+ if (channel.queue.length > 0) {
139
+ const currentAudio = channel.queue[0];
140
+ currentAudio.volume = clampedVolume;
141
+ }
142
+ }
143
+ });
144
+ exports.setChannelVolume = setChannelVolume;
145
+ /**
146
+ * Gets the current volume for a specific channel
147
+ * @param channelNumber - The channel number to get volume for (defaults to 0)
148
+ * @returns Current volume level (0-1) or 1.0 if channel doesn't exist
149
+ * @example
150
+ * ```typescript
151
+ * const volume = getChannelVolume(0);
152
+ * const defaultChannelVolume = getChannelVolume(); // Gets channel 0
153
+ * console.log(`Channel 0 volume: ${volume * 100}%`);
154
+ * ```
155
+ */
156
+ const getChannelVolume = (channelNumber = 0) => {
157
+ const channel = info_1.audioChannels[channelNumber];
158
+ return (channel === null || channel === void 0 ? void 0 : channel.volume) || 1.0;
159
+ };
160
+ exports.getChannelVolume = getChannelVolume;
161
+ /**
162
+ * Gets the volume levels for all channels
163
+ * @returns Array of volume levels (0-1) for each channel
164
+ * @example
165
+ * ```typescript
166
+ * const volumes = getAllChannelsVolume();
167
+ * volumes.forEach((volume, index) => {
168
+ * console.log(`Channel ${index}: ${volume * 100}%`);
169
+ * });
170
+ * ```
171
+ */
172
+ const getAllChannelsVolume = () => {
173
+ return info_1.audioChannels.map((channel) => (channel === null || channel === void 0 ? void 0 : channel.volume) || 1.0);
174
+ };
175
+ exports.getAllChannelsVolume = getAllChannelsVolume;
176
+ /**
177
+ * Sets volume for all channels to the same level
178
+ * @param volume - Volume level (0-1) to apply to all channels
179
+ * @example
180
+ * ```typescript
181
+ * await setAllChannelsVolume(0.6); // Set all channels to 60% volume
182
+ * ```
183
+ */
184
+ const setAllChannelsVolume = (volume) => __awaiter(void 0, void 0, void 0, function* () {
185
+ const promises = [];
186
+ info_1.audioChannels.forEach((_channel, index) => {
187
+ promises.push((0, exports.setChannelVolume)(index, volume));
188
+ });
189
+ yield Promise.all(promises);
190
+ });
191
+ exports.setAllChannelsVolume = setAllChannelsVolume;
192
+ /**
193
+ * Configures volume ducking for channels. When the priority channel plays audio,
194
+ * all other channels will be automatically reduced to the ducking volume level
195
+ * @param config - Volume ducking configuration
196
+ * @example
197
+ * ```typescript
198
+ * // When channel 1 plays, reduce all other channels to 20% volume
199
+ * setVolumeDucking({
200
+ * priorityChannel: 1,
201
+ * priorityVolume: 1.0,
202
+ * duckingVolume: 0.2
203
+ * });
204
+ * ```
205
+ */
206
+ const setVolumeDucking = (config) => {
207
+ // First, ensure we have enough channels for the priority channel
208
+ while (info_1.audioChannels.length <= config.priorityChannel) {
209
+ info_1.audioChannels.push({
210
+ audioCompleteCallbacks: new Set(),
211
+ audioErrorCallbacks: new Set(),
212
+ audioPauseCallbacks: new Set(),
213
+ audioResumeCallbacks: new Set(),
214
+ audioStartCallbacks: new Set(),
215
+ isPaused: false,
216
+ progressCallbacks: new Map(),
217
+ queue: [],
218
+ queueChangeCallbacks: new Set(),
219
+ volume: 1.0
220
+ });
221
+ }
222
+ // Apply the config to all existing channels
223
+ info_1.audioChannels.forEach((channel, index) => {
224
+ if (!info_1.audioChannels[index]) {
225
+ info_1.audioChannels[index] = {
226
+ audioCompleteCallbacks: new Set(),
227
+ audioErrorCallbacks: new Set(),
228
+ audioPauseCallbacks: new Set(),
229
+ audioResumeCallbacks: new Set(),
230
+ audioStartCallbacks: new Set(),
231
+ isPaused: false,
232
+ progressCallbacks: new Map(),
233
+ queue: [],
234
+ queueChangeCallbacks: new Set(),
235
+ volume: 1.0
236
+ };
237
+ }
238
+ info_1.audioChannels[index].volumeConfig = config;
239
+ });
240
+ };
241
+ exports.setVolumeDucking = setVolumeDucking;
242
+ /**
243
+ * Removes volume ducking configuration from all channels
244
+ * @example
245
+ * ```typescript
246
+ * clearVolumeDucking(); // Remove all volume ducking effects
247
+ * ```
248
+ */
249
+ const clearVolumeDucking = () => {
250
+ info_1.audioChannels.forEach((channel) => {
251
+ if (channel) {
252
+ delete channel.volumeConfig;
253
+ }
254
+ });
255
+ };
256
+ exports.clearVolumeDucking = clearVolumeDucking;
257
+ /**
258
+ * Applies volume ducking effects based on current playback state with smooth transitions
259
+ * @param activeChannelNumber - The channel that just started playing
260
+ * @internal
261
+ */
262
+ const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
263
+ const transitionPromises = [];
264
+ info_1.audioChannels.forEach((channel, channelNumber) => {
265
+ if (channel === null || channel === void 0 ? void 0 : channel.volumeConfig) {
266
+ const config = channel.volumeConfig;
267
+ if (activeChannelNumber === config.priorityChannel) {
268
+ const duration = config.duckTransitionDuration || 250;
269
+ const easing = config.transitionEasing || 'ease-out';
270
+ // Priority channel is active, duck other channels
271
+ if (channelNumber === config.priorityChannel) {
272
+ transitionPromises.push((0, exports.transitionVolume)(channelNumber, config.priorityVolume, duration, easing));
273
+ }
274
+ else {
275
+ transitionPromises.push((0, exports.transitionVolume)(channelNumber, config.duckingVolume, duration, easing));
276
+ }
277
+ }
278
+ }
279
+ });
280
+ // Wait for all transitions to complete
281
+ yield Promise.all(transitionPromises);
282
+ });
283
+ exports.applyVolumeDucking = applyVolumeDucking;
284
+ /**
285
+ * Restores normal volume levels when priority channel stops with smooth transitions
286
+ * @param stoppedChannelNumber - The channel that just stopped playing
287
+ * @internal
288
+ */
289
+ const restoreVolumeLevels = (stoppedChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
290
+ const transitionPromises = [];
291
+ info_1.audioChannels.forEach((channel, channelNumber) => {
292
+ if (channel === null || channel === void 0 ? void 0 : channel.volumeConfig) {
293
+ const config = channel.volumeConfig;
294
+ if (stoppedChannelNumber === config.priorityChannel) {
295
+ const duration = config.restoreTransitionDuration || 500;
296
+ const easing = config.transitionEasing || 'ease-out';
297
+ // Priority channel stopped, restore normal volumes
298
+ transitionPromises.push((0, exports.transitionVolume)(channelNumber, channel.volume || 1.0, duration, easing));
299
+ }
300
+ }
301
+ });
302
+ // Wait for all transitions to complete
303
+ yield Promise.all(transitionPromises);
304
+ });
305
+ exports.restoreVolumeLevels = restoreVolumeLevels;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "audio-channel-queue",
3
- "version": "1.5.0",
3
+ "version": "1.7.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",
@@ -31,6 +31,9 @@
31
31
  "url": "https://github.com/tonycarpenter21/audio-queue-package/issues"
32
32
  },
33
33
  "homepage": "https://github.com/tonycarpenter21/audio-queue-package#readme",
34
+ "engines": {
35
+ "node": ">=14.0.0"
36
+ },
34
37
  "devDependencies": {
35
38
  "@types/jest": "^29.5.13",
36
39
  "jest": "^29.7.0",