audio-channel-queue 1.4.0 → 1.5.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 +249 -13
- package/dist/core.d.ts +53 -0
- package/dist/core.js +189 -0
- package/dist/events.d.ts +59 -0
- package/dist/events.js +162 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +47 -0
- package/dist/info.d.ts +122 -0
- package/dist/info.js +240 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.js +5 -0
- package/dist/utils.d.ts +52 -0
- package/dist/utils.js +106 -0
- package/package.json +7 -4
- package/src/core.ts +209 -0
- package/src/events.ts +189 -0
- package/src/index.ts +66 -0
- package/src/info.ts +262 -0
- package/src/types.ts +127 -0
- package/src/utils.ts +109 -0
- package/dist/audio.d.ts +0 -10
- package/dist/audio.js +0 -66
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Audio Channel Queue
|
|
2
|
-
The purpose of this package is to help queue audio files so they do not play on top of each other. You can also enqueue audio files to different queues. This allows you to play sounds concurrently, but not have them overlap in their given audio queue.
|
|
2
|
+
The purpose of this package is to help queue audio files so they do not play on top of each other. You can also enqueue audio files to different queues. This allows you to play sounds concurrently, but not have them overlap in their given audio queue. The package also supports real-time audio progress tracking, duration information, and playback metadata! 🎵📊
|
|
3
3
|
|
|
4
4
|
This package offers TypeScript support 📘, boasts zero dependencies 🚫, and is released under the MIT license 📜. As an added bonus, it's NON-GMO 🌱 and 100% Free Range Organic 🐓.
|
|
5
5
|
|
|
@@ -9,55 +9,291 @@ NPM package can be found [here](https://www.npmjs.com/package/audio-channel-queu
|
|
|
9
9
|
|
|
10
10
|
GitHub Repo can be found [here](https://github.com/tonycarpenter21/audio-channel-queue).
|
|
11
11
|
|
|
12
|
+
## Architecture & Code Organization 🏗️
|
|
13
|
+
|
|
14
|
+
Modular architecture for maintainability and extensibility:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
src/
|
|
18
|
+
├── index.ts # Main entry point with organized exports
|
|
19
|
+
├── types.ts # TypeScript interfaces and type definitions
|
|
20
|
+
├── core.ts # Core queue management functions
|
|
21
|
+
├── info.ts # Audio information and progress tracking
|
|
22
|
+
├── events.ts # Event handling and emission logic
|
|
23
|
+
└── utils.ts # Helper functions and utilities
|
|
24
|
+
```
|
|
25
|
+
|
|
12
26
|
## How To Install This Package:
|
|
13
27
|
Install this package by running either of these commands (typescript packages are included automatically):
|
|
14
28
|
- For npm run `npm install audio-channel-queue`
|
|
15
29
|
- For yarn run `yarn add audio-channel-queue`
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
|
|
31
|
+
## Basic Queue Management Functions:
|
|
32
|
+
|
|
33
|
+
### Queue Audio
|
|
34
|
+
```queueAudio(audioFileGoesHere, channelNumber);```
|
|
19
35
|
Use the `queueAudio()` function to add a file to the queue and start playing it automatically. It takes two arguments:
|
|
20
36
|
- The first argument is an imported sound file.
|
|
21
37
|
- The second argument is optional and it allows you to choose a different queue channel.
|
|
22
38
|
|
|
39
|
+
### Stop Current Audio
|
|
23
40
|
```stopCurrentAudioInChannel(queueChannelNumberGoesHere);```
|
|
24
41
|
Use the `stopCurrentAudioInChannel()` function to stop the current playback of a file in a queue and start playing the next one automatically. It takes one argument:
|
|
25
42
|
- The first argument is optional and it allows you to choose a different queue channel. If you are only using the default channel, just use `stopCurrentAudioInChannel()`.
|
|
26
43
|
|
|
44
|
+
### Stop All Audio in Channel
|
|
27
45
|
```stopAllAudioInChannel(queueChannelNumberGoesHere);```
|
|
28
46
|
Use the `stopAllAudioInChannel()` function to stop the current playback of all files in a queue and removes all enqueued files. It takes one argument:
|
|
29
47
|
- The first argument is optional and it allows you to choose a different queue channel. If you are only using the default channel, just use `stopAllAudioInChannel()`.
|
|
30
48
|
|
|
31
|
-
|
|
49
|
+
### Stop All Audio
|
|
50
|
+
```stopAllAudio();```
|
|
32
51
|
Use the `stopAllAudio()` function to stop the current playback of all files in all queues. It takes no arguments.
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
## Audio Information and Progress Tracking:
|
|
54
|
+
|
|
55
|
+
### AudioInfo Interface
|
|
56
|
+
The package now provides detailed information about audio playback through the `AudioInfo` interface:
|
|
57
|
+
```typescript
|
|
58
|
+
interface AudioInfo {
|
|
59
|
+
currentTime: number; // Current position in milliseconds
|
|
60
|
+
duration: number; // Total duration in milliseconds
|
|
61
|
+
fileName: string; // Extracted filename from URL
|
|
62
|
+
isPlaying: boolean; // Whether audio is currently playing
|
|
63
|
+
progress: number; // Progress as percentage (0-1)
|
|
64
|
+
src: string; // Audio file source URL
|
|
65
|
+
}
|
|
66
|
+
```
|
|
35
67
|
|
|
36
|
-
###
|
|
68
|
+
### Get Current Audio Info
|
|
69
|
+
```getCurrentAudioInfo(channelNumber);```
|
|
70
|
+
Get information about the currently playing audio in a specific channel. Returns `AudioInfo | null`.
|
|
71
|
+
- `channelNumber` (optional): The channel number (defaults to 0)
|
|
72
|
+
- Returns `null` if no audio is currently playing in the channel
|
|
37
73
|
|
|
38
|
-
|
|
74
|
+
### Get All Channels Info
|
|
75
|
+
```getAllChannelsInfo();```
|
|
76
|
+
Get audio information for all channels. Returns an array of `AudioInfo | null` objects.
|
|
77
|
+
- Returns an array where each index corresponds to a channel number
|
|
78
|
+
- `null` values indicate channels with no currently playing audio
|
|
79
|
+
|
|
80
|
+
### Queue State Management
|
|
81
|
+
```getQueueSnapshot(channelNumber);```
|
|
82
|
+
Get a complete snapshot of the queue state for a specific channel. Returns `QueueSnapshot | null`.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
interface QueueSnapshot {
|
|
86
|
+
channelNumber: number; // Channel this snapshot represents
|
|
87
|
+
totalItems: number; // Total items in queue
|
|
88
|
+
currentIndex: number; // Index of currently playing item
|
|
89
|
+
items: Array<{ // Array of queue items with metadata
|
|
90
|
+
src: string;
|
|
91
|
+
fileName: string;
|
|
92
|
+
duration: number;
|
|
93
|
+
isCurrentlyPlaying: boolean;
|
|
94
|
+
}>;
|
|
95
|
+
}
|
|
39
96
|
```
|
|
97
|
+
|
|
98
|
+
### Real-time Progress Tracking
|
|
99
|
+
```onAudioProgress(channelNumber, callback);```
|
|
100
|
+
Subscribe to real-time progress updates for a specific channel.
|
|
101
|
+
- `channelNumber`: The channel number to monitor
|
|
102
|
+
- `callback`: Function that receives `AudioInfo` updates
|
|
103
|
+
|
|
104
|
+
```offAudioProgress(channelNumber);```
|
|
105
|
+
Remove all progress listeners for a specific channel.
|
|
106
|
+
- `channelNumber`: The channel number to stop monitoring
|
|
107
|
+
|
|
108
|
+
### Queue Change Events
|
|
109
|
+
```onQueueChange(channelNumber, callback);```
|
|
110
|
+
Subscribe to queue change events for visual updates.
|
|
111
|
+
- Triggered when items are added, removed, or when playback moves to next item
|
|
112
|
+
- Perfect for updating UI queue displays
|
|
113
|
+
|
|
114
|
+
```offQueueChange(channelNumber);```
|
|
115
|
+
Remove queue change listeners for a specific channel.
|
|
116
|
+
|
|
117
|
+
### Audio Lifecycle Events
|
|
118
|
+
```onAudioStart(channelNumber, callback);```
|
|
119
|
+
Subscribe to audio start events.
|
|
120
|
+
- Triggered when audio begins playing (after metadata loads)
|
|
121
|
+
- Provides duration, filename, and source information
|
|
122
|
+
|
|
123
|
+
```onAudioComplete(channelNumber, callback);```
|
|
124
|
+
Subscribe to audio completion events.
|
|
125
|
+
- Triggered when audio finishes or is stopped
|
|
126
|
+
- Includes remaining queue count information
|
|
127
|
+
|
|
128
|
+
### Example Usage with Progress Tracking:
|
|
129
|
+
|
|
130
|
+
`App.tsx`
|
|
131
|
+
```typescript
|
|
40
132
|
import redTeamWins from './audio/red_team_wins.mp3';
|
|
41
|
-
import {
|
|
133
|
+
import {
|
|
134
|
+
queueAudio,
|
|
135
|
+
getCurrentAudioInfo,
|
|
136
|
+
onAudioProgress,
|
|
137
|
+
offAudioProgress,
|
|
138
|
+
onQueueChange,
|
|
139
|
+
onAudioStart,
|
|
140
|
+
onAudioComplete,
|
|
141
|
+
getQueueSnapshot,
|
|
142
|
+
AudioInfo,
|
|
143
|
+
QueueSnapshot
|
|
144
|
+
} from 'audio-channel-queue';
|
|
145
|
+
import { useState, useEffect } from 'react';
|
|
42
146
|
|
|
43
147
|
function App(): JSX.Element {
|
|
148
|
+
const [currentInfo, setCurrentInfo] = useState<AudioInfo | null>(null);
|
|
149
|
+
const [queueSnapshot, setQueueSnapshot] = useState<QueueSnapshot | null>(null);
|
|
150
|
+
const [isTracking, setIsTracking] = useState(false);
|
|
151
|
+
|
|
152
|
+
const playSound = () => {
|
|
153
|
+
queueAudio(redTeamWins);
|
|
154
|
+
|
|
155
|
+
// Get initial audio info
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
const info = getCurrentAudioInfo();
|
|
158
|
+
setCurrentInfo(info);
|
|
159
|
+
}, 100);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const startTracking = () => {
|
|
163
|
+
setIsTracking(true);
|
|
164
|
+
|
|
165
|
+
// Track real-time progress
|
|
166
|
+
onAudioProgress(0, (info: AudioInfo) => {
|
|
167
|
+
setCurrentInfo(info);
|
|
168
|
+
console.log(`Progress: ${(info.progress * 100).toFixed(1)}%`);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Track queue changes
|
|
172
|
+
onQueueChange(0, (snapshot: QueueSnapshot) => {
|
|
173
|
+
setQueueSnapshot(snapshot);
|
|
174
|
+
console.log(`Queue updated: ${snapshot.totalItems} items`);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Track audio lifecycle
|
|
178
|
+
onAudioStart(0, (info) => {
|
|
179
|
+
console.log(`Started playing: ${info.fileName}`);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
onAudioComplete(0, (info) => {
|
|
183
|
+
console.log(`Completed: ${info.fileName}, ${info.remainingInQueue} remaining`);
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const stopTracking = () => {
|
|
188
|
+
setIsTracking(false);
|
|
189
|
+
offAudioProgress(0);
|
|
190
|
+
offQueueChange(0);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const addMoreAudio = () => {
|
|
194
|
+
queueAudio('./sounds/notification.wav');
|
|
195
|
+
queueAudio('./sounds/alert.mp3');
|
|
196
|
+
};
|
|
44
197
|
|
|
45
198
|
return (
|
|
46
199
|
<div className="App">
|
|
47
|
-
<button onClick={
|
|
200
|
+
<button onClick={playSound}>Play Sound</button>
|
|
201
|
+
<button onClick={addMoreAudio}>Add More to Queue</button>
|
|
202
|
+
<button onClick={startTracking} disabled={isTracking}>
|
|
203
|
+
Start Progress Tracking
|
|
204
|
+
</button>
|
|
205
|
+
<button onClick={stopTracking} disabled={!isTracking}>
|
|
206
|
+
Stop Progress Tracking
|
|
207
|
+
</button>
|
|
208
|
+
|
|
209
|
+
{currentInfo && (
|
|
210
|
+
<div>
|
|
211
|
+
<h3>Now Playing:</h3>
|
|
212
|
+
<p>File: {currentInfo.fileName}</p>
|
|
213
|
+
<p>Duration: {(currentInfo.duration / 1000).toFixed(1)}s</p>
|
|
214
|
+
<p>Current Time: {(currentInfo.currentTime / 1000).toFixed(1)}s</p>
|
|
215
|
+
<p>Progress: {(currentInfo.progress * 100).toFixed(1)}%</p>
|
|
216
|
+
<p>Playing: {currentInfo.isPlaying ? 'Yes' : 'No'}</p>
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
{queueSnapshot && (
|
|
221
|
+
<div>
|
|
222
|
+
<h3>Queue Status:</h3>
|
|
223
|
+
<p>Channel: {queueSnapshot.channelNumber}</p>
|
|
224
|
+
<p>Total Items: {queueSnapshot.totalItems}</p>
|
|
225
|
+
<p>Current Index: {queueSnapshot.currentIndex}</p>
|
|
226
|
+
<ul>
|
|
227
|
+
{queueSnapshot.items.map((item, index) => (
|
|
228
|
+
<li key={index} style={{
|
|
229
|
+
fontWeight: item.isCurrentlyPlaying ? 'bold' : 'normal'
|
|
230
|
+
}}>
|
|
231
|
+
{item.fileName} ({(item.duration / 1000).toFixed(1)}s)
|
|
232
|
+
{item.isCurrentlyPlaying && ' ▶️'}
|
|
233
|
+
</li>
|
|
234
|
+
))}
|
|
235
|
+
</ul>
|
|
236
|
+
</div>
|
|
237
|
+
)}
|
|
48
238
|
</div>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
239
|
+
);
|
|
240
|
+
}
|
|
51
241
|
|
|
52
242
|
export default App;
|
|
53
243
|
```
|
|
54
244
|
|
|
245
|
+
### Legacy Access
|
|
246
|
+
If you need to expose the queue array for logging or other purposes, it is available to you as well: `audioChannels`.
|
|
247
|
+
|
|
248
|
+
### TypeScript Support
|
|
55
249
|
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:
|
|
56
250
|
|
|
57
251
|
`custom.d.ts`
|
|
58
|
-
```
|
|
252
|
+
```typescript
|
|
59
253
|
declare module '*.mp3' {
|
|
60
254
|
const src: string;
|
|
61
255
|
export default src;
|
|
62
256
|
}
|
|
63
|
-
```
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Features:
|
|
260
|
+
- ✅ Queue management across multiple channels
|
|
261
|
+
- ✅ Real-time audio progress tracking
|
|
262
|
+
- ✅ Audio duration and metadata extraction
|
|
263
|
+
- ✅ Automatic filename extraction from URLs
|
|
264
|
+
- ✅ Queue state snapshots and change events
|
|
265
|
+
- ✅ Audio lifecycle events (start/complete)
|
|
266
|
+
- ✅ TypeScript support with full type definitions
|
|
267
|
+
- ✅ Modular architecture with clean separation of concerns
|
|
268
|
+
- ✅ Comprehensive JSDoc documentation with examples
|
|
269
|
+
- ✅ Zero dependencies
|
|
270
|
+
- ✅ Backward compatible with existing implementations
|
|
271
|
+
- ✅ Comprehensive error handling
|
|
272
|
+
|
|
273
|
+
## Development & Contributing 🛠️
|
|
274
|
+
|
|
275
|
+
The package uses a modular TypeScript architecture that makes it easy to contribute and extend:
|
|
276
|
+
|
|
277
|
+
### File Structure:
|
|
278
|
+
- **`src/types.ts`** - Interface definitions and type exports
|
|
279
|
+
- **`src/core.ts`** - Main queue management logic
|
|
280
|
+
- **`src/info.ts`** - Audio information and progress tracking
|
|
281
|
+
- **`src/events.ts`** - Event system and callback management
|
|
282
|
+
- **`src/utils.ts`** - Helper functions and utilities
|
|
283
|
+
- **`src/index.ts`** - Public API exports
|
|
284
|
+
|
|
285
|
+
### Testing:
|
|
286
|
+
The package includes a comprehensive test suite with 64+ tests covering all functionality:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Run tests once
|
|
290
|
+
npm test
|
|
291
|
+
|
|
292
|
+
# Run tests in watch mode during development
|
|
293
|
+
npm run test:watch
|
|
294
|
+
|
|
295
|
+
# Run tests with coverage report
|
|
296
|
+
npm run test:coverage
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Test Coverage**: 85%+ code coverage across all modules with realistic HTMLAudioElement mocking using jsdom.
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Core queue management functions for the audio-channel-queue package
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Queues an audio file to a specific channel and starts playing if it's the first in queue
|
|
6
|
+
* @param audioUrl - The URL of the audio file to queue
|
|
7
|
+
* @param channelNumber - The channel number to queue the audio to (defaults to 0)
|
|
8
|
+
* @returns Promise that resolves when the audio is queued and starts playing (if first in queue)
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* await queueAudio('https://example.com/song.mp3', 0);
|
|
12
|
+
* await queueAudio('./sounds/notification.wav'); // Uses default channel 0
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare const queueAudio: (audioUrl: string, channelNumber?: number) => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Plays the audio queue for a specific channel
|
|
18
|
+
* @param channelNumber - The channel number to play
|
|
19
|
+
* @returns Promise that resolves when the current audio finishes playing
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* await playAudioQueue(0); // Play queue for channel 0
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare const playAudioQueue: (channelNumber: number) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Stops the currently playing audio in a specific channel and plays the next audio in queue
|
|
28
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* stopCurrentAudioInChannel(0); // Stop current audio in channel 0
|
|
32
|
+
* stopCurrentAudioInChannel(); // Stop current audio in default channel
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare const stopCurrentAudioInChannel: (channelNumber?: number) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Stops all audio in a specific channel and clears the entire queue
|
|
38
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* stopAllAudioInChannel(0); // Clear all audio in channel 0
|
|
42
|
+
* stopAllAudioInChannel(); // Clear all audio in default channel
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare const stopAllAudioInChannel: (channelNumber?: number) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Stops all audio across all channels and clears all queues
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* stopAllAudio(); // Emergency stop - clears everything
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare const stopAllAudio: () => void;
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Core queue 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.stopAllAudio = exports.stopAllAudioInChannel = exports.stopCurrentAudioInChannel = exports.playAudioQueue = exports.queueAudio = void 0;
|
|
16
|
+
const info_1 = require("./info");
|
|
17
|
+
const utils_1 = require("./utils");
|
|
18
|
+
const events_1 = require("./events");
|
|
19
|
+
/**
|
|
20
|
+
* Queues an audio file to a specific channel and starts playing if it's the first in queue
|
|
21
|
+
* @param audioUrl - The URL of the audio file to queue
|
|
22
|
+
* @param channelNumber - The channel number to queue the audio to (defaults to 0)
|
|
23
|
+
* @returns Promise that resolves when the audio is queued and starts playing (if first in queue)
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* await queueAudio('https://example.com/song.mp3', 0);
|
|
27
|
+
* await queueAudio('./sounds/notification.wav'); // Uses default channel 0
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
const queueAudio = (audioUrl_1, ...args_1) => __awaiter(void 0, [audioUrl_1, ...args_1], void 0, function* (audioUrl, channelNumber = 0) {
|
|
31
|
+
if (!info_1.audioChannels[channelNumber]) {
|
|
32
|
+
info_1.audioChannels[channelNumber] = {
|
|
33
|
+
audioCompleteCallbacks: new Set(),
|
|
34
|
+
audioStartCallbacks: new Set(),
|
|
35
|
+
progressCallbacks: new Map(),
|
|
36
|
+
queue: [],
|
|
37
|
+
queueChangeCallbacks: new Set()
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const audio = new Audio(audioUrl);
|
|
41
|
+
info_1.audioChannels[channelNumber].queue.push(audio);
|
|
42
|
+
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
43
|
+
if (info_1.audioChannels[channelNumber].queue.length === 1) {
|
|
44
|
+
(0, exports.playAudioQueue)(channelNumber);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
exports.queueAudio = queueAudio;
|
|
48
|
+
/**
|
|
49
|
+
* Plays the audio queue for a specific channel
|
|
50
|
+
* @param channelNumber - The channel number to play
|
|
51
|
+
* @returns Promise that resolves when the current audio finishes playing
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* await playAudioQueue(0); // Play queue for channel 0
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
58
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
59
|
+
if (channel.queue.length === 0)
|
|
60
|
+
return;
|
|
61
|
+
const currentAudio = channel.queue[0];
|
|
62
|
+
(0, events_1.setupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
let hasStarted = false;
|
|
65
|
+
let metadataLoaded = false;
|
|
66
|
+
let playStarted = false;
|
|
67
|
+
// Check if we should fire onAudioStart (both conditions met)
|
|
68
|
+
const tryFireAudioStart = () => {
|
|
69
|
+
if (!hasStarted && metadataLoaded && playStarted) {
|
|
70
|
+
hasStarted = true;
|
|
71
|
+
(0, events_1.emitAudioStart)(channelNumber, {
|
|
72
|
+
channelNumber,
|
|
73
|
+
duration: currentAudio.duration * 1000, // Now guaranteed to have valid duration
|
|
74
|
+
fileName: (0, utils_1.extractFileName)(currentAudio.src),
|
|
75
|
+
src: currentAudio.src
|
|
76
|
+
}, info_1.audioChannels);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
// Event handler for when metadata loads (duration becomes available)
|
|
80
|
+
const handleLoadedMetadata = () => {
|
|
81
|
+
metadataLoaded = true;
|
|
82
|
+
tryFireAudioStart();
|
|
83
|
+
};
|
|
84
|
+
// Event handler for when audio actually starts playing
|
|
85
|
+
const handlePlay = () => {
|
|
86
|
+
playStarted = true;
|
|
87
|
+
tryFireAudioStart();
|
|
88
|
+
};
|
|
89
|
+
// Event handler for when audio ends
|
|
90
|
+
const handleEnded = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
|
+
(0, events_1.emitAudioComplete)(channelNumber, {
|
|
92
|
+
channelNumber,
|
|
93
|
+
fileName: (0, utils_1.extractFileName)(currentAudio.src),
|
|
94
|
+
remainingInQueue: channel.queue.length - 1,
|
|
95
|
+
src: currentAudio.src
|
|
96
|
+
}, info_1.audioChannels);
|
|
97
|
+
// Clean up event listeners
|
|
98
|
+
currentAudio.removeEventListener('loadedmetadata', handleLoadedMetadata);
|
|
99
|
+
currentAudio.removeEventListener('play', handlePlay);
|
|
100
|
+
currentAudio.removeEventListener('ended', handleEnded);
|
|
101
|
+
(0, events_1.cleanupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
|
|
102
|
+
channel.queue.shift();
|
|
103
|
+
// Emit queue change after completion
|
|
104
|
+
setTimeout(() => (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels), 10);
|
|
105
|
+
yield (0, exports.playAudioQueue)(channelNumber);
|
|
106
|
+
resolve();
|
|
107
|
+
});
|
|
108
|
+
// Add event listeners
|
|
109
|
+
currentAudio.addEventListener('loadedmetadata', handleLoadedMetadata);
|
|
110
|
+
currentAudio.addEventListener('play', handlePlay);
|
|
111
|
+
currentAudio.addEventListener('ended', handleEnded);
|
|
112
|
+
// Check if metadata is already loaded (in case it loads before we add the listener)
|
|
113
|
+
if (currentAudio.readyState >= 1) { // HAVE_METADATA or higher
|
|
114
|
+
metadataLoaded = true;
|
|
115
|
+
}
|
|
116
|
+
currentAudio.play();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
exports.playAudioQueue = playAudioQueue;
|
|
120
|
+
/**
|
|
121
|
+
* Stops the currently playing audio in a specific channel and plays the next audio in queue
|
|
122
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* stopCurrentAudioInChannel(0); // Stop current audio in channel 0
|
|
126
|
+
* stopCurrentAudioInChannel(); // Stop current audio in default channel
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
const stopCurrentAudioInChannel = (channelNumber = 0) => {
|
|
130
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
131
|
+
if (channel && channel.queue.length > 0) {
|
|
132
|
+
const currentAudio = channel.queue[0];
|
|
133
|
+
(0, events_1.emitAudioComplete)(channelNumber, {
|
|
134
|
+
channelNumber,
|
|
135
|
+
fileName: (0, utils_1.extractFileName)(currentAudio.src),
|
|
136
|
+
remainingInQueue: channel.queue.length - 1,
|
|
137
|
+
src: currentAudio.src
|
|
138
|
+
}, info_1.audioChannels);
|
|
139
|
+
currentAudio.pause();
|
|
140
|
+
(0, events_1.cleanupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
|
|
141
|
+
channel.queue.shift();
|
|
142
|
+
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
143
|
+
(0, exports.playAudioQueue)(channelNumber);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
exports.stopCurrentAudioInChannel = stopCurrentAudioInChannel;
|
|
147
|
+
/**
|
|
148
|
+
* Stops all audio in a specific channel and clears the entire queue
|
|
149
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* stopAllAudioInChannel(0); // Clear all audio in channel 0
|
|
153
|
+
* stopAllAudioInChannel(); // Clear all audio in default channel
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
const stopAllAudioInChannel = (channelNumber = 0) => {
|
|
157
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
158
|
+
if (channel) {
|
|
159
|
+
if (channel.queue.length > 0) {
|
|
160
|
+
const currentAudio = channel.queue[0];
|
|
161
|
+
(0, events_1.emitAudioComplete)(channelNumber, {
|
|
162
|
+
channelNumber,
|
|
163
|
+
fileName: (0, utils_1.extractFileName)(currentAudio.src),
|
|
164
|
+
remainingInQueue: 0, // Will be 0 since we're clearing the queue
|
|
165
|
+
src: currentAudio.src
|
|
166
|
+
}, info_1.audioChannels);
|
|
167
|
+
currentAudio.pause();
|
|
168
|
+
(0, events_1.cleanupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
|
|
169
|
+
}
|
|
170
|
+
// Clean up all progress tracking for this channel
|
|
171
|
+
channel.queue.forEach(audio => (0, events_1.cleanupProgressTracking)(audio, channelNumber, info_1.audioChannels));
|
|
172
|
+
channel.queue = [];
|
|
173
|
+
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
exports.stopAllAudioInChannel = stopAllAudioInChannel;
|
|
177
|
+
/**
|
|
178
|
+
* Stops all audio across all channels and clears all queues
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* stopAllAudio(); // Emergency stop - clears everything
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
const stopAllAudio = () => {
|
|
185
|
+
info_1.audioChannels.forEach((_channel, index) => {
|
|
186
|
+
(0, exports.stopAllAudioInChannel)(index);
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
exports.stopAllAudio = stopAllAudio;
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Event handling and emission for the audio-channel-queue package
|
|
3
|
+
*/
|
|
4
|
+
import { AudioStartInfo, AudioCompleteInfo, ExtendedAudioQueueChannel } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Emits a queue change event to all registered listeners for a specific channel
|
|
7
|
+
* @param channelNumber - The channel number that experienced a queue change
|
|
8
|
+
* @param audioChannels - Array of audio channels
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* emitQueueChange(0, audioChannels); // Notifies all queue change listeners
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare const emitQueueChange: (channelNumber: number, audioChannels: ExtendedAudioQueueChannel[]) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Emits an audio start event to all registered listeners for a specific channel
|
|
17
|
+
* @param channelNumber - The channel number where audio started
|
|
18
|
+
* @param audioInfo - Information about the audio that started
|
|
19
|
+
* @param audioChannels - Array of audio channels
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* emitAudioStart(0, { src: 'song.mp3', fileName: 'song.mp3', duration: 180000, channelNumber: 0 }, audioChannels);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare const emitAudioStart: (channelNumber: number, audioInfo: AudioStartInfo, audioChannels: ExtendedAudioQueueChannel[]) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Emits an audio complete event to all registered listeners for a specific channel
|
|
28
|
+
* @param channelNumber - The channel number where audio completed
|
|
29
|
+
* @param audioInfo - Information about the audio that completed
|
|
30
|
+
* @param audioChannels - Array of audio channels
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* emitAudioComplete(0, { src: 'song.mp3', fileName: 'song.mp3', channelNumber: 0, remainingInQueue: 2 }, audioChannels);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare const emitAudioComplete: (channelNumber: number, audioInfo: AudioCompleteInfo, audioChannels: ExtendedAudioQueueChannel[]) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Sets up comprehensive progress tracking for an audio element
|
|
39
|
+
* @param audio - The HTML audio element to track
|
|
40
|
+
* @param channelNumber - The channel number this audio belongs to
|
|
41
|
+
* @param audioChannels - Array of audio channels
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const audioElement = new Audio('song.mp3');
|
|
45
|
+
* setupProgressTracking(audioElement, 0, audioChannels);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare const setupProgressTracking: (audio: HTMLAudioElement, channelNumber: number, audioChannels: ExtendedAudioQueueChannel[]) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Cleans up progress tracking for an audio element to prevent memory leaks
|
|
51
|
+
* @param audio - The HTML audio element to clean up
|
|
52
|
+
* @param channelNumber - The channel number this audio belongs to
|
|
53
|
+
* @param audioChannels - Array of audio channels
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* cleanupProgressTracking(audioElement, 0, audioChannels);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare const cleanupProgressTracking: (audio: HTMLAudioElement, channelNumber: number, audioChannels: ExtendedAudioQueueChannel[]) => void;
|