audio-channel-queue 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/info.ts CHANGED
@@ -17,7 +17,8 @@ import { getAudioInfoFromElement, createQueueSnapshot } from './utils';
17
17
  import { setupProgressTracking, cleanupProgressTracking } from './events';
18
18
 
19
19
  /**
20
- * Global array of extended audio queue channels
20
+ * Global array to store audio channels with their queues and callback management
21
+ * Each channel maintains its own audio queue and event callback sets
21
22
  */
22
23
  export const audioChannels: ExtendedAudioQueueChannel[] = [];
23
24
 
@@ -100,6 +101,7 @@ export const onAudioProgress = (channelNumber: number, callback: ProgressCallbac
100
101
  if (!audioChannels[channelNumber]) {
101
102
  audioChannels[channelNumber] = {
102
103
  audioCompleteCallbacks: new Set(),
104
+ audioErrorCallbacks: new Set(),
103
105
  audioPauseCallbacks: new Set(),
104
106
  audioResumeCallbacks: new Set(),
105
107
  audioStartCallbacks: new Set(),
@@ -173,6 +175,7 @@ export const onQueueChange = (channelNumber: number, callback: QueueChangeCallba
173
175
  if (!audioChannels[channelNumber]) {
174
176
  audioChannels[channelNumber] = {
175
177
  audioCompleteCallbacks: new Set(),
178
+ audioErrorCallbacks: new Set(),
176
179
  audioPauseCallbacks: new Set(),
177
180
  audioResumeCallbacks: new Set(),
178
181
  audioStartCallbacks: new Set(),
@@ -223,6 +226,7 @@ export const onAudioStart = (channelNumber: number, callback: AudioStartCallback
223
226
  if (!audioChannels[channelNumber]) {
224
227
  audioChannels[channelNumber] = {
225
228
  audioCompleteCallbacks: new Set(),
229
+ audioErrorCallbacks: new Set(),
226
230
  audioPauseCallbacks: new Set(),
227
231
  audioResumeCallbacks: new Set(),
228
232
  audioStartCallbacks: new Set(),
@@ -260,6 +264,7 @@ export const onAudioComplete = (channelNumber: number, callback: AudioCompleteCa
260
264
  if (!audioChannels[channelNumber]) {
261
265
  audioChannels[channelNumber] = {
262
266
  audioCompleteCallbacks: new Set(),
267
+ audioErrorCallbacks: new Set(),
263
268
  audioPauseCallbacks: new Set(),
264
269
  audioResumeCallbacks: new Set(),
265
270
  audioStartCallbacks: new Set(),
@@ -295,6 +300,7 @@ export const onAudioPause = (channelNumber: number, callback: AudioPauseCallback
295
300
  if (!audioChannels[channelNumber]) {
296
301
  audioChannels[channelNumber] = {
297
302
  audioCompleteCallbacks: new Set(),
303
+ audioErrorCallbacks: new Set(),
298
304
  audioPauseCallbacks: new Set(),
299
305
  audioResumeCallbacks: new Set(),
300
306
  audioStartCallbacks: new Set(),
@@ -330,6 +336,7 @@ export const onAudioResume = (channelNumber: number, callback: AudioResumeCallba
330
336
  if (!audioChannels[channelNumber]) {
331
337
  audioChannels[channelNumber] = {
332
338
  audioCompleteCallbacks: new Set(),
339
+ audioErrorCallbacks: new Set(),
333
340
  audioPauseCallbacks: new Set(),
334
341
  audioResumeCallbacks: new Set(),
335
342
  audioStartCallbacks: new Set(),
package/src/pause.ts CHANGED
@@ -1,190 +1,412 @@
1
- /**
2
- * @fileoverview Pause and resume management functions for the audio-channel-queue package
3
- */
4
-
5
- import { ExtendedAudioQueueChannel, AudioInfo } from './types';
6
- import { audioChannels } from './info';
7
- import { getAudioInfoFromElement } from './utils';
8
- import { emitAudioPause, emitAudioResume } from './events';
9
-
10
- /**
11
- * Pauses the currently playing audio in a specific channel
12
- * @param channelNumber - The channel number to pause (defaults to 0)
13
- * @returns Promise that resolves when the audio is paused
14
- * @example
15
- * ```typescript
16
- * await pauseChannel(0); // Pause audio in channel 0
17
- * await pauseChannel(); // Pause audio in default channel
18
- * ```
19
- */
20
- export const pauseChannel = async (channelNumber: number = 0): Promise<void> => {
21
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
22
-
23
- if (channel && channel.queue.length > 0) {
24
- const currentAudio: HTMLAudioElement = channel.queue[0];
25
-
26
- if (!currentAudio.paused && !currentAudio.ended) {
27
- currentAudio.pause();
28
- channel.isPaused = true;
29
-
30
- const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
31
- if (audioInfo) {
32
- emitAudioPause(channelNumber, audioInfo, audioChannels);
33
- }
34
- }
35
- }
36
- };
37
-
38
- /**
39
- * Resumes the currently paused audio in a specific channel
40
- * @param channelNumber - The channel number to resume (defaults to 0)
41
- * @returns Promise that resolves when the audio starts playing
42
- * @example
43
- * ```typescript
44
- * await resumeChannel(0); // Resume audio in channel 0
45
- * await resumeChannel(); // Resume audio in default channel
46
- * ```
47
- */
48
- export const resumeChannel = async (channelNumber: number = 0): Promise<void> => {
49
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
50
-
51
- if (channel && channel.queue.length > 0) {
52
- const currentAudio: HTMLAudioElement = channel.queue[0];
53
-
54
- // Only resume if both the channel is marked as paused AND the audio element is actually paused
55
- if (channel.isPaused && currentAudio.paused && !currentAudio.ended) {
56
- await currentAudio.play();
57
- channel.isPaused = false;
58
-
59
- const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
60
- if (audioInfo) {
61
- emitAudioResume(channelNumber, audioInfo, audioChannels);
62
- }
63
- }
64
- }
65
- };
66
-
67
- /**
68
- * Toggles pause/resume state for a specific channel
69
- * @param channelNumber - The channel number to toggle (defaults to 0)
70
- * @returns Promise that resolves when the toggle is complete
71
- * @example
72
- * ```typescript
73
- * await togglePauseChannel(0); // Toggle pause state for channel 0
74
- * ```
75
- */
76
- export const togglePauseChannel = async (channelNumber: number = 0): Promise<void> => {
77
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
78
-
79
- if (channel && channel.queue.length > 0) {
80
- const currentAudio: HTMLAudioElement = channel.queue[0];
81
-
82
- if (currentAudio.paused) {
83
- await resumeChannel(channelNumber);
84
- } else {
85
- await pauseChannel(channelNumber);
86
- }
87
- }
88
- };
89
-
90
- /**
91
- * Pauses all currently playing audio across all channels
92
- * @returns Promise that resolves when all audio is paused
93
- * @example
94
- * ```typescript
95
- * await pauseAllChannels(); // Pause everything
96
- * ```
97
- */
98
- export const pauseAllChannels = async (): Promise<void> => {
99
- const pausePromises: Promise<void>[] = [];
100
-
101
- audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
102
- pausePromises.push(pauseChannel(index));
103
- });
104
-
105
- await Promise.all(pausePromises);
106
- };
107
-
108
- /**
109
- * Resumes all currently paused audio across all channels
110
- * @returns Promise that resolves when all audio is resumed
111
- * @example
112
- * ```typescript
113
- * await resumeAllChannels(); // Resume everything that was paused
114
- * ```
115
- */
116
- export const resumeAllChannels = async (): Promise<void> => {
117
- const resumePromises: Promise<void>[] = [];
118
-
119
- audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
120
- resumePromises.push(resumeChannel(index));
121
- });
122
-
123
- await Promise.all(resumePromises);
124
- };
125
-
126
- /**
127
- * Checks if a specific channel is currently paused
128
- * @param channelNumber - The channel number to check (defaults to 0)
129
- * @returns True if the channel is paused, false otherwise
130
- * @example
131
- * ```typescript
132
- * const isPaused = isChannelPaused(0);
133
- * console.log(`Channel 0 is ${isPaused ? 'paused' : 'playing'}`);
134
- * ```
135
- */
136
- export const isChannelPaused = (channelNumber: number = 0): boolean => {
137
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
138
- return channel?.isPaused || false;
139
- };
140
-
141
- /**
142
- * Gets the pause state of all channels
143
- * @returns Array of boolean values indicating pause state for each channel
144
- * @example
145
- * ```typescript
146
- * const pauseStates = getAllChannelsPauseState();
147
- * pauseStates.forEach((isPaused, index) => {
148
- * console.log(`Channel ${index}: ${isPaused ? 'paused' : 'playing'}`);
149
- * });
150
- * ```
151
- */
152
- export const getAllChannelsPauseState = (): boolean[] => {
153
- return audioChannels.map((channel: ExtendedAudioQueueChannel) =>
154
- channel?.isPaused || false
155
- );
156
- };
157
-
158
- /**
159
- * Toggles pause/resume state for all channels globally
160
- * If any channels are currently playing, all channels will be paused
161
- * If all channels are paused, all channels will be resumed
162
- * @returns Promise that resolves when the toggle is complete
163
- * @example
164
- * ```typescript
165
- * await togglePauseAllChannels(); // Pause all if any are playing, resume all if all are paused
166
- * ```
167
- */
168
- export const togglePauseAllChannels = async (): Promise<void> => {
169
- let hasPlayingChannel: boolean = false;
170
-
171
- // Check if any channel is currently playing
172
- for (let i = 0; i < audioChannels.length; i++) {
173
- const channel: ExtendedAudioQueueChannel = audioChannels[i];
174
- if (channel && channel.queue.length > 0) {
175
- const currentAudio: HTMLAudioElement = channel.queue[0];
176
- if (!currentAudio.paused && !currentAudio.ended) {
177
- hasPlayingChannel = true;
178
- break;
179
- }
180
- }
181
- }
182
-
183
- // If any channel is playing, pause all channels
184
- // If no channels are playing, resume all channels
185
- if (hasPlayingChannel) {
186
- await pauseAllChannels();
187
- } else {
188
- await resumeAllChannels();
189
- }
1
+ /**
2
+ * @fileoverview Pause and resume management functions for the audio-channel-queue package
3
+ */
4
+
5
+ import { ExtendedAudioQueueChannel, AudioInfo, FadeType, FadeConfig, ChannelFadeState, EasingType } from './types';
6
+ import { audioChannels } from './info';
7
+ import { getAudioInfoFromElement } from './utils';
8
+ import { emitAudioPause, emitAudioResume } from './events';
9
+ import { transitionVolume } from './volume';
10
+
11
+ /**
12
+ * Predefined fade configurations for different transition types
13
+ */
14
+ const FADE_CONFIGS: Record<FadeType, FadeConfig> = {
15
+ [FadeType.Linear]: { duration: 800, pauseCurve: EasingType.Linear, resumeCurve: EasingType.Linear },
16
+ [FadeType.Gentle]: { duration: 800, pauseCurve: EasingType.EaseOut, resumeCurve: EasingType.EaseIn },
17
+ [FadeType.Dramatic]: { duration: 800, pauseCurve: EasingType.EaseIn, resumeCurve: EasingType.EaseOut }
18
+ };
19
+
20
+ /**
21
+ * Gets the current volume for a channel, accounting for synchronous state
22
+ * @param channelNumber - The channel number
23
+ * @returns Current volume level (0-1)
24
+ */
25
+ const getChannelVolumeSync = (channelNumber: number): number => {
26
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
27
+ return channel?.volume || 1.0;
28
+ };
29
+
30
+ /**
31
+ * Sets the channel volume synchronously in internal state
32
+ * @param channelNumber - The channel number
33
+ * @param volume - Volume level (0-1)
34
+ */
35
+ const setChannelVolumeSync = (channelNumber: number, volume: number): void => {
36
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
37
+ if (channel) {
38
+ channel.volume = volume;
39
+ if (channel.queue.length > 0) {
40
+ channel.queue[0].volume = volume;
41
+ }
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
+ * @returns Promise that resolves when the pause and fade are complete
50
+ * @example
51
+ * ```typescript
52
+ * await pauseWithFade(FadeType.Gentle, 0); // Pause with gentle fade out over 800ms
53
+ * await pauseWithFade(FadeType.Dramatic, 1); // Pause with dramatic fade out over 800ms
54
+ * await pauseWithFade(FadeType.Linear, 2); // Linear pause with 800ms fade
55
+ * ```
56
+ */
57
+ export const pauseWithFade = async (fadeType: FadeType = FadeType.Gentle, channelNumber: number = 0): Promise<void> => {
58
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
59
+
60
+ if (!channel || channel.queue.length === 0) return;
61
+
62
+ const currentAudio: HTMLAudioElement = channel.queue[0];
63
+
64
+ // Don't pause if already paused or ended
65
+ if (currentAudio.paused || currentAudio.ended) return;
66
+
67
+ const config: FadeConfig = FADE_CONFIGS[fadeType];
68
+ const originalVolume: number = getChannelVolumeSync(channelNumber);
69
+
70
+ // Store fade state for resumeWithFade to use
71
+ channel.fadeState = {
72
+ originalVolume,
73
+ fadeType,
74
+ isPaused: true
75
+ };
76
+
77
+ if (config.duration === 0) {
78
+ // Instant pause
79
+ await pauseChannel(channelNumber);
80
+ return;
81
+ }
82
+
83
+ // Fade to 0 with pause curve, then pause
84
+ await transitionVolume(channelNumber, 0, config.duration, config.pauseCurve);
85
+ await pauseChannel(channelNumber);
86
+
87
+ // Reset volume to original for resume (synchronously to avoid state issues)
88
+ setChannelVolumeSync(channelNumber, originalVolume);
89
+ };
90
+
91
+ /**
92
+ * Resumes the currently paused audio in a specific channel with smooth volume fade
93
+ * Uses the complementary fade curve automatically based on the pause fade type, or allows override
94
+ * @param fadeType - Optional fade type to override the stored fade type from pause
95
+ * @param channelNumber - The channel number to resume (defaults to 0)
96
+ * @returns Promise that resolves when the resume and fade are complete
97
+ * @example
98
+ * ```typescript
99
+ * await resumeWithFade(); // Resume with automatically paired fade curve from pause
100
+ * await resumeWithFade(FadeType.Dramatic, 0); // Override with dramatic fade
101
+ * await resumeWithFade(FadeType.Linear); // Override with linear fade on default channel
102
+ * ```
103
+ */
104
+ export const resumeWithFade = async (fadeType?: FadeType, channelNumber: number = 0): Promise<void> => {
105
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
106
+
107
+ if (!channel || channel.queue.length === 0) return;
108
+
109
+ const fadeState: ChannelFadeState | undefined = channel.fadeState;
110
+ if (!fadeState || !fadeState.isPaused) {
111
+ // Fall back to regular resume if no fade state
112
+ await resumeChannel(channelNumber);
113
+ return;
114
+ }
115
+
116
+ // Use provided fadeType or fall back to stored fadeType from pause
117
+ const effectiveFadeType: FadeType = fadeType || fadeState.fadeType;
118
+ const config: FadeConfig = FADE_CONFIGS[effectiveFadeType];
119
+
120
+ if (config.duration === 0) {
121
+ // Instant resume
122
+ await resumeChannel(channelNumber);
123
+ fadeState.isPaused = false;
124
+ return;
125
+ }
126
+
127
+ // Set volume to 0, resume, then fade to original with resume curve
128
+ setChannelVolumeSync(channelNumber, 0);
129
+ await resumeChannel(channelNumber);
130
+ await transitionVolume(channelNumber, fadeState.originalVolume, config.duration, config.resumeCurve);
131
+
132
+ fadeState.isPaused = false;
133
+ };
134
+
135
+ /**
136
+ * Toggles pause/resume state for a specific channel with integrated fade
137
+ * @param fadeType - Type of fade transition to apply when pausing
138
+ * @param channelNumber - The channel number to toggle (defaults to 0)
139
+ * @returns Promise that resolves when the toggle and fade are complete
140
+ * @example
141
+ * ```typescript
142
+ * await togglePauseWithFade(FadeType.Gentle, 0); // Toggle with gentle fade
143
+ * ```
144
+ */
145
+ export const togglePauseWithFade = async (fadeType: FadeType = FadeType.Gentle, channelNumber: number = 0): Promise<void> => {
146
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
147
+
148
+ if (!channel || channel.queue.length === 0) return;
149
+
150
+ const currentAudio: HTMLAudioElement = channel.queue[0];
151
+
152
+ if (currentAudio.paused) {
153
+ await resumeWithFade(undefined, channelNumber);
154
+ } else {
155
+ await pauseWithFade(fadeType, channelNumber);
156
+ }
157
+ };
158
+
159
+ /**
160
+ * Pauses all currently playing audio across all channels with smooth volume fade
161
+ * @param fadeType - Type of fade transition to apply to all channels
162
+ * @returns Promise that resolves when all channels are paused and faded
163
+ * @example
164
+ * ```typescript
165
+ * await pauseAllWithFade('dramatic'); // Pause everything with dramatic fade
166
+ * ```
167
+ */
168
+ export const pauseAllWithFade = async (fadeType: FadeType = FadeType.Gentle): Promise<void> => {
169
+ const pausePromises: Promise<void>[] = [];
170
+
171
+ audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
172
+ pausePromises.push(pauseWithFade(fadeType, index));
173
+ });
174
+
175
+ await Promise.all(pausePromises);
176
+ };
177
+
178
+ /**
179
+ * Resumes all currently paused audio across all channels with smooth volume fade
180
+ * Uses automatically paired fade curves based on each channel's pause fade type
181
+ * @returns Promise that resolves when all channels are resumed and faded
182
+ * @example
183
+ * ```typescript
184
+ * await resumeAllWithFade(); // Resume everything with paired fade curves
185
+ * ```
186
+ */
187
+ export const resumeAllWithFade = async (): Promise<void> => {
188
+ const resumePromises: Promise<void>[] = [];
189
+
190
+ audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
191
+ resumePromises.push(resumeWithFade(undefined, index));
192
+ });
193
+
194
+ await Promise.all(resumePromises);
195
+ };
196
+
197
+ /**
198
+ * Toggles pause/resume state for all channels with integrated fade
199
+ * If any channels are playing, all will be paused with fade
200
+ * If all channels are paused, all will be resumed with fade
201
+ * @param fadeType - Type of fade transition to apply when pausing
202
+ * @returns Promise that resolves when all toggles and fades are complete
203
+ * @example
204
+ * ```typescript
205
+ * await togglePauseAllWithFade('gentle'); // Global toggle with gentle fade
206
+ * ```
207
+ */
208
+ export const togglePauseAllWithFade = async (fadeType: FadeType = FadeType.Gentle): Promise<void> => {
209
+ let hasPlayingChannel: boolean = false;
210
+
211
+ // Check if any channel is currently playing
212
+ for (let i = 0; i < audioChannels.length; i++) {
213
+ const channel: ExtendedAudioQueueChannel = audioChannels[i];
214
+ if (channel && channel.queue.length > 0) {
215
+ const currentAudio: HTMLAudioElement = channel.queue[0];
216
+ if (!currentAudio.paused && !currentAudio.ended) {
217
+ hasPlayingChannel = true;
218
+ break;
219
+ }
220
+ }
221
+ }
222
+
223
+ // If any channel is playing, pause all with fade
224
+ // If no channels are playing, resume all with fade
225
+ if (hasPlayingChannel) {
226
+ await pauseAllWithFade(fadeType);
227
+ } else {
228
+ await resumeAllWithFade();
229
+ }
230
+ };
231
+
232
+ /**
233
+ * Pauses the currently playing audio in a specific channel
234
+ * @param channelNumber - The channel number to pause (defaults to 0)
235
+ * @returns Promise that resolves when the audio is paused
236
+ * @example
237
+ * ```typescript
238
+ * await pauseChannel(0); // Pause audio in channel 0
239
+ * await pauseChannel(); // Pause audio in default channel
240
+ * ```
241
+ */
242
+ export const pauseChannel = async (channelNumber: number = 0): Promise<void> => {
243
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
244
+
245
+ if (channel && channel.queue.length > 0) {
246
+ const currentAudio: HTMLAudioElement = channel.queue[0];
247
+
248
+ if (!currentAudio.paused && !currentAudio.ended) {
249
+ currentAudio.pause();
250
+ channel.isPaused = true;
251
+
252
+ const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
253
+ if (audioInfo) {
254
+ emitAudioPause(channelNumber, audioInfo, audioChannels);
255
+ }
256
+ }
257
+ }
258
+ };
259
+
260
+ /**
261
+ * Resumes the currently paused audio in a specific channel
262
+ * @param channelNumber - The channel number to resume (defaults to 0)
263
+ * @returns Promise that resolves when the audio starts playing
264
+ * @example
265
+ * ```typescript
266
+ * await resumeChannel(0); // Resume audio in channel 0
267
+ * await resumeChannel(); // Resume audio in default channel
268
+ * ```
269
+ */
270
+ export const resumeChannel = async (channelNumber: number = 0): Promise<void> => {
271
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
272
+
273
+ if (channel && channel.queue.length > 0) {
274
+ const currentAudio: HTMLAudioElement = channel.queue[0];
275
+
276
+ // Only resume if both the channel is marked as paused AND the audio element is actually paused AND not ended
277
+ if (channel.isPaused && currentAudio.paused && !currentAudio.ended) {
278
+ await currentAudio.play();
279
+ channel.isPaused = false;
280
+
281
+ const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
282
+ if (audioInfo) {
283
+ emitAudioResume(channelNumber, audioInfo, audioChannels);
284
+ }
285
+ }
286
+ }
287
+ };
288
+
289
+ /**
290
+ * Toggles pause/resume state for a specific channel
291
+ * @param channelNumber - The channel number to toggle (defaults to 0)
292
+ * @returns Promise that resolves when the toggle is complete
293
+ * @example
294
+ * ```typescript
295
+ * await togglePauseChannel(0); // Toggle pause state for channel 0
296
+ * ```
297
+ */
298
+ export const togglePauseChannel = async (channelNumber: number = 0): Promise<void> => {
299
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
300
+
301
+ if (channel && channel.queue.length > 0) {
302
+ const currentAudio: HTMLAudioElement = channel.queue[0];
303
+
304
+ if (currentAudio.paused) {
305
+ await resumeChannel(channelNumber);
306
+ } else {
307
+ await pauseChannel(channelNumber);
308
+ }
309
+ }
310
+ };
311
+
312
+ /**
313
+ * Pauses all currently playing audio across all channels
314
+ * @returns Promise that resolves when all audio is paused
315
+ * @example
316
+ * ```typescript
317
+ * await pauseAllChannels(); // Pause everything
318
+ * ```
319
+ */
320
+ export const pauseAllChannels = async (): Promise<void> => {
321
+ const pausePromises: Promise<void>[] = [];
322
+
323
+ audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
324
+ pausePromises.push(pauseChannel(index));
325
+ });
326
+
327
+ await Promise.all(pausePromises);
328
+ };
329
+
330
+ /**
331
+ * Resumes all currently paused audio across all channels
332
+ * @returns Promise that resolves when all audio is resumed
333
+ * @example
334
+ * ```typescript
335
+ * await resumeAllChannels(); // Resume everything that was paused
336
+ * ```
337
+ */
338
+ export const resumeAllChannels = async (): Promise<void> => {
339
+ const resumePromises: Promise<void>[] = [];
340
+
341
+ audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
342
+ resumePromises.push(resumeChannel(index));
343
+ });
344
+
345
+ await Promise.all(resumePromises);
346
+ };
347
+
348
+ /**
349
+ * Checks if a specific channel is currently paused
350
+ * @param channelNumber - The channel number to check (defaults to 0)
351
+ * @returns True if the channel is paused, false otherwise
352
+ * @example
353
+ * ```typescript
354
+ * const isPaused = isChannelPaused(0);
355
+ * console.log(`Channel 0 is ${isPaused ? 'paused' : 'playing'}`);
356
+ * ```
357
+ */
358
+ export const isChannelPaused = (channelNumber: number = 0): boolean => {
359
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
360
+ return channel?.isPaused || false;
361
+ };
362
+
363
+ /**
364
+ * Gets the pause state of all channels
365
+ * @returns Array of boolean values indicating pause state for each channel
366
+ * @example
367
+ * ```typescript
368
+ * const pauseStates = getAllChannelsPauseState();
369
+ * pauseStates.forEach((isPaused, index) => {
370
+ * console.log(`Channel ${index}: ${isPaused ? 'paused' : 'playing'}`);
371
+ * });
372
+ * ```
373
+ */
374
+ export const getAllChannelsPauseState = (): boolean[] => {
375
+ return audioChannels.map((channel: ExtendedAudioQueueChannel) =>
376
+ channel?.isPaused || false
377
+ );
378
+ };
379
+
380
+ /**
381
+ * Toggles pause/resume state for all channels globally
382
+ * If any channels are currently playing, all channels will be paused
383
+ * If all channels are paused, all channels will be resumed
384
+ * @returns Promise that resolves when the toggle is complete
385
+ * @example
386
+ * ```typescript
387
+ * await togglePauseAllChannels(); // Pause all if any are playing, resume all if all are paused
388
+ * ```
389
+ */
390
+ export const togglePauseAllChannels = async (): Promise<void> => {
391
+ let hasPlayingChannel: boolean = false;
392
+
393
+ // Check if any channel is currently playing
394
+ for (let i = 0; i < audioChannels.length; i++) {
395
+ const channel: ExtendedAudioQueueChannel = audioChannels[i];
396
+ if (channel && channel.queue.length > 0) {
397
+ const currentAudio: HTMLAudioElement = channel.queue[0];
398
+ if (!currentAudio.paused && !currentAudio.ended) {
399
+ hasPlayingChannel = true;
400
+ break;
401
+ }
402
+ }
403
+ }
404
+
405
+ // If any channel is playing, pause all channels
406
+ // If no channels are playing, resume all channels
407
+ if (hasPlayingChannel) {
408
+ await pauseAllChannels();
409
+ } else {
410
+ await resumeAllChannels();
411
+ }
190
412
  };