audioq 2.0.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/LICENSE +21 -0
- package/README.md +486 -0
- package/dist/core.d.ts +129 -0
- package/dist/core.js +591 -0
- package/dist/errors.d.ts +138 -0
- package/dist/errors.js +441 -0
- package/dist/events.d.ts +81 -0
- package/dist/events.js +217 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +119 -0
- package/dist/info.d.ts +224 -0
- package/dist/info.js +529 -0
- package/dist/pause.d.ts +170 -0
- package/dist/pause.js +467 -0
- package/dist/queue-manipulation.d.ts +104 -0
- package/dist/queue-manipulation.js +319 -0
- package/dist/types.d.ts +382 -0
- package/dist/types.js +55 -0
- package/dist/utils.d.ts +83 -0
- package/dist/utils.js +215 -0
- package/dist/volume.d.ts +162 -0
- package/dist/volume.js +644 -0
- package/dist/web-audio.d.ts +156 -0
- package/dist/web-audio.js +327 -0
- package/package.json +63 -0
- package/src/core.ts +698 -0
- package/src/errors.ts +467 -0
- package/src/events.ts +252 -0
- package/src/index.ts +162 -0
- package/src/info.ts +590 -0
- package/src/pause.ts +523 -0
- package/src/queue-manipulation.ts +378 -0
- package/src/types.ts +415 -0
- package/src/utils.ts +235 -0
- package/src/volume.ts +735 -0
- package/src/web-audio.ts +331 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Queue manipulation functions for the audioq package
|
|
4
|
+
* Provides advanced queue management including item removal, reordering, and clearing
|
|
5
|
+
*/
|
|
6
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
9
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
10
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
11
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
12
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.swapQueueItems = exports.getQueueLength = exports.getQueueItemInfo = exports.clearQueueAfterCurrent = exports.reorderQueue = exports.removeQueuedItem = void 0;
|
|
17
|
+
const info_1 = require("./info");
|
|
18
|
+
const events_1 = require("./events");
|
|
19
|
+
const events_2 = require("./events");
|
|
20
|
+
const utils_1 = require("./utils");
|
|
21
|
+
/**
|
|
22
|
+
* Removes a specific item from the queue by its slot number (0-based index)
|
|
23
|
+
* Cannot remove the currently playing item (index 0) - use stopCurrentAudioInChannel instead
|
|
24
|
+
* @param queuedSlotNumber - Zero-based index of the item to remove (must be > 0)
|
|
25
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
26
|
+
* @returns Promise resolving to operation result with success status and updated queue
|
|
27
|
+
* @throws Error if trying to remove currently playing item or invalid slot number
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* // Remove the second item in queue (index 1)
|
|
31
|
+
* const result = await removeQueuedItem(1, 0);
|
|
32
|
+
* if (result.success) {
|
|
33
|
+
* console.log(`Removed item, queue now has ${result.updatedQueue.totalItems} items`);
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* // Remove the third item from channel 1
|
|
37
|
+
* await removeQueuedItem(2, 1);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
const removeQueuedItem = (queuedSlotNumber_1, ...args_1) => __awaiter(void 0, [queuedSlotNumber_1, ...args_1], void 0, function* (queuedSlotNumber, channelNumber = 0) {
|
|
41
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
42
|
+
if (!channel) {
|
|
43
|
+
return {
|
|
44
|
+
error: `Channel ${channelNumber} does not exist`,
|
|
45
|
+
success: false
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (queuedSlotNumber < 0 || queuedSlotNumber >= channel.queue.length) {
|
|
49
|
+
return {
|
|
50
|
+
error: `Invalid slot number ${queuedSlotNumber}. Queue has ${channel.queue.length} items ` +
|
|
51
|
+
`(indices 0-${channel.queue.length - 1})`,
|
|
52
|
+
success: false
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (queuedSlotNumber === 0) {
|
|
56
|
+
return {
|
|
57
|
+
error: 'Cannot remove currently playing item (index 0). ' +
|
|
58
|
+
'Use stopCurrentAudioInChannel() instead',
|
|
59
|
+
success: false
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Remove the audio element from the queue
|
|
63
|
+
const removedAudio = channel.queue.splice(queuedSlotNumber, 1)[0];
|
|
64
|
+
// Clean up any progress tracking for the removed audio
|
|
65
|
+
(0, events_2.cleanupProgressTracking)(removedAudio, channelNumber, info_1.audioChannels);
|
|
66
|
+
// Emit queue change event
|
|
67
|
+
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
68
|
+
const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
exports.removeQueuedItem = removeQueuedItem;
|
|
75
|
+
/**
|
|
76
|
+
* Reorders a queue item by moving it from one position to another
|
|
77
|
+
* Cannot reorder the currently playing item (index 0)
|
|
78
|
+
* @param currentQueuedSlotNumber - Current zero-based index of the item to move (must be > 0)
|
|
79
|
+
* @param newQueuedSlotNumber - New zero-based index where the item should be placed (must be > 0)
|
|
80
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
81
|
+
* @returns Promise resolving to operation result with success status and updated queue
|
|
82
|
+
* @throws Error if trying to reorder currently playing item or invalid slot numbers
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* // Move item from position 2 to position 1 (make it play next)
|
|
86
|
+
* const result = await reorderQueue(2, 1, 0);
|
|
87
|
+
* if (result.success) {
|
|
88
|
+
* console.log('Item moved successfully');
|
|
89
|
+
* }
|
|
90
|
+
*
|
|
91
|
+
* // Move item from position 1 to end of queue
|
|
92
|
+
* await reorderQueue(1, 4, 0); // Assuming queue has 5+ items
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
const reorderQueue = (currentQueuedSlotNumber_1, newQueuedSlotNumber_1, ...args_1) => __awaiter(void 0, [currentQueuedSlotNumber_1, newQueuedSlotNumber_1, ...args_1], void 0, function* (currentQueuedSlotNumber, newQueuedSlotNumber, channelNumber = 0) {
|
|
96
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
97
|
+
if (!channel) {
|
|
98
|
+
return {
|
|
99
|
+
error: `Channel ${channelNumber} does not exist`,
|
|
100
|
+
success: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (currentQueuedSlotNumber < 0 || currentQueuedSlotNumber >= channel.queue.length) {
|
|
104
|
+
return {
|
|
105
|
+
error: `Invalid current slot number ${currentQueuedSlotNumber}. Queue has ` +
|
|
106
|
+
`${channel.queue.length} items (indices 0-${channel.queue.length - 1})`,
|
|
107
|
+
success: false
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (newQueuedSlotNumber < 0 || newQueuedSlotNumber >= channel.queue.length) {
|
|
111
|
+
return {
|
|
112
|
+
error: `Invalid new slot number ${newQueuedSlotNumber}. Queue has ` +
|
|
113
|
+
`${channel.queue.length} items (indices 0-${channel.queue.length - 1})`,
|
|
114
|
+
success: false
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (currentQueuedSlotNumber === 0) {
|
|
118
|
+
return {
|
|
119
|
+
error: 'Cannot reorder currently playing item (index 0). ' + 'Stop current audio first if needed',
|
|
120
|
+
success: false
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (newQueuedSlotNumber === 0) {
|
|
124
|
+
return {
|
|
125
|
+
error: 'Cannot move item to currently playing position (index 0). ' +
|
|
126
|
+
'Use queueAudioPriority() to add items to front of queue',
|
|
127
|
+
success: false
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (currentQueuedSlotNumber === newQueuedSlotNumber) {
|
|
131
|
+
// No change needed, but return success
|
|
132
|
+
const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// Remove the item from its current position
|
|
139
|
+
const audioToMove = channel.queue.splice(currentQueuedSlotNumber, 1)[0];
|
|
140
|
+
// Insert it at the new position
|
|
141
|
+
channel.queue.splice(newQueuedSlotNumber, 0, audioToMove);
|
|
142
|
+
// Emit queue change event
|
|
143
|
+
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
144
|
+
const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
|
|
145
|
+
return {
|
|
146
|
+
success: true,
|
|
147
|
+
updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
exports.reorderQueue = reorderQueue;
|
|
151
|
+
/**
|
|
152
|
+
* Clears all queued audio items after the currently playing item
|
|
153
|
+
* The current audio will continue playing but nothing will follow it
|
|
154
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
155
|
+
* @returns Promise resolving to operation result with success status and updated queue
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* // Let current song finish but clear everything after it
|
|
159
|
+
* const result = await clearQueueAfterCurrent(0);
|
|
160
|
+
* if (result.success) {
|
|
161
|
+
* console.log(`Cleared queue, current audio will be the last to play`);
|
|
162
|
+
* }
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
const clearQueueAfterCurrent = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (channelNumber = 0) {
|
|
166
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
167
|
+
if (!channel) {
|
|
168
|
+
// For empty/non-existent channels, we can consider this a successful no-op
|
|
169
|
+
// since there's nothing to clear anyway
|
|
170
|
+
return {
|
|
171
|
+
success: true,
|
|
172
|
+
updatedQueue: {
|
|
173
|
+
channelNumber,
|
|
174
|
+
currentIndex: -1,
|
|
175
|
+
isPaused: false,
|
|
176
|
+
items: [],
|
|
177
|
+
totalItems: 0,
|
|
178
|
+
volume: 1.0
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (channel.queue.length <= 1) {
|
|
183
|
+
// Nothing to clear - either empty queue or only current audio
|
|
184
|
+
const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
|
|
185
|
+
return {
|
|
186
|
+
success: true,
|
|
187
|
+
updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Clean up progress tracking for all items except the current one
|
|
191
|
+
for (let i = 1; i < channel.queue.length; i++) {
|
|
192
|
+
(0, events_2.cleanupProgressTracking)(channel.queue[i], channelNumber, info_1.audioChannels);
|
|
193
|
+
}
|
|
194
|
+
// Keep only the currently playing audio (index 0)
|
|
195
|
+
channel.queue = channel.queue.slice(0, 1);
|
|
196
|
+
// Emit queue change event
|
|
197
|
+
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
198
|
+
const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
|
|
199
|
+
return {
|
|
200
|
+
success: true,
|
|
201
|
+
updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
exports.clearQueueAfterCurrent = clearQueueAfterCurrent;
|
|
205
|
+
/**
|
|
206
|
+
* Gets information about a specific queue item by its slot number
|
|
207
|
+
* @param queueSlotNumber - Zero-based index of the queue item
|
|
208
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
209
|
+
* @returns QueueItem information or null if slot doesn't exist
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const itemInfo = getQueueItemInfo(1, 0);
|
|
213
|
+
* if (itemInfo) {
|
|
214
|
+
* console.log(`Next to play: ${itemInfo.fileName}`);
|
|
215
|
+
* console.log(`Duration: ${itemInfo.duration}ms`);
|
|
216
|
+
* }
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
const getQueueItemInfo = (queueSlotNumber, channelNumber = 0) => {
|
|
220
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
221
|
+
if (!channel || queueSlotNumber < 0 || queueSlotNumber >= channel.queue.length) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const audio = channel.queue[queueSlotNumber];
|
|
225
|
+
const audioInfo = (0, utils_1.getAudioInfoFromElement)(audio, channelNumber, info_1.audioChannels);
|
|
226
|
+
if (!audioInfo) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const { duration, fileName, isLooping, isPlaying, src, volume } = audioInfo;
|
|
230
|
+
return {
|
|
231
|
+
duration,
|
|
232
|
+
fileName,
|
|
233
|
+
isCurrentlyPlaying: queueSlotNumber === 0 && isPlaying,
|
|
234
|
+
isLooping,
|
|
235
|
+
src,
|
|
236
|
+
volume
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
exports.getQueueItemInfo = getQueueItemInfo;
|
|
240
|
+
/**
|
|
241
|
+
* Gets the current queue length for a specific channel
|
|
242
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
243
|
+
* @returns Number of items in the queue, or 0 if channel doesn't exist
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* const queueSize = getQueueLength(0);
|
|
247
|
+
* console.log(`Channel 0 has ${queueSize} items in queue`);
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
const getQueueLength = (channelNumber = 0) => {
|
|
251
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
252
|
+
return channel ? channel.queue.length : 0;
|
|
253
|
+
};
|
|
254
|
+
exports.getQueueLength = getQueueLength;
|
|
255
|
+
/**
|
|
256
|
+
* Swaps the positions of two queue items
|
|
257
|
+
* Cannot swap with the currently playing item (index 0)
|
|
258
|
+
* @param slotA - Zero-based index of first item to swap (must be > 0)
|
|
259
|
+
* @param slotB - Zero-based index of second item to swap (must be > 0)
|
|
260
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
261
|
+
* @returns Promise resolving to operation result with success status and updated queue
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* // Swap the second and third items in queue
|
|
265
|
+
* const result = await swapQueueItems(1, 2, 0);
|
|
266
|
+
* if (result.success) {
|
|
267
|
+
* console.log('Items swapped successfully');
|
|
268
|
+
* }
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
const swapQueueItems = (slotA_1, slotB_1, ...args_1) => __awaiter(void 0, [slotA_1, slotB_1, ...args_1], void 0, function* (slotA, slotB, channelNumber = 0) {
|
|
272
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
273
|
+
if (!channel) {
|
|
274
|
+
return {
|
|
275
|
+
error: `Channel ${channelNumber} does not exist`,
|
|
276
|
+
success: false
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
if (slotA < 0 || slotA >= channel.queue.length) {
|
|
280
|
+
return {
|
|
281
|
+
error: `Invalid slot A ${slotA}. Queue has ${channel.queue.length} items ` +
|
|
282
|
+
`(indices 0-${channel.queue.length - 1})`,
|
|
283
|
+
success: false
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (slotB < 0 || slotB >= channel.queue.length) {
|
|
287
|
+
return {
|
|
288
|
+
error: `Invalid slot B ${slotB}. Queue has ${channel.queue.length} items ` +
|
|
289
|
+
`(indices 0-${channel.queue.length - 1})`,
|
|
290
|
+
success: false
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (slotA === 0 || slotB === 0) {
|
|
294
|
+
return {
|
|
295
|
+
error: 'Cannot swap with currently playing item (index 0)',
|
|
296
|
+
success: false
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
if (slotA === slotB) {
|
|
300
|
+
// No change needed, but return success
|
|
301
|
+
const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
|
|
302
|
+
return {
|
|
303
|
+
success: true,
|
|
304
|
+
updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
// Swap the audio elements
|
|
308
|
+
const temp = channel.queue[slotA];
|
|
309
|
+
channel.queue[slotA] = channel.queue[slotB];
|
|
310
|
+
channel.queue[slotB] = temp;
|
|
311
|
+
// Emit queue change event
|
|
312
|
+
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
313
|
+
const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
|
|
314
|
+
return {
|
|
315
|
+
success: true,
|
|
316
|
+
updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
exports.swapQueueItems = swapQueueItems;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Type definitions for the audioq package
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Maximum number of audio channels allowed to prevent memory exhaustion
|
|
6
|
+
*/
|
|
7
|
+
export declare const MAX_CHANNELS: number;
|
|
8
|
+
/**
|
|
9
|
+
* Symbol used as a key for global (channel-wide) progress callbacks
|
|
10
|
+
* This avoids the need for `null as any` type assertions
|
|
11
|
+
*/
|
|
12
|
+
export declare const GLOBAL_PROGRESS_KEY: unique symbol;
|
|
13
|
+
/**
|
|
14
|
+
* Array of HTMLAudioElement objects representing an audio queue
|
|
15
|
+
*/
|
|
16
|
+
export type AudioQueue = HTMLAudioElement[];
|
|
17
|
+
/**
|
|
18
|
+
* Basic audio queue channel structure
|
|
19
|
+
*/
|
|
20
|
+
export interface AudioQueueChannel {
|
|
21
|
+
queue: AudioQueue;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Volume ducking configuration for channels
|
|
25
|
+
*/
|
|
26
|
+
export interface VolumeConfig {
|
|
27
|
+
/** Duration in milliseconds for volume duck transition (defaults to 250ms) */
|
|
28
|
+
duckTransitionDuration?: number;
|
|
29
|
+
/** Volume level for all other channels when priority channel is active (0-1) */
|
|
30
|
+
duckingVolume: number;
|
|
31
|
+
/** The channel number that should have priority */
|
|
32
|
+
priorityChannel: number;
|
|
33
|
+
/** Volume level for the priority channel (0-1) */
|
|
34
|
+
priorityVolume: number;
|
|
35
|
+
/** Duration in milliseconds for volume restore transition (defaults to 250ms) */
|
|
36
|
+
restoreTransitionDuration?: number;
|
|
37
|
+
/** Easing function for volume transitions (defaults to 'ease-out') */
|
|
38
|
+
transitionEasing?: EasingType;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Configuration options for queuing audio
|
|
42
|
+
*/
|
|
43
|
+
export interface AudioQueueOptions {
|
|
44
|
+
/** Whether to add this audio to the front of the queue (after currently playing) */
|
|
45
|
+
addToFront?: boolean;
|
|
46
|
+
/** Whether the audio should loop when it finishes */
|
|
47
|
+
loop?: boolean;
|
|
48
|
+
/** Maximum number of items allowed in the queue (defaults to unlimited) */
|
|
49
|
+
maxQueueSize?: number;
|
|
50
|
+
/** Volume level for this specific audio (0-1) */
|
|
51
|
+
volume?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Global queue configuration options
|
|
55
|
+
*/
|
|
56
|
+
export interface QueueConfig {
|
|
57
|
+
/** Default maximum queue size across all channels (defaults to unlimited) */
|
|
58
|
+
defaultMaxQueueSize?: number;
|
|
59
|
+
/** Whether to drop oldest items when queue is full (defaults to false - reject new items) */
|
|
60
|
+
dropOldestWhenFull?: boolean;
|
|
61
|
+
/** Whether to show warnings when queue limits are reached (defaults to true) */
|
|
62
|
+
showQueueWarnings?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Comprehensive audio information interface providing metadata about currently playing audio
|
|
66
|
+
*/
|
|
67
|
+
export interface AudioInfo {
|
|
68
|
+
/** Current playback position in milliseconds */
|
|
69
|
+
currentTime: number;
|
|
70
|
+
/** Total audio duration in milliseconds */
|
|
71
|
+
duration: number;
|
|
72
|
+
/** Extracted filename from the source URL */
|
|
73
|
+
fileName: string;
|
|
74
|
+
/** Whether the audio is set to loop */
|
|
75
|
+
isLooping: boolean;
|
|
76
|
+
/** Whether the audio is currently paused */
|
|
77
|
+
isPaused: boolean;
|
|
78
|
+
/** Whether the audio is currently playing */
|
|
79
|
+
isPlaying: boolean;
|
|
80
|
+
/** Playback progress as a decimal (0-1) */
|
|
81
|
+
progress: number;
|
|
82
|
+
/** Number of audio files remaining in the queue after current */
|
|
83
|
+
remainingInQueue: number;
|
|
84
|
+
/** Audio file source URL */
|
|
85
|
+
src: string;
|
|
86
|
+
/** Current volume level (0-1) */
|
|
87
|
+
volume: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Information provided when an audio file completes playback
|
|
91
|
+
*/
|
|
92
|
+
export interface AudioCompleteInfo {
|
|
93
|
+
/** Channel number where the audio completed */
|
|
94
|
+
channelNumber: number;
|
|
95
|
+
/** Extracted filename from the source URL */
|
|
96
|
+
fileName: string;
|
|
97
|
+
/** Number of audio files remaining in the queue after completion */
|
|
98
|
+
remainingInQueue: number;
|
|
99
|
+
/** Audio file source URL */
|
|
100
|
+
src: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Information provided when an audio file starts playing
|
|
104
|
+
*/
|
|
105
|
+
export interface AudioStartInfo {
|
|
106
|
+
/** Channel number where the audio is starting */
|
|
107
|
+
channelNumber: number;
|
|
108
|
+
/** Total audio duration in milliseconds */
|
|
109
|
+
duration: number;
|
|
110
|
+
/** Extracted filename from the source URL */
|
|
111
|
+
fileName: string;
|
|
112
|
+
/** Audio file source URL */
|
|
113
|
+
src: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Information about a single item in an audio queue
|
|
117
|
+
*/
|
|
118
|
+
export interface QueueItem {
|
|
119
|
+
/** Total audio duration in milliseconds */
|
|
120
|
+
duration: number;
|
|
121
|
+
/** Extracted filename from the source URL */
|
|
122
|
+
fileName: string;
|
|
123
|
+
/** Whether this item is currently playing */
|
|
124
|
+
isCurrentlyPlaying: boolean;
|
|
125
|
+
/** Whether this item is set to loop */
|
|
126
|
+
isLooping: boolean;
|
|
127
|
+
/** Audio file source URL */
|
|
128
|
+
src: string;
|
|
129
|
+
/** Volume level for this item (0-1) */
|
|
130
|
+
volume: number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Complete snapshot of a queue's current state
|
|
134
|
+
*/
|
|
135
|
+
export interface QueueSnapshot {
|
|
136
|
+
/** Channel number this snapshot represents */
|
|
137
|
+
channelNumber: number;
|
|
138
|
+
/** Zero-based index of the currently playing item */
|
|
139
|
+
currentIndex: number;
|
|
140
|
+
/** Whether the current audio is paused */
|
|
141
|
+
isPaused: boolean;
|
|
142
|
+
/** Array of audio items in the queue with their metadata */
|
|
143
|
+
items: QueueItem[];
|
|
144
|
+
/** Total number of items in the queue */
|
|
145
|
+
totalItems: number;
|
|
146
|
+
/** Current volume level for the channel (0-1) */
|
|
147
|
+
volume: number;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Information about a queue manipulation operation result
|
|
151
|
+
*/
|
|
152
|
+
export interface QueueManipulationResult {
|
|
153
|
+
/** Error message if operation failed */
|
|
154
|
+
error?: string;
|
|
155
|
+
/** Whether the operation was successful */
|
|
156
|
+
success: boolean;
|
|
157
|
+
/** The queue snapshot after the operation (if successful) */
|
|
158
|
+
updatedQueue?: QueueSnapshot;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Callback function type for audio progress updates
|
|
162
|
+
* @param info Current audio information
|
|
163
|
+
*/
|
|
164
|
+
export type ProgressCallback = (info: AudioInfo) => void;
|
|
165
|
+
/**
|
|
166
|
+
* Callback function type for queue change notifications
|
|
167
|
+
* @param queueSnapshot Current state of the queue
|
|
168
|
+
*/
|
|
169
|
+
export type QueueChangeCallback = (queueSnapshot: QueueSnapshot) => void;
|
|
170
|
+
/**
|
|
171
|
+
* Callback function type for audio start notifications
|
|
172
|
+
* @param audioInfo Information about the audio that started
|
|
173
|
+
*/
|
|
174
|
+
export type AudioStartCallback = (audioInfo: AudioStartInfo) => void;
|
|
175
|
+
/**
|
|
176
|
+
* Callback function type for audio complete notifications
|
|
177
|
+
* @param audioInfo Information about the audio that completed
|
|
178
|
+
*/
|
|
179
|
+
export type AudioCompleteCallback = (audioInfo: AudioCompleteInfo) => void;
|
|
180
|
+
/**
|
|
181
|
+
* Callback function type for audio pause notifications
|
|
182
|
+
* @param channelNumber Channel that was paused
|
|
183
|
+
* @param audioInfo Information about the audio that was paused
|
|
184
|
+
*/
|
|
185
|
+
export type AudioPauseCallback = (channelNumber: number, audioInfo: AudioInfo) => void;
|
|
186
|
+
/**
|
|
187
|
+
* Callback function type for audio resume notifications
|
|
188
|
+
* @param channelNumber Channel that was resumed
|
|
189
|
+
* @param audioInfo Information about the audio that was resumed
|
|
190
|
+
*/
|
|
191
|
+
export type AudioResumeCallback = (channelNumber: number, audioInfo: AudioInfo) => void;
|
|
192
|
+
/**
|
|
193
|
+
* Types of audio errors that can occur during playback
|
|
194
|
+
*/
|
|
195
|
+
export declare enum AudioErrorType {
|
|
196
|
+
Abort = "abort",
|
|
197
|
+
Decode = "decode",
|
|
198
|
+
Network = "network",
|
|
199
|
+
Permission = "permission",
|
|
200
|
+
Timeout = "timeout",
|
|
201
|
+
Unknown = "unknown",
|
|
202
|
+
Unsupported = "unsupported"
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Information about an audio error that occurred during playback or loading
|
|
206
|
+
*/
|
|
207
|
+
export interface AudioErrorInfo {
|
|
208
|
+
/** Channel number where the error occurred */
|
|
209
|
+
channelNumber: number;
|
|
210
|
+
/** The actual error object that was thrown */
|
|
211
|
+
error: Error;
|
|
212
|
+
/** Categorized type of error for handling different scenarios */
|
|
213
|
+
errorType: AudioErrorType;
|
|
214
|
+
/** Extracted filename from the source URL */
|
|
215
|
+
fileName: string;
|
|
216
|
+
/** Number of audio files remaining in the queue after this error */
|
|
217
|
+
remainingInQueue: number;
|
|
218
|
+
/** Current retry attempt number (if retrying is enabled) */
|
|
219
|
+
retryAttempt?: number;
|
|
220
|
+
/** Audio file source URL that failed */
|
|
221
|
+
src: string;
|
|
222
|
+
/** Unix timestamp when the error occurred */
|
|
223
|
+
timestamp: number;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Configuration for automatic retry behavior when audio fails to load or play
|
|
227
|
+
*/
|
|
228
|
+
export interface RetryConfig {
|
|
229
|
+
/** Initial delay in milliseconds before first retry attempt */
|
|
230
|
+
baseDelay: number;
|
|
231
|
+
/** Whether automatic retries are enabled for this channel */
|
|
232
|
+
enabled: boolean;
|
|
233
|
+
/** Whether to use exponential backoff (doubling delay each retry) */
|
|
234
|
+
exponentialBackoff: boolean;
|
|
235
|
+
/** Alternative URLs to try if the primary source fails */
|
|
236
|
+
fallbackUrls: string[];
|
|
237
|
+
/** Maximum number of retry attempts before giving up */
|
|
238
|
+
maxRetries: number;
|
|
239
|
+
/** Whether to skip to next track in queue if all retries fail */
|
|
240
|
+
skipOnFailure: boolean;
|
|
241
|
+
/** Timeout in milliseconds for each individual retry attempt */
|
|
242
|
+
timeoutMs: number;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Configuration options for error recovery mechanisms across the audio system
|
|
246
|
+
*/
|
|
247
|
+
export interface ErrorRecoveryOptions {
|
|
248
|
+
/** Whether to automatically retry failed audio loads/plays */
|
|
249
|
+
autoRetry: boolean;
|
|
250
|
+
/** Whether to automatically skip to next track when current fails */
|
|
251
|
+
fallbackToNextTrack: boolean;
|
|
252
|
+
/** Whether to send error data to analytics systems */
|
|
253
|
+
logErrorsToAnalytics: boolean;
|
|
254
|
+
/** Whether to maintain queue integrity when errors occur */
|
|
255
|
+
preserveQueueOnError: boolean;
|
|
256
|
+
/** Whether to display user-visible error feedback */
|
|
257
|
+
showUserFeedback: boolean;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Callback function type for audio error events
|
|
261
|
+
*/
|
|
262
|
+
export type AudioErrorCallback = (errorInfo: AudioErrorInfo) => void;
|
|
263
|
+
/**
|
|
264
|
+
* Web Audio API configuration options
|
|
265
|
+
*/
|
|
266
|
+
export interface WebAudioConfig {
|
|
267
|
+
/** Whether to automatically use Web Audio API on iOS devices */
|
|
268
|
+
autoDetectIOS: boolean;
|
|
269
|
+
/** Whether Web Audio API support is enabled */
|
|
270
|
+
enabled: boolean;
|
|
271
|
+
/** Whether to force Web Audio API usage on all devices */
|
|
272
|
+
forceWebAudio: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Web Audio API support information
|
|
276
|
+
*/
|
|
277
|
+
export interface WebAudioSupport {
|
|
278
|
+
/** Whether Web Audio API is available in the current environment */
|
|
279
|
+
available: boolean;
|
|
280
|
+
/** Whether the current device is iOS */
|
|
281
|
+
isIOS: boolean;
|
|
282
|
+
/** Whether Web Audio API is currently being used */
|
|
283
|
+
usingWebAudio: boolean;
|
|
284
|
+
/** Reason for current Web Audio API usage state */
|
|
285
|
+
reason: string;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Web Audio API node set for audio element control
|
|
289
|
+
*/
|
|
290
|
+
export interface WebAudioNodeSet {
|
|
291
|
+
/** Gain node for volume control */
|
|
292
|
+
gainNode: GainNode;
|
|
293
|
+
/** Media element source node */
|
|
294
|
+
sourceNode: MediaElementAudioSourceNode;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Extended audio channel with comprehensive queue management, callback support, and state tracking
|
|
298
|
+
*/
|
|
299
|
+
export interface ExtendedAudioQueueChannel {
|
|
300
|
+
/** Set of callbacks triggered when audio completes playback */
|
|
301
|
+
audioCompleteCallbacks: Set<AudioCompleteCallback>;
|
|
302
|
+
/** Set of callbacks triggered when audio errors occur */
|
|
303
|
+
audioErrorCallbacks: Set<AudioErrorCallback>;
|
|
304
|
+
/** Set of callbacks triggered when audio is paused */
|
|
305
|
+
audioPauseCallbacks: Set<AudioPauseCallback>;
|
|
306
|
+
/** Set of callbacks triggered when audio is resumed */
|
|
307
|
+
audioResumeCallbacks: Set<AudioResumeCallback>;
|
|
308
|
+
/** Set of callbacks triggered when audio starts playing */
|
|
309
|
+
audioStartCallbacks: Set<AudioStartCallback>;
|
|
310
|
+
/** Current fade state if pause/resume with fade is active */
|
|
311
|
+
fadeState?: ChannelFadeState;
|
|
312
|
+
/** Whether the channel is currently paused */
|
|
313
|
+
isPaused: boolean;
|
|
314
|
+
/** Active operation lock to prevent race conditions */
|
|
315
|
+
isLocked?: boolean;
|
|
316
|
+
/** Maximum allowed queue size for this channel */
|
|
317
|
+
maxQueueSize?: number;
|
|
318
|
+
/** Map of progress callbacks keyed by audio element or global symbol */
|
|
319
|
+
progressCallbacks: Map<HTMLAudioElement | typeof GLOBAL_PROGRESS_KEY, Set<ProgressCallback>>;
|
|
320
|
+
/** Array of HTMLAudioElement objects in the queue */
|
|
321
|
+
queue: HTMLAudioElement[];
|
|
322
|
+
/** Set of callbacks triggered when the queue changes */
|
|
323
|
+
queueChangeCallbacks: Set<QueueChangeCallback>;
|
|
324
|
+
/** Retry configuration for failed audio loads/plays */
|
|
325
|
+
retryConfig?: RetryConfig;
|
|
326
|
+
/** Current volume level for the channel (0-1) */
|
|
327
|
+
volume: number;
|
|
328
|
+
/** Web Audio API context for this channel */
|
|
329
|
+
webAudioContext?: AudioContext;
|
|
330
|
+
/** Map of Web Audio API nodes for each audio element */
|
|
331
|
+
webAudioNodes?: Map<HTMLAudioElement, WebAudioNodeSet>;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Easing function types for smooth volume transitions and animations
|
|
335
|
+
*/
|
|
336
|
+
export declare enum EasingType {
|
|
337
|
+
Linear = "linear",
|
|
338
|
+
EaseIn = "ease-in",
|
|
339
|
+
EaseOut = "ease-out",
|
|
340
|
+
EaseInOut = "ease-in-out"
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Predefined fade types for pause/resume operations with different transition characteristics
|
|
344
|
+
*/
|
|
345
|
+
export declare enum FadeType {
|
|
346
|
+
Linear = "linear",
|
|
347
|
+
Gentle = "gentle",
|
|
348
|
+
Dramatic = "dramatic"
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Timer implementation types used for volume transitions to ensure proper cleanup
|
|
352
|
+
*/
|
|
353
|
+
export declare enum TimerType {
|
|
354
|
+
RequestAnimationFrame = "raf",
|
|
355
|
+
Timeout = "timeout"
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Configuration for fade transitions
|
|
359
|
+
*/
|
|
360
|
+
export interface FadeConfig {
|
|
361
|
+
/** Duration in milliseconds for the fade transition */
|
|
362
|
+
duration: number;
|
|
363
|
+
/** Easing curve to use when pausing (fading out) */
|
|
364
|
+
pauseCurve: EasingType;
|
|
365
|
+
/** Easing curve to use when resuming (fading in) */
|
|
366
|
+
resumeCurve: EasingType;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Internal fade state tracking for pause/resume with fade functionality
|
|
370
|
+
*/
|
|
371
|
+
export interface ChannelFadeState {
|
|
372
|
+
/** The original volume level before fading began */
|
|
373
|
+
originalVolume: number;
|
|
374
|
+
/** The type of fade being used */
|
|
375
|
+
fadeType: FadeType;
|
|
376
|
+
/** Whether the channel is currently paused due to fade */
|
|
377
|
+
isPaused: boolean;
|
|
378
|
+
/** Custom duration in milliseconds if specified (overrides fade type default) */
|
|
379
|
+
customDuration?: number;
|
|
380
|
+
/** Whether the channel is currently transitioning (during any fade operation) to prevent capturing intermediate volumes during rapid pause/resume toggles */
|
|
381
|
+
isTransitioning?: boolean;
|
|
382
|
+
}
|