audio-channel-queue 1.8.0 → 1.10.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/events.ts CHANGED
@@ -2,13 +2,14 @@
2
2
  * @fileoverview Event handling and emission for the audio-channel-queue package
3
3
  */
4
4
 
5
- import {
6
- AudioStartInfo,
7
- AudioCompleteInfo,
5
+ import {
6
+ AudioStartInfo,
7
+ AudioCompleteInfo,
8
8
  ExtendedAudioQueueChannel,
9
9
  QueueSnapshot,
10
10
  ProgressCallback,
11
11
  AudioInfo,
12
+ GLOBAL_PROGRESS_KEY
12
13
  } from './types';
13
14
  import { createQueueSnapshot, getAudioInfoFromElement } from './utils';
14
15
 
@@ -22,19 +23,20 @@ import { createQueueSnapshot, getAudioInfoFromElement } from './utils';
22
23
  * ```
23
24
  */
24
25
  export const emitQueueChange = (
25
- channelNumber: number,
26
+ channelNumber: number,
26
27
  audioChannels: ExtendedAudioQueueChannel[]
27
28
  ): void => {
28
29
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
29
- if (!channel || !channel.queueChangeCallbacks) return;
30
+ if (!channel?.queueChangeCallbacks) return;
30
31
 
31
32
  const snapshot: QueueSnapshot | null = createQueueSnapshot(channelNumber, audioChannels);
32
33
  if (!snapshot) return;
33
34
 
34
- channel.queueChangeCallbacks.forEach(callback => {
35
+ channel.queueChangeCallbacks.forEach((callback) => {
35
36
  try {
36
37
  callback(snapshot);
37
38
  } catch (error) {
39
+ // eslint-disable-next-line no-console
38
40
  console.error('Error in queue change callback:', error);
39
41
  }
40
42
  });
@@ -51,17 +53,18 @@ export const emitQueueChange = (
51
53
  * ```
52
54
  */
53
55
  export const emitAudioStart = (
54
- channelNumber: number,
56
+ channelNumber: number,
55
57
  audioInfo: AudioStartInfo,
56
58
  audioChannels: ExtendedAudioQueueChannel[]
57
59
  ): void => {
58
60
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
59
- if (!channel || !channel.audioStartCallbacks) return;
61
+ if (!channel?.audioStartCallbacks) return;
60
62
 
61
- channel.audioStartCallbacks.forEach(callback => {
63
+ channel.audioStartCallbacks.forEach((callback) => {
62
64
  try {
63
65
  callback(audioInfo);
64
66
  } catch (error) {
67
+ // eslint-disable-next-line no-console
65
68
  console.error('Error in audio start callback:', error);
66
69
  }
67
70
  });
@@ -78,17 +81,18 @@ export const emitAudioStart = (
78
81
  * ```
79
82
  */
80
83
  export const emitAudioComplete = (
81
- channelNumber: number,
84
+ channelNumber: number,
82
85
  audioInfo: AudioCompleteInfo,
83
86
  audioChannels: ExtendedAudioQueueChannel[]
84
87
  ): void => {
85
88
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
86
- if (!channel || !channel.audioCompleteCallbacks) return;
89
+ if (!channel?.audioCompleteCallbacks) return;
87
90
 
88
- channel.audioCompleteCallbacks.forEach(callback => {
91
+ channel.audioCompleteCallbacks.forEach((callback) => {
89
92
  try {
90
93
  callback(audioInfo);
91
94
  } catch (error) {
95
+ // eslint-disable-next-line no-console
92
96
  console.error('Error in audio complete callback:', error);
93
97
  }
94
98
  });
@@ -105,17 +109,18 @@ export const emitAudioComplete = (
105
109
  * ```
106
110
  */
107
111
  export const emitAudioPause = (
108
- channelNumber: number,
112
+ channelNumber: number,
109
113
  audioInfo: AudioInfo,
110
114
  audioChannels: ExtendedAudioQueueChannel[]
111
115
  ): void => {
112
116
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
113
- if (!channel || !channel.audioPauseCallbacks) return;
117
+ if (!channel?.audioPauseCallbacks) return;
114
118
 
115
- channel.audioPauseCallbacks.forEach(callback => {
119
+ channel.audioPauseCallbacks.forEach((callback) => {
116
120
  try {
117
121
  callback(channelNumber, audioInfo);
118
122
  } catch (error) {
123
+ // eslint-disable-next-line no-console
119
124
  console.error('Error in audio pause callback:', error);
120
125
  }
121
126
  });
@@ -132,24 +137,25 @@ export const emitAudioPause = (
132
137
  * ```
133
138
  */
134
139
  export const emitAudioResume = (
135
- channelNumber: number,
140
+ channelNumber: number,
136
141
  audioInfo: AudioInfo,
137
142
  audioChannels: ExtendedAudioQueueChannel[]
138
143
  ): void => {
139
144
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
140
- if (!channel || !channel.audioResumeCallbacks) return;
145
+ if (!channel?.audioResumeCallbacks) return;
141
146
 
142
- channel.audioResumeCallbacks.forEach(callback => {
147
+ channel.audioResumeCallbacks.forEach((callback) => {
143
148
  try {
144
149
  callback(channelNumber, audioInfo);
145
150
  } catch (error) {
151
+ // eslint-disable-next-line no-console
146
152
  console.error('Error in audio resume callback:', error);
147
153
  }
148
154
  });
149
155
  };
150
156
 
151
157
  // Store listener functions for cleanup
152
- const progressListeners = new WeakMap<HTMLAudioElement, () => void>();
158
+ const progressListeners: WeakMap<HTMLAudioElement, () => void> = new WeakMap();
153
159
 
154
160
  /**
155
161
  * Sets up comprehensive progress tracking for an audio element
@@ -163,7 +169,7 @@ const progressListeners = new WeakMap<HTMLAudioElement, () => void>();
163
169
  * ```
164
170
  */
165
171
  export const setupProgressTracking = (
166
- audio: HTMLAudioElement,
172
+ audio: HTMLAudioElement,
167
173
  channelNumber: number,
168
174
  audioChannels: ExtendedAudioQueueChannel[]
169
175
  ): void => {
@@ -179,12 +185,14 @@ export const setupProgressTracking = (
179
185
 
180
186
  const updateProgress = (): void => {
181
187
  // Get callbacks for this specific audio element AND the channel-wide callbacks
182
- const audioCallbacks: Set<ProgressCallback> = channel.progressCallbacks?.get(audio) || new Set();
183
- const channelCallbacks: Set<ProgressCallback> = channel.progressCallbacks?.get(null as any) || new Set();
184
-
188
+ const audioCallbacks: Set<ProgressCallback> =
189
+ channel.progressCallbacks?.get(audio) ?? new Set();
190
+ const channelCallbacks: Set<ProgressCallback> =
191
+ channel.progressCallbacks?.get(GLOBAL_PROGRESS_KEY) ?? new Set();
192
+
185
193
  // Combine both sets of callbacks
186
194
  const allCallbacks: Set<ProgressCallback> = new Set([...audioCallbacks, ...channelCallbacks]);
187
-
195
+
188
196
  if (allCallbacks.size === 0) return;
189
197
 
190
198
  const info: AudioInfo | null = getAudioInfoFromElement(audio, channelNumber, audioChannels);
@@ -193,6 +201,7 @@ export const setupProgressTracking = (
193
201
  try {
194
202
  callback(info);
195
203
  } catch (error) {
204
+ // eslint-disable-next-line no-console
196
205
  console.error('Error in progress callback:', error);
197
206
  }
198
207
  });
@@ -221,12 +230,12 @@ export const setupProgressTracking = (
221
230
  * ```
222
231
  */
223
232
  export const cleanupProgressTracking = (
224
- audio: HTMLAudioElement,
233
+ audio: HTMLAudioElement,
225
234
  channelNumber: number,
226
235
  audioChannels: ExtendedAudioQueueChannel[]
227
236
  ): void => {
228
237
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
229
- if (!channel || !channel.progressCallbacks) return;
238
+ if (!channel?.progressCallbacks) return;
230
239
 
231
240
  // Remove event listeners
232
241
  const updateProgress: (() => void) | undefined = progressListeners.get(audio);
@@ -240,4 +249,4 @@ export const cleanupProgressTracking = (
240
249
  }
241
250
 
242
251
  channel.progressCallbacks.delete(audio);
243
- };
252
+ };
package/src/index.ts CHANGED
@@ -5,65 +5,89 @@
5
5
  */
6
6
 
7
7
  // Core queue management functions
8
- export { queueAudio, queueAudioPriority, stopCurrentAudioInChannel, stopAllAudioInChannel, stopAllAudio, playAudioQueue } from './core';
8
+ export {
9
+ queueAudio,
10
+ queueAudioPriority,
11
+ stopCurrentAudioInChannel,
12
+ stopAllAudioInChannel,
13
+ stopAllAudio,
14
+ playAudioQueue,
15
+ destroyChannel,
16
+ destroyAllChannels,
17
+ setQueueConfig,
18
+ getQueueConfig,
19
+ setChannelQueueLimit
20
+ } from './core';
21
+
22
+ // Queue manipulation functions
23
+ export {
24
+ clearQueueAfterCurrent,
25
+ getQueueItemInfo,
26
+ getQueueLength,
27
+ removeQueuedItem,
28
+ reorderQueue,
29
+ swapQueueItems
30
+ } from './queue-manipulation';
9
31
 
10
32
  // Error handling and recovery functions
11
- export {
12
- getErrorRecovery,
13
- getRetryConfig,
14
- offAudioError,
15
- onAudioError,
33
+ export {
34
+ getErrorRecovery,
35
+ getRetryConfig,
36
+ offAudioError,
37
+ onAudioError,
16
38
  retryFailedAudio,
17
- setErrorRecovery,
18
- setRetryConfig,
39
+ setErrorRecovery,
40
+ setRetryConfig
19
41
  } from './errors';
20
42
 
21
43
  // Pause and resume management functions
22
- export {
23
- getAllChannelsPauseState,
24
- isChannelPaused,
25
- pauseAllChannels,
44
+ export {
45
+ getAllChannelsPauseState,
46
+ isChannelPaused,
47
+ pauseAllChannels,
26
48
  pauseAllWithFade,
27
- pauseChannel,
49
+ pauseChannel,
28
50
  pauseWithFade,
29
- resumeAllChannels,
51
+ resumeAllChannels,
30
52
  resumeAllWithFade,
31
- resumeChannel,
53
+ resumeChannel,
32
54
  resumeWithFade,
33
55
  togglePauseAllChannels,
34
56
  togglePauseAllWithFade,
35
57
  togglePauseChannel,
36
- togglePauseWithFade,
58
+ togglePauseWithFade
37
59
  } from './pause';
38
60
 
39
61
  // Volume control and ducking functions
40
- export {
62
+ export {
41
63
  clearVolumeDucking,
42
64
  fadeVolume,
43
- getAllChannelsVolume,
44
- getChannelVolume,
65
+ getAllChannelsVolume,
66
+ getChannelVolume,
45
67
  getFadeConfig,
46
- setAllChannelsVolume,
47
- setChannelVolume,
68
+ setAllChannelsVolume,
69
+ setChannelVolume,
48
70
  setVolumeDucking,
49
71
  transitionVolume,
72
+ cancelVolumeTransition,
73
+ cancelAllVolumeTransitions
50
74
  } from './volume';
51
75
 
52
76
  // Audio information and progress tracking functions
53
- export {
54
- getAllChannelsInfo,
55
- getCurrentAudioInfo,
56
- getQueueSnapshot,
57
- offAudioPause,
58
- offAudioProgress,
77
+ export {
78
+ getAllChannelsInfo,
79
+ getCurrentAudioInfo,
80
+ getQueueSnapshot,
81
+ offAudioPause,
82
+ offAudioProgress,
59
83
  offAudioResume,
60
- offQueueChange,
61
- onAudioComplete,
62
- onAudioPause,
63
- onAudioProgress,
64
- onAudioResume,
65
- onAudioStart,
66
- onQueueChange,
84
+ offQueueChange,
85
+ onAudioComplete,
86
+ onAudioPause,
87
+ onAudioProgress,
88
+ onAudioResume,
89
+ onAudioStart,
90
+ onQueueChange
67
91
  } from './info';
68
92
 
69
93
  // Core data access for legacy compatibility
@@ -74,16 +98,18 @@ export {
74
98
  cleanWebpackFilename,
75
99
  createQueueSnapshot,
76
100
  extractFileName,
77
- getAudioInfoFromElement
101
+ getAudioInfoFromElement,
102
+ sanitizeForDisplay,
103
+ validateAudioUrl
78
104
  } from './utils';
79
105
 
80
106
  // TypeScript type definitions and interfaces
81
- export type {
107
+ export type {
82
108
  AudioCompleteCallback,
83
109
  AudioCompleteInfo,
84
110
  AudioErrorCallback,
85
111
  AudioErrorInfo,
86
- AudioInfo,
112
+ AudioInfo,
87
113
  AudioPauseCallback,
88
114
  AudioQueueOptions,
89
115
  AudioResumeCallback,
@@ -96,13 +122,12 @@ export type {
96
122
  ProgressCallback,
97
123
  QueueChangeCallback,
98
124
  QueueItem,
125
+ QueueManipulationResult,
99
126
  QueueSnapshot,
100
127
  RetryConfig,
101
- VolumeConfig
128
+ VolumeConfig,
129
+ QueueConfig
102
130
  } from './types';
103
131
 
104
- // Enums
105
- export {
106
- EasingType,
107
- FadeType
108
- } from './types';
132
+ // Enums and constants
133
+ export { EasingType, FadeType, MAX_CHANNELS, TimerType, GLOBAL_PROGRESS_KEY } from './types';
package/src/info.ts CHANGED
@@ -2,16 +2,18 @@
2
2
  * @fileoverview Audio information and progress tracking functions for the audio-channel-queue package
3
3
  */
4
4
 
5
- import {
6
- AudioInfo,
7
- QueueSnapshot,
5
+ import {
6
+ AudioInfo,
7
+ QueueSnapshot,
8
8
  ProgressCallback,
9
9
  QueueChangeCallback,
10
10
  AudioStartCallback,
11
11
  AudioCompleteCallback,
12
12
  AudioPauseCallback,
13
13
  AudioResumeCallback,
14
- ExtendedAudioQueueChannel
14
+ ExtendedAudioQueueChannel,
15
+ GLOBAL_PROGRESS_KEY,
16
+ MAX_CHANNELS
15
17
  } from './types';
16
18
  import { getAudioInfoFromElement, createQueueSnapshot } from './utils';
17
19
  import { setupProgressTracking, cleanupProgressTracking } from './events';
@@ -19,8 +21,85 @@ import { setupProgressTracking, cleanupProgressTracking } from './events';
19
21
  /**
20
22
  * Global array to store audio channels with their queues and callback management
21
23
  * Each channel maintains its own audio queue and event callback sets
24
+ *
25
+ * Note: While you can inspect this array for debugging, direct modification is discouraged.
26
+ * Use the provided API functions for safe channel management.
22
27
  */
23
- export const audioChannels: ExtendedAudioQueueChannel[] = [];
28
+ export const audioChannels: ExtendedAudioQueueChannel[] = new Proxy(
29
+ [] as ExtendedAudioQueueChannel[],
30
+ {
31
+ deleteProperty(target: ExtendedAudioQueueChannel[], prop: string | symbol): boolean {
32
+ if (typeof prop === 'string' && !isNaN(Number(prop))) {
33
+ // eslint-disable-next-line no-console
34
+ console.warn(
35
+ 'Warning: Direct deletion from audioChannels detected. ' +
36
+ 'Consider using stopAllAudioInChannel() for proper cleanup.'
37
+ );
38
+ }
39
+ delete (target as unknown as Record<string, unknown>)[prop as string];
40
+ return true;
41
+ },
42
+ get(target: ExtendedAudioQueueChannel[], prop: string | symbol): unknown {
43
+ const value = (target as unknown as Record<string, unknown>)[prop as string];
44
+
45
+ // Return channel objects with warnings on modification attempts
46
+ if (
47
+ typeof value === 'object' &&
48
+ value !== null &&
49
+ typeof prop === 'string' &&
50
+ !isNaN(Number(prop))
51
+ ) {
52
+ return new Proxy(value as ExtendedAudioQueueChannel, {
53
+ set(
54
+ channelTarget: ExtendedAudioQueueChannel,
55
+ channelProp: string | symbol,
56
+ channelValue: unknown
57
+ ): boolean {
58
+ // Allow internal modifications but warn about direct property changes
59
+ if (
60
+ typeof channelProp === 'string' &&
61
+ !['queue', 'volume', 'isPaused', 'isLocked', 'volumeConfig'].includes(channelProp)
62
+ ) {
63
+ // eslint-disable-next-line no-console
64
+ console.warn(
65
+ `Warning: Direct modification of channel.${channelProp} detected. ` +
66
+ 'Use API functions for safer channel management.'
67
+ );
68
+ }
69
+ const key = typeof channelProp === 'symbol' ? channelProp.toString() : channelProp;
70
+ (channelTarget as unknown as Record<string, unknown>)[key] = channelValue;
71
+ return true;
72
+ }
73
+ });
74
+ }
75
+
76
+ return value;
77
+ },
78
+ set(target: ExtendedAudioQueueChannel[], prop: string | symbol, value: unknown): boolean {
79
+ // Allow normal array operations
80
+ const key = typeof prop === 'symbol' ? prop.toString() : prop;
81
+ (target as unknown as Record<string, unknown>)[key] = value;
82
+ return true;
83
+ }
84
+ }
85
+ );
86
+
87
+ /**
88
+ * Validates a channel number against MAX_CHANNELS limit
89
+ * @param channelNumber - The channel number to validate
90
+ * @throws Error if the channel number is invalid
91
+ * @internal
92
+ */
93
+ const validateChannelNumber = (channelNumber: number): void => {
94
+ if (channelNumber < 0) {
95
+ throw new Error('Channel number must be non-negative');
96
+ }
97
+ if (channelNumber >= MAX_CHANNELS) {
98
+ throw new Error(
99
+ `Channel number ${channelNumber} exceeds maximum allowed channels (${MAX_CHANNELS})`
100
+ );
101
+ }
102
+ };
24
103
 
25
104
  /**
26
105
  * Gets current audio information for a specific channel
@@ -60,28 +139,29 @@ export const getCurrentAudioInfo = (channelNumber: number = 0): AudioInfo | null
60
139
  */
61
140
  export const getAllChannelsInfo = (): (AudioInfo | null)[] => {
62
141
  const allChannelsInfo: (AudioInfo | null)[] = [];
63
-
64
- for (let i = 0; i < audioChannels.length; i++) {
142
+
143
+ for (let i: number = 0; i < audioChannels.length; i++) {
65
144
  allChannelsInfo.push(getCurrentAudioInfo(i));
66
145
  }
67
-
146
+
68
147
  return allChannelsInfo;
69
148
  };
70
149
 
71
150
  /**
72
151
  * Gets a complete snapshot of the queue state for a specific channel
73
- * @param channelNumber - The channel number
152
+ * @param channelNumber - The channel number (defaults to 0)
74
153
  * @returns QueueSnapshot object or null if channel doesn't exist
75
154
  * @example
76
155
  * ```typescript
77
- * const snapshot = getQueueSnapshot(0);
156
+ * const snapshot = getQueueSnapshot();
78
157
  * if (snapshot) {
79
158
  * console.log(`Queue has ${snapshot.totalItems} items`);
80
159
  * console.log(`Currently playing: ${snapshot.items[0]?.fileName}`);
81
160
  * }
161
+ * const channelSnapshot = getQueueSnapshot(2);
82
162
  * ```
83
163
  */
84
- export const getQueueSnapshot = (channelNumber: number): QueueSnapshot | null => {
164
+ export const getQueueSnapshot = (channelNumber: number = 0): QueueSnapshot | null => {
85
165
  return createQueueSnapshot(channelNumber, audioChannels);
86
166
  };
87
167
 
@@ -89,6 +169,7 @@ export const getQueueSnapshot = (channelNumber: number): QueueSnapshot | null =>
89
169
  * Subscribes to real-time progress updates for a specific channel
90
170
  * @param channelNumber - The channel number
91
171
  * @param callback - Function to call with audio info updates
172
+ * @throws Error if the channel number exceeds the maximum allowed channels
92
173
  * @example
93
174
  * ```typescript
94
175
  * onAudioProgress(0, (info) => {
@@ -98,8 +179,10 @@ export const getQueueSnapshot = (channelNumber: number): QueueSnapshot | null =>
98
179
  * ```
99
180
  */
100
181
  export const onAudioProgress = (channelNumber: number, callback: ProgressCallback): void => {
182
+ validateChannelNumber(channelNumber);
183
+
101
184
  if (!audioChannels[channelNumber]) {
102
- audioChannels[channelNumber] = {
185
+ audioChannels[channelNumber] = {
103
186
  audioCompleteCallbacks: new Set(),
104
187
  audioErrorCallbacks: new Set(),
105
188
  audioPauseCallbacks: new Set(),
@@ -125,29 +208,30 @@ export const onAudioProgress = (channelNumber: number, callback: ProgressCallbac
125
208
  channel.progressCallbacks.set(currentAudio, new Set());
126
209
  }
127
210
  channel.progressCallbacks.get(currentAudio)!.add(callback);
128
-
211
+
129
212
  // Set up tracking if not already done
130
213
  setupProgressTracking(currentAudio, channelNumber, audioChannels);
131
214
  }
132
215
 
133
216
  // Store callback for future audio elements in this channel
134
- if (!channel.progressCallbacks.has(null as any)) {
135
- channel.progressCallbacks.set(null as any, new Set());
217
+ if (!channel.progressCallbacks.has(GLOBAL_PROGRESS_KEY)) {
218
+ channel.progressCallbacks.set(GLOBAL_PROGRESS_KEY, new Set());
136
219
  }
137
- channel.progressCallbacks.get(null as any)!.add(callback);
220
+ channel.progressCallbacks.get(GLOBAL_PROGRESS_KEY)!.add(callback);
138
221
  };
139
222
 
140
223
  /**
141
224
  * Removes progress listeners for a specific channel
142
- * @param channelNumber - The channel number
225
+ * @param channelNumber - The channel number (defaults to 0)
143
226
  * @example
144
227
  * ```typescript
145
- * offAudioProgress(0); // Stop receiving progress updates for channel 0
228
+ * offAudioProgress();
229
+ * offAudioProgress(1); // Stop receiving progress updates for channel 1
146
230
  * ```
147
231
  */
148
- export const offAudioProgress = (channelNumber: number): void => {
232
+ export function offAudioProgress(channelNumber: number = 0): void {
149
233
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
150
- if (!channel || !channel.progressCallbacks) return;
234
+ if (!channel?.progressCallbacks) return;
151
235
 
152
236
  // Clean up event listeners for current audio if exists
153
237
  if (channel.queue.length > 0) {
@@ -157,12 +241,13 @@ export const offAudioProgress = (channelNumber: number): void => {
157
241
 
158
242
  // Clear all callbacks for this channel
159
243
  channel.progressCallbacks.clear();
160
- };
244
+ }
161
245
 
162
246
  /**
163
247
  * Subscribes to queue change events for a specific channel
164
248
  * @param channelNumber - The channel number to monitor
165
249
  * @param callback - Function to call when queue changes
250
+ * @throws Error if the channel number exceeds the maximum allowed channels
166
251
  * @example
167
252
  * ```typescript
168
253
  * onQueueChange(0, (snapshot) => {
@@ -172,8 +257,10 @@ export const offAudioProgress = (channelNumber: number): void => {
172
257
  * ```
173
258
  */
174
259
  export const onQueueChange = (channelNumber: number, callback: QueueChangeCallback): void => {
260
+ validateChannelNumber(channelNumber);
261
+
175
262
  if (!audioChannels[channelNumber]) {
176
- audioChannels[channelNumber] = {
263
+ audioChannels[channelNumber] = {
177
264
  audioCompleteCallbacks: new Set(),
178
265
  audioErrorCallbacks: new Set(),
179
266
  audioPauseCallbacks: new Set(),
@@ -205,7 +292,7 @@ export const onQueueChange = (channelNumber: number, callback: QueueChangeCallba
205
292
  */
206
293
  export const offQueueChange = (channelNumber: number): void => {
207
294
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
208
- if (!channel || !channel.queueChangeCallbacks) return;
295
+ if (!channel?.queueChangeCallbacks) return;
209
296
 
210
297
  channel.queueChangeCallbacks.clear();
211
298
  };
@@ -214,6 +301,7 @@ export const offQueueChange = (channelNumber: number): void => {
214
301
  * Subscribes to audio start events for a specific channel
215
302
  * @param channelNumber - The channel number to monitor
216
303
  * @param callback - Function to call when audio starts playing
304
+ * @throws Error if the channel number exceeds the maximum allowed channels
217
305
  * @example
218
306
  * ```typescript
219
307
  * onAudioStart(0, (info) => {
@@ -223,8 +311,10 @@ export const offQueueChange = (channelNumber: number): void => {
223
311
  * ```
224
312
  */
225
313
  export const onAudioStart = (channelNumber: number, callback: AudioStartCallback): void => {
314
+ validateChannelNumber(channelNumber);
315
+
226
316
  if (!audioChannels[channelNumber]) {
227
- audioChannels[channelNumber] = {
317
+ audioChannels[channelNumber] = {
228
318
  audioCompleteCallbacks: new Set(),
229
319
  audioErrorCallbacks: new Set(),
230
320
  audioPauseCallbacks: new Set(),
@@ -250,6 +340,7 @@ export const onAudioStart = (channelNumber: number, callback: AudioStartCallback
250
340
  * Subscribes to audio complete events for a specific channel
251
341
  * @param channelNumber - The channel number to monitor
252
342
  * @param callback - Function to call when audio completes
343
+ * @throws Error if the channel number exceeds the maximum allowed channels
253
344
  * @example
254
345
  * ```typescript
255
346
  * onAudioComplete(0, (info) => {
@@ -261,8 +352,10 @@ export const onAudioStart = (channelNumber: number, callback: AudioStartCallback
261
352
  * ```
262
353
  */
263
354
  export const onAudioComplete = (channelNumber: number, callback: AudioCompleteCallback): void => {
355
+ validateChannelNumber(channelNumber);
356
+
264
357
  if (!audioChannels[channelNumber]) {
265
- audioChannels[channelNumber] = {
358
+ audioChannels[channelNumber] = {
266
359
  audioCompleteCallbacks: new Set(),
267
360
  audioErrorCallbacks: new Set(),
268
361
  audioPauseCallbacks: new Set(),
@@ -288,6 +381,7 @@ export const onAudioComplete = (channelNumber: number, callback: AudioCompleteCa
288
381
  * Subscribes to audio pause events for a specific channel
289
382
  * @param channelNumber - The channel number to monitor
290
383
  * @param callback - Function to call when audio is paused
384
+ * @throws Error if the channel number exceeds the maximum allowed channels
291
385
  * @example
292
386
  * ```typescript
293
387
  * onAudioPause(0, (channelNumber, info) => {
@@ -297,8 +391,10 @@ export const onAudioComplete = (channelNumber: number, callback: AudioCompleteCa
297
391
  * ```
298
392
  */
299
393
  export const onAudioPause = (channelNumber: number, callback: AudioPauseCallback): void => {
394
+ validateChannelNumber(channelNumber);
395
+
300
396
  if (!audioChannels[channelNumber]) {
301
- audioChannels[channelNumber] = {
397
+ audioChannels[channelNumber] = {
302
398
  audioCompleteCallbacks: new Set(),
303
399
  audioErrorCallbacks: new Set(),
304
400
  audioPauseCallbacks: new Set(),
@@ -324,6 +420,7 @@ export const onAudioPause = (channelNumber: number, callback: AudioPauseCallback
324
420
  * Subscribes to audio resume events for a specific channel
325
421
  * @param channelNumber - The channel number to monitor
326
422
  * @param callback - Function to call when audio is resumed
423
+ * @throws Error if the channel number exceeds the maximum allowed channels
327
424
  * @example
328
425
  * ```typescript
329
426
  * onAudioResume(0, (channelNumber, info) => {
@@ -333,8 +430,10 @@ export const onAudioPause = (channelNumber: number, callback: AudioPauseCallback
333
430
  * ```
334
431
  */
335
432
  export const onAudioResume = (channelNumber: number, callback: AudioResumeCallback): void => {
433
+ validateChannelNumber(channelNumber);
434
+
336
435
  if (!audioChannels[channelNumber]) {
337
- audioChannels[channelNumber] = {
436
+ audioChannels[channelNumber] = {
338
437
  audioCompleteCallbacks: new Set(),
339
438
  audioErrorCallbacks: new Set(),
340
439
  audioPauseCallbacks: new Set(),
@@ -366,7 +465,7 @@ export const onAudioResume = (channelNumber: number, callback: AudioResumeCallba
366
465
  */
367
466
  export const offAudioPause = (channelNumber: number): void => {
368
467
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
369
- if (!channel || !channel.audioPauseCallbacks) return;
468
+ if (!channel?.audioPauseCallbacks) return;
370
469
 
371
470
  channel.audioPauseCallbacks.clear();
372
471
  };
@@ -381,7 +480,7 @@ export const offAudioPause = (channelNumber: number): void => {
381
480
  */
382
481
  export const offAudioResume = (channelNumber: number): void => {
383
482
  const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
384
- if (!channel || !channel.audioResumeCallbacks) return;
483
+ if (!channel?.audioResumeCallbacks) return;
385
484
 
386
485
  channel.audioResumeCallbacks.clear();
387
- };
486
+ };