audio-channel-queue 1.9.0 β†’ 1.11.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/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Audio Channel Queue
2
2
  The purpose of this package is to help manage the playback of audio files.
3
3
 
4
+ 🎡 [Demo](https://tonycarpenter21.github.io/audio-queue-demo/queue-management)
5
+
6
+ πŸ“š [Docs](https://tonycarpenter21.github.io/audio-queue-docs/)
7
+
4
8
  ## 🌟 Key Features
5
9
 
6
10
  - βœ… **Multi-channel queue management** - Independent audio queues for concurrent playback
@@ -22,6 +26,8 @@ NPM package can be found [here](https://www.npmjs.com/package/audio-channel-queu
22
26
 
23
27
  GitHub Repo can be found [here](https://github.com/tonycarpenter21/audio-channel-queue).
24
28
 
29
+ Documentation can be found [here](https://tonycarpenter21.github.io/audio-queue-docs/)
30
+
25
31
  ## 🌐 Browser Compatibility
26
32
 
27
33
  This package is designed for **browser environments** and uses the Web Audio API (`HTMLAudioElement`). It is **not** intended for Node.js server-side use.
@@ -66,111 +72,206 @@ Install this package by running either of these commands (typescript packages ar
66
72
  ## Basic Queue Management Functions:
67
73
 
68
74
  ### Queue Audio
69
- ```queueAudio(audioFileGoesHere, channelNumber, options);```
70
- Use the `queueAudio()` function to add a file to the queue and start playing it automatically. It takes three arguments:
71
- - The first argument is an imported sound file.
72
- - The second argument is optional and it allows you to choose a different queue channel.
73
- - The third argument is optional configuration for loop, volume, and priority.
75
+ ```typescript
76
+ // Add an audio file to the queue and start playing it automatically.
77
+ queueAudio(audioUrl, channelNumber?, options?);
78
+ queueAudio('hello.mp3'); // Add to default channel 0
79
+ queueAudio('laser.mp3', 1, { loop: true, volume: 0.8 }); // Add to channel 1 with options
80
+ ```
81
+
74
82
 
75
83
  ### Queue Audio with Priority
76
- ```queueAudioPriority(audioFileGoesHere, channelNumber, options);```
77
- Use the `queueAudioPriority()` function to add a file to the front of the queue (plays after current audio finishes). Perfect for urgent announcements!
84
+ ```typescript
85
+ // Add a file to the front of the queue (plays after current audio finishes).
86
+ queueAudioPriority(audioUrl, channelNumber?, options?);
87
+ queueAudioPriority('urgent.mp3'); // Add to front of default channel 0
88
+ queueAudioPriority('announcement.mp3', 1, { volume: 1.0 }); // Add to front of channel 1
89
+ ```
90
+
78
91
 
79
92
  ### Stop Current Audio
80
- ```stopCurrentAudioInChannel(queueChannelNumberGoesHere);```
81
- Use the `stopCurrentAudioInChannel()` function to stop the current playback of a file in a queue and start playing the next one automatically.
93
+ ```typescript
94
+ // Stop the current audio and automatically start playing the next one in queue.
95
+ stopCurrentAudioInChannel(channelNumber?);
96
+ stopCurrentAudioInChannel(); // Stop current audio in default channel (0)
97
+ stopCurrentAudioInChannel(2); // Stop current audio in channel 2
98
+ ```
99
+
82
100
 
83
101
  ### Stop All Audio in Channel
84
- ```stopAllAudioInChannel(queueChannelNumberGoesHere);```
85
- Use the `stopAllAudioInChannel()` function to stop the current playback of all files in a queue and removes all enqueued files.
102
+ ```typescript
103
+ // Stop all audio in a channel and remove all enqueued files.
104
+ stopAllAudioInChannel(channelNumber?);
105
+ stopAllAudioInChannel(); // Stop and clear all audio in default channel (0)
106
+ stopAllAudioInChannel(1); // Stop and clear all audio in channel 1
107
+ ```
86
108
 
87
109
  ### Stop All Audio
88
- ```stopAllAudio();```
89
- Use the `stopAllAudio()` function to stop the current playback of all files in all queues. It takes no arguments.
110
+ ```typescript
111
+ // Stop all audio in all channels and remove all enqueued files.
112
+ stopAllAudio();
113
+ ```
114
+
115
+ ## πŸ”„ Advanced Queue Manipulation:
116
+
117
+ ### Remove Queued Item
118
+ ```typescript
119
+ // Remove a specific item from the queue by its position (cannot remove currently playing item at index 0).
120
+ removeQueuedItem(queuedSlotNumber, channelNumber?);
121
+ const result = removeQueuedItem(2); // Remove item at index 2 from default channel (0)
122
+ const result = removeQueuedItem(1, 1); // Remove item at index 1 from channel 1
123
+ ```
124
+
125
+ ### Reorder Queue Items
126
+ ```typescript
127
+ // Move a queue item from one position to another (cannot move currently playing item at index 0).
128
+ reorderQueue(currentQueuedSlotNumber, newQueuedSlotNumber, channelNumber?);
129
+ const result = reorderQueue(3, 1); // Move item from index 3 to index 1 in default channel (0)
130
+ const result = reorderQueue(2, 4, 1); // Move item from index 2 to index 4 in channel 1
131
+ ```
132
+
133
+ ### Clear Queue After Current
134
+ ```typescript
135
+ // Remove all items from the queue except the currently playing audio.
136
+ clearQueueAfterCurrent(channelNumber?);
137
+ const result = clearQueueAfterCurrent(); // Clear queue after current in default channel (0)
138
+ const result = clearQueueAfterCurrent(2); // Clear queue after current in channel 2
139
+ ```
140
+
141
+ ### Swap Queue Items
142
+ ```typescript
143
+ // Swap the positions of two items in the queue (cannot involve currently playing item at index 0).
144
+ swapQueueItems(firstQueuedSlotNumber, secondQueuedSlotNumber, channelNumber?);
145
+ const result = swapQueueItems(1, 3); // Swap items at index 1 and 3 in default channel (0)
146
+ const result = swapQueueItems(2, 4, 1); // Swap items at index 2 and 4 in channel 1
147
+ ```
148
+
149
+ ### Get Queue Item Info
150
+ ```typescript
151
+ // Get information about a specific item in the queue.
152
+ getQueueItemInfo(queuedSlotNumber, channelNumber?);
153
+ const itemInfo = getQueueItemInfo(1); // Get info for item at index 1 in default channel (0)
154
+ const info = getQueueItemInfo(2, 1); // Get info for item at index 2 in channel 1
155
+ ```
156
+
157
+ ### Get Queue Length
158
+ ```typescript
159
+ // Get the total number of items in a channel's queue.
160
+ getQueueLength(channelNumber?);
161
+ const length = getQueueLength(); // Get queue length for default channel (0)
162
+ const count = getQueueLength(2); // Get queue length for channel 2
163
+ ```
164
+
165
+ ```typescript
166
+ // All queue manipulation functions return a QueueManipulationResult:
167
+ interface QueueManipulationResult {
168
+ success: boolean; // Whether the operation was successful
169
+ error?: string; // Error message if operation failed
170
+ updatedQueue?: QueueSnapshot; // The queue snapshot after the operation (if successful)
171
+ }
172
+ ```
90
173
 
91
174
  ## πŸŽ›οΈ Volume Control Functions:
92
175
 
93
176
  ### Set Channel Volume
94
- ```setChannelVolume(channelNumber, volume);```
95
- Set the volume for a specific channel (0-1 range).
96
177
  ```typescript
178
+ // Set the volume for a specific channel (0-1 range).
179
+ setChannelVolume(channelNumber, volume);
97
180
  setChannelVolume(0, 0.5); // Set channel 0 to 50% volume
98
181
  setChannelVolume(1, 0.8); // Set channel 1 to 80% volume
99
182
  ```
100
183
 
101
184
  ### Get Channel Volume
102
- ```getChannelVolume(channelNumber);```
103
- Get the current volume level for a specific channel.
104
185
  ```typescript
105
- const volume = getChannelVolume(0);
106
- console.log(`Channel 0 volume: ${volume * 100}%`);
186
+ // Get the current volume level for a specific channel.
187
+ getChannelVolume(channelNumber?);
188
+ const volume = getChannelVolume(); // Get default channel (0) volume
189
+ console.log(`Channel volume: ${(getChannelVolume(2) * 100).toFixed(0)}%`); // Get channel 2 volume
107
190
  ```
108
191
 
109
192
  ### Set All Channels Volume
110
- ```setAllChannelsVolume(volume);```
111
- Set the same volume level for all channels.
112
193
  ```typescript
194
+ // Set the same volume level for all channels.
195
+ setAllChannelsVolume(volume);
113
196
  setAllChannelsVolume(0.6); // Set all channels to 60% volume
197
+ setAllChannelsVolume(0.0); // Mute all channels
114
198
  ```
115
199
 
116
200
  ### Volume Ducking (Background Audio Reduction)
117
- ```setVolumeDucking(config);```
118
- Automatically reduce other channels' volume when priority audio plays - perfect for voice announcements over background music!
119
201
  ```typescript
120
- // When channel 1 plays, reduce all other channels to 20% volume
202
+ // Automatically reduce other channels' volume when priority audio plays.
203
+ setVolumeDucking(config);
204
+ setVolumeDucking({ priorityChannel: 1, duckingVolume: 0.2 }); // Simple ducking
121
205
  setVolumeDucking({
122
206
  priorityChannel: 1,
123
207
  priorityVolume: 1.0,
124
- duckingVolume: 0.2
125
- });
208
+ duckingVolume: 0.2,
209
+ transitionDuration: 500
210
+ }); // Full configuration
126
211
  ```
127
212
 
128
- ```clearVolumeDucking();```
129
- Remove volume ducking configuration from all channels.
213
+ ```typescript
214
+ // Remove volume ducking configuration from all channels.
215
+ clearVolumeDucking();
216
+ ```
130
217
 
131
218
  ## ⏯️ Pause/Resume Functions:
132
219
 
133
220
  ### Pause Channel
134
- ```pauseChannel(channelNumber);```
135
- Pause audio playback in a specific channel.
136
221
  ```typescript
137
- await pauseChannel(0); // Pause audio in channel 0
138
- await pauseChannel(); // Pause audio in default channel
222
+ // Pause audio playback in a specific channel.
223
+ pauseChannel(channelNumber?);
224
+ await pauseChannel(); // Pause audio in default channel (0)
225
+ await pauseChannel(1); // Pause audio in channel 1
139
226
  ```
140
227
 
141
228
  ### Resume Channel
142
- ```resumeChannel(channelNumber);```
143
- Resume audio playback in a specific channel.
144
229
  ```typescript
145
- await resumeChannel(0); // Resume audio in channel 0
230
+ // Resume audio playback in a specific channel.
231
+ resumeChannel(channelNumber?);
232
+ await resumeChannel(); // Resume audio in default channel (0)
233
+ await resumeChannel(1); // Resume audio in channel 1
146
234
  ```
147
235
 
148
236
  ### Toggle Pause
149
- ```togglePauseChannel(channelNumber);```
150
- Toggle between pause and resume states.
151
237
  ```typescript
152
- await togglePauseChannel(0); // Pause if playing, resume if paused
238
+ // Toggle between pause and resume states.
239
+ togglePauseChannel(channelNumber?);
240
+ await togglePauseChannel(); // Toggle default channel (0)
241
+ await togglePauseChannel(1); // Toggle channel 1
153
242
  ```
154
243
 
155
244
  ### Pause/Resume All Channels
156
- ```pauseAllChannels();``` and ```resumeAllChannels();```
157
- Control all channels simultaneously.
158
245
  ```typescript
159
- await pauseAllChannels(); // Emergency pause - everything stops
160
- await resumeAllChannels(); // Resume everything that was paused
246
+ // Pause all channels simultaneously.
247
+ pauseAllChannels();
248
+ await pauseAllChannels();
249
+ ```
250
+
251
+ ```typescript
252
+ // Resume all channels that were paused.
253
+ resumeAllChannels();
254
+ await resumeAllChannels();
161
255
  ```
162
256
 
163
257
  ### Global Toggle Pause/Resume
164
- ```togglePauseAllChannels();```
165
- Smart toggle that pauses all channels if any are playing, or resumes all if all are paused.
166
258
  ```typescript
167
- await togglePauseAllChannels(); // Pause all if any playing, resume all if all paused
259
+ // Smart toggle that pauses all channels if any are playing, or resumes all if all are paused.
260
+ togglePauseAllChannels();
261
+ await togglePauseAllChannels();
168
262
  ```
169
263
 
170
264
  ### Check Pause State
171
- ```isChannelPaused(channelNumber);``` and ```getAllChannelsPauseState();```
172
265
  ```typescript
173
- const isPaused = isChannelPaused(0);
266
+ // Check if a specific channel is paused.
267
+ isChannelPaused(channelNumber?);
268
+ const isPaused = isChannelPaused(); // Check if default channel (0) is paused
269
+ const channelPaused = isChannelPaused(2); // Check if channel 2 is paused
270
+ ```
271
+
272
+ ```typescript
273
+ // Get pause state for all channels.
274
+ getAllChannelsPauseState();
174
275
  const allPauseStates = getAllChannelsPauseState();
175
276
  ```
176
277
 
@@ -193,16 +294,27 @@ interface AudioInfo {
193
294
  ```
194
295
 
195
296
  ### Get Current Audio Info
196
- ```getCurrentAudioInfo(channelNumber);```
197
- Get information about the currently playing audio in a specific channel. Returns `AudioInfo | null`.
297
+ ```typescript
298
+ // Get information about the currently playing audio in a specific channel.
299
+ getCurrentAudioInfo(channelNumber?);
300
+ const audioInfo = getCurrentAudioInfo(); // Get current audio info for default channel (0)
301
+ const info = getCurrentAudioInfo(1); // Get info for channel 1 - returns AudioInfo | null
302
+ ```
198
303
 
199
304
  ### Get All Channels Info
200
- ```getAllChannelsInfo();```
201
- Get audio information for all channels. Returns an array of `AudioInfo | null` objects.
305
+ ```typescript
306
+ // Get audio information for all channels.
307
+ getAllChannelsInfo();
308
+ const allChannelsInfo = getAllChannelsInfo();
309
+ ```
202
310
 
203
311
  ### Queue State Management
204
- ```getQueueSnapshot(channelNumber);```
205
- Get a complete snapshot of the queue state for a specific channel. Returns `QueueSnapshot | null`.
312
+ ```typescript
313
+ // Get a complete snapshot of the queue state for a specific channel.
314
+ getQueueSnapshot(channelNumber?);
315
+ const queueSnapshot = getQueueSnapshot(); // Get snapshot for default channel (0)
316
+ const snapshot = getQueueSnapshot(2); // Get snapshot for channel 2 - returns QueueSnapshot | null
317
+ ```
206
318
 
207
319
  ```typescript
208
320
  interface QueueSnapshot {
@@ -223,247 +335,62 @@ interface QueueSnapshot {
223
335
  ```
224
336
 
225
337
  ### Real-time Progress Tracking
226
- ```onAudioProgress(channelNumber, callback);```
227
- Subscribe to real-time progress updates for a specific channel.
338
+ ```typescript
339
+ // Subscribe to real-time progress updates for a specific channel.
340
+ onAudioProgress(channelNumber, callback);
341
+ onAudioProgress(0, (info) => console.log(info.progress)); // Simple progress logging
342
+ onAudioProgress(1, (info) => updateProgressBar(info.currentTime, info.duration)); // Complex UI update
343
+ ```
228
344
 
229
- ```offAudioProgress(channelNumber);```
230
- Remove all progress listeners for a specific channel.
345
+ ```typescript
346
+ // Remove all progress listeners for a specific channel.
347
+ offAudioProgress(channelNumber?);
348
+ offAudioProgress(); // Remove all progress listeners in default channel (0)
349
+ offAudioProgress(1); // Remove all progress listeners in channel 1
350
+ ```
231
351
 
232
352
  ### Enhanced Event System
233
- ```onQueueChange(channelNumber, callback);```
234
- Subscribe to queue change events for visual updates.
235
-
236
- ```onAudioStart(channelNumber, callback);```
237
- Subscribe to audio start events.
238
-
239
- ```onAudioComplete(channelNumber, callback);```
240
- Subscribe to audio completion events.
241
-
242
- ```onAudioPause(channelNumber, callback);```
243
- Subscribe to audio pause events.
244
-
245
- ```onAudioResume(channelNumber, callback);```
246
- Subscribe to audio resume events.
247
-
248
- ### Example Usage with All New Features:
249
-
250
- `App.tsx`
251
- ```typescript
252
- import backgroundMusic from './audio/background.mp3';
253
- import announcement from './audio/announcement.wav';
254
- import {
255
- queueAudio,
256
- queueAudioPriority,
257
- getCurrentAudioInfo,
258
- pauseChannel,
259
- resumeChannel,
260
- togglePauseAllChannels,
261
- setChannelVolume,
262
- setVolumeDucking,
263
- onAudioProgress,
264
- onAudioPause,
265
- onAudioResume,
266
- onQueueChange,
267
- AudioInfo,
268
- QueueSnapshot
269
- } from 'audio-channel-queue';
270
- import { useState, useEffect } from 'react';
271
-
272
- function App(): JSX.Element {
273
- const [currentInfo, setCurrentInfo] = useState<AudioInfo | null>(null);
274
- const [queueSnapshot, setQueueSnapshot] = useState<QueueSnapshot | null>(null);
275
- const [isPaused, setIsPaused] = useState(false);
276
-
277
- useEffect(() => {
278
- // Set up background music with looping
279
- const setupBackgroundMusic = async () => {
280
- await queueAudio(backgroundMusic, 0, {
281
- loop: true,
282
- volume: 0.3
283
- });
284
- setChannelVolume(0, 0.3); // 30% volume for background
285
- };
286
-
287
- // Configure volume ducking for announcements
288
- setVolumeDucking({
289
- priorityChannel: 1, // Announcements on channel 1
290
- priorityVolume: 1.0, // Full volume for announcements
291
- duckingVolume: 0.1 // Reduce background to 10% during announcements
292
- });
293
-
294
- // Set up event listeners
295
- onAudioProgress(0, (info: AudioInfo) => {
296
- setCurrentInfo(info);
297
- });
298
-
299
- onQueueChange(0, (snapshot: QueueSnapshot) => {
300
- setQueueSnapshot(snapshot);
301
- });
302
-
303
- onAudioPause(0, (channelNumber, info) => {
304
- setIsPaused(true);
305
- console.log(`Channel ${channelNumber} paused: ${info.fileName}`);
306
- });
307
-
308
- onAudioResume(0, (channelNumber, info) => {
309
- setIsPaused(false);
310
- console.log(`Channel ${channelNumber} resumed: ${info.fileName}`);
311
- });
312
-
313
- setupBackgroundMusic();
314
- }, []);
315
-
316
- const playAnnouncement = async () => {
317
- // Priority queue - plays next, automatically ducks background music
318
- await queueAudioPriority(announcement, 1);
319
- };
320
-
321
- const toggleBackgroundMusic = async () => {
322
- if (isPaused) {
323
- await resumeChannel(0);
324
- } else {
325
- await pauseChannel(0);
326
- }
327
- };
328
-
329
- const adjustBackgroundVolume = (volume: number) => {
330
- setChannelVolume(0, volume);
331
- };
332
-
333
- const emergencyToggleAll = async () => {
334
- await togglePauseAllChannels(); // Smart toggle - pause all if any playing, resume all if all paused
335
- };
336
-
337
- return (
338
- <div className="App">
339
- <h2>Audio Control Center</h2>
340
-
341
- {/* Background Music Controls */}
342
- <div className="background-controls">
343
- <h3>Background Music</h3>
344
- <button onClick={toggleBackgroundMusic}>
345
- {isPaused ? '▢️ Resume' : '⏸️ Pause'}
346
- </button>
347
- <label>
348
- Volume:
349
- <input
350
- type="range"
351
- min="0"
352
- max="1"
353
- step="0.1"
354
- onChange={(e) => adjustBackgroundVolume(Number(e.target.value))}
355
- />
356
- </label>
357
- </div>
358
-
359
- {/* Announcement Controls */}
360
- <div className="announcement-controls">
361
- <h3>Announcements</h3>
362
- <button onClick={playAnnouncement}>
363
- πŸ“’ Play Announcement (Auto-ducks background)
364
- </button>
365
- </div>
366
-
367
- {/* Emergency Controls */}
368
- <div className="emergency-controls">
369
- <h3>Emergency Controls</h3>
370
- <button onClick={emergencyToggleAll}>
371
- 🚨 Toggle All Audio (Smart Pause/Resume)
372
- </button>
373
- </div>
374
-
375
- {/* Current Audio Info */}
376
- {currentInfo && (
377
- <div className="current-info">
378
- <h3>Now Playing:</h3>
379
- <p>🎡 {currentInfo.fileName}</p>
380
- <p>⏱️ {(currentInfo.currentTime / 1000).toFixed(1)}s / {(currentInfo.duration / 1000).toFixed(1)}s</p>
381
- <p>πŸ“Š Progress: {(currentInfo.progress * 100).toFixed(1)}%</p>
382
- <p>πŸ”Š Volume: {(currentInfo.volume * 100).toFixed(0)}%</p>
383
- <p>πŸ”„ Looping: {currentInfo.isLooping ? 'Yes' : 'No'}</p>
384
- <p>⏸️ Paused: {currentInfo.isPaused ? 'Yes' : 'No'}</p>
385
-
386
- {/* Progress Bar */}
387
- <div className="progress-bar">
388
- <div
389
- className="progress-fill"
390
- style={{ width: `${currentInfo.progress * 100}%` }}
391
- />
392
- </div>
393
- </div>
394
- )}
395
-
396
- {/* Queue Status */}
397
- {queueSnapshot && (
398
- <div className="queue-status">
399
- <h3>Queue Status (Channel {queueSnapshot.channelNumber}):</h3>
400
- <p>πŸ“‹ Total Items: {queueSnapshot.totalItems}</p>
401
- <p>πŸ”Š Channel Volume: {(queueSnapshot.volume * 100).toFixed(0)}%</p>
402
- <p>⏸️ Channel Paused: {queueSnapshot.isPaused ? 'Yes' : 'No'}</p>
403
-
404
- <h4>Queue Items:</h4>
405
- <ul>
406
- {queueSnapshot.items.map((item, index) => (
407
- <li key={index} style={{
408
- fontWeight: item.isCurrentlyPlaying ? 'bold' : 'normal',
409
- color: item.isCurrentlyPlaying ? '#007bff' : 'inherit'
410
- }}>
411
- {item.fileName} ({(item.duration / 1000).toFixed(1)}s)
412
- {item.isCurrentlyPlaying && ' ▢️ PLAYING'}
413
- {item.isLooping && ' πŸ”„ LOOP'}
414
- </li>
415
- ))}
416
- </ul>
417
- </div>
418
- )}
419
- </div>
420
- );
421
- }
422
-
423
- export default App;
353
+ ```typescript
354
+ // Subscribe to queue change events for visual updates.
355
+ onQueueChange(channelNumber, callback);
356
+ onQueueChange(0, (snapshot) => updateQueueDisplay(snapshot)); // Update UI on queue changes
424
357
  ```
425
358
 
426
- ### Advanced Usage Examples:
427
-
428
- #### Gaming Audio System
429
359
  ```typescript
430
- // Background music (channel 0)
431
- await queueAudio('./music/background.mp3', 0, { loop: true, volume: 0.4 });
432
-
433
- // Sound effects (channel 1)
434
- await queueAudio('./sfx/explosion.wav', 1);
435
-
436
- // Voice chat (channel 2) - ducks other audio
437
- setVolumeDucking({
438
- priorityChannel: 2,
439
- priorityVolume: 1.0,
440
- duckingVolume: 0.2
441
- });
442
-
443
- // Critical game announcements (priority)
444
- await queueAudioPriority('./voice/game-over.wav', 2);
360
+ // Subscribe to audio start events.
361
+ onAudioStart(channelNumber, callback);
362
+ onAudioStart(0, (info) => console.log(`Started: ${info.fileName}`)); // Log audio starts
445
363
  ```
446
364
 
447
- #### Podcast/Radio App
448
365
  ```typescript
449
- // Main content (channel 0)
450
- await queueAudio('./podcast/episode1.mp3', 0);
366
+ // Unsubscribe from audio start events (removes ALL start callbacks for the channel)
367
+ offAudioStart(channelNumber);
368
+ offAudioStart(0); // Stop receiving all start notifications for channel 0
369
+ ```
451
370
 
452
- // Jingles and ads (channel 1) - interrupt at natural breaks
453
- await queueAudioPriority('./ads/sponsor.mp3', 0);
371
+ ```typescript
372
+ // Subscribe to audio completion events.
373
+ onAudioComplete(channelNumber, callback);
374
+ onAudioComplete(0, (info) => logPlayHistory(info)); // Track completed audio
375
+ ```
454
376
 
455
- // Background ambient (channel 2)
456
- await queueAudio('./ambient/coffee-shop.mp3', 2, {
457
- loop: true,
458
- volume: 0.1
459
- });
377
+ ```typescript
378
+ // Unsubscribe from audio completion events (removes ALL complete callbacks for the channel)
379
+ offAudioComplete(channelNumber);
380
+ offAudioComplete(0); // Stop receiving all completion notifications for channel 0
381
+ ```
460
382
 
461
- // Pause everything for phone calls
462
- onPhoneCall(() => pauseAllChannels());
383
+ ```typescript
384
+ // Subscribe to audio pause events.
385
+ onAudioPause(channelNumber, callback);
386
+ onAudioPause(0, (info) => showPauseIcon(info)); // Show pause state in UI
463
387
  ```
464
388
 
465
- ### Legacy Access
466
- If you need to expose the queue array for logging or other purposes, it is available to you as well: `audioChannels`.
389
+ ```typescript
390
+ // Subscribe to audio resume events.
391
+ onAudioResume(channelNumber, callback);
392
+ onAudioResume(0, (info) => showPlayIcon(info)); // Show play state in UI
393
+ ```
467
394
 
468
395
  ### TypeScript Support
469
396
  If you cannot import audio files into your app, you may need a `custom.d.ts` file in the root directory. An example of one is shown here:
@@ -476,39 +403,10 @@ declare module '*.mp3' {
476
403
  }
477
404
  ```
478
405
 
479
- ## Features:
480
- - βœ… Queue management across multiple channels
481
- - βœ… Pause and resume functionality for individual channels and all channels
482
- - βœ… Volume control with per-channel precision (0-1 range)
483
- - βœ… Volume ducking for priority audio (automatic background reduction)
484
- - βœ… Audio looping capabilities for background music and ambient sounds
485
- - βœ… Priority queueing system for urgent audio playback
486
- - βœ… Real-time audio progress tracking with enhanced metadata
487
- - βœ… Comprehensive event system (start, complete, pause, resume, queue changes)
488
- - βœ… Audio duration and metadata extraction with loop and volume info
489
- - βœ… Automatic filename extraction from URLs
490
- - βœ… Queue state snapshots with pause and volume information
491
- - βœ… TypeScript support with full type definitions
492
- - βœ… Modular architecture with clean separation of concerns
493
- - βœ… Comprehensive JSDoc documentation with examples
494
- - βœ… Zero dependencies
495
- - βœ… Backward compatible with existing implementations
496
- - βœ… Comprehensive error handling with graceful degradation
497
-
498
406
  ## Development & Contributing πŸ› οΈ
499
407
 
500
408
  The package uses a modular TypeScript architecture that makes it easy to contribute and extend:
501
409
 
502
- ### File Structure:
503
- - **`src/types.ts`** - Interface definitions and type exports
504
- - **`src/core.ts`** - Main queue management logic and priority queueing
505
- - **`src/pause.ts`** - Pause and resume functionality
506
- - **`src/volume.ts`** - Volume control and ducking management
507
- - **`src/info.ts`** - Audio information and progress tracking
508
- - **`src/events.ts`** - Event system and callback management
509
- - **`src/utils.ts`** - Helper functions and utilities
510
- - **`src/index.ts`** - Public API exports
511
-
512
410
  ### Testing:
513
411
  The package includes a comprehensive test suite with 74+ tests covering all functionality:
514
412
 
@@ -522,5 +420,3 @@ npm run test:watch
522
420
  # Run tests with coverage report
523
421
  npm run test:coverage
524
422
  ```
525
-
526
- **Test Coverage**: 85%+ code coverage across all modules with realistic HTMLAudioElement mocking using jsdom.