0
# Event System
1
2
Comprehensive event handling system for lifecycle, playback, user interaction, and waveform state changes with subscription management and error handling.
3
4
## Capabilities
5
6
### Event Subscription and Management
7
8
Subscribe to events and manage event listeners with unsubscribe functionality.
9
10
```typescript { .api }
11
interface WaveSurfer {
12
/**
13
* Subscribe to an event with callback function
14
* @param event - Event name to listen for
15
* @param listener - Callback function to execute
16
* @param options - Optional subscription options
17
* @returns Unsubscribe function
18
*/
19
on<EventName extends keyof WaveSurferEvents>(
20
event: EventName,
21
listener: (...args: WaveSurferEvents[EventName]) => void,
22
options?: { once?: boolean }
23
): () => void;
24
25
/**
26
* Subscribe to an event only once
27
* @param event - Event name to listen for
28
* @param listener - Callback function to execute
29
* @returns Unsubscribe function
30
*/
31
once<EventName extends keyof WaveSurferEvents>(
32
event: EventName,
33
listener: (...args: WaveSurferEvents[EventName]) => void
34
): () => void;
35
36
/**
37
* Unsubscribe from an event
38
* @param event - Event name to unsubscribe from
39
* @param listener - Specific callback function to remove
40
*/
41
un<EventName extends keyof WaveSurferEvents>(
42
event: EventName,
43
listener: (...args: WaveSurferEvents[EventName]) => void
44
): void;
45
46
/**
47
* Clear all event listeners
48
*/
49
unAll(): void;
50
}
51
```
52
53
**Usage Examples:**
54
55
```typescript
56
// Basic event subscription
57
const unsubscribe = wavesurfer.on("ready", (duration) => {
58
console.log(`Audio loaded, duration: ${duration} seconds`);
59
});
60
61
// One-time event subscription
62
wavesurfer.once("play", () => {
63
console.log("First time playing!");
64
});
65
66
// Unsubscribe from specific event
67
const playHandler = () => console.log("Playing");
68
wavesurfer.on("play", playHandler);
69
wavesurfer.un("play", playHandler); // Remove specific handler
70
71
// Unsubscribe using returned function
72
const unsub = wavesurfer.on("pause", () => console.log("Paused"));
73
unsub(); // Remove this subscription
74
75
// Clear all event listeners
76
wavesurfer.unAll();
77
```
78
79
### Lifecycle Events
80
81
Events related to WaveSurfer initialization, loading, and destruction.
82
83
```typescript { .api }
84
interface WaveSurferEvents {
85
/** Fired after WaveSurfer instance is created and initialized */
86
init: [];
87
88
/** Fired when the audio is fully loaded and ready to play */
89
ready: [duration: number];
90
91
/** Fired just before the WaveSurfer instance is destroyed */
92
destroy: [];
93
}
94
```
95
96
**Usage Examples:**
97
98
```typescript
99
// Initialize event - setup UI
100
wavesurfer.on("init", () => {
101
console.log("WaveSurfer initialized");
102
document.getElementById("loading").style.display = "none";
103
});
104
105
// Ready event - enable playback controls
106
wavesurfer.on("ready", (duration) => {
107
console.log(`Audio ready, duration: ${duration.toFixed(2)}s`);
108
document.getElementById("play-button").disabled = false;
109
document.getElementById("duration").textContent = formatTime(duration);
110
});
111
112
// Destroy event - cleanup
113
wavesurfer.on("destroy", () => {
114
console.log("WaveSurfer destroyed, cleaning up");
115
document.getElementById("controls").innerHTML = "";
116
});
117
118
function formatTime(seconds) {
119
const mins = Math.floor(seconds / 60);
120
const secs = Math.floor(seconds % 60).toString().padStart(2, "0");
121
return `${mins}:${secs}`;
122
}
123
```
124
125
### Audio Loading Events
126
127
Events that track the audio loading and decoding process.
128
129
```typescript { .api }
130
interface WaveSurferEvents {
131
/** Fired when audio loading starts */
132
load: [url: string];
133
134
/** Fired during audio loading with progress percentage */
135
loading: [percent: number];
136
137
/** Fired when the audio has been decoded into waveform data */
138
decode: [duration: number];
139
}
140
```
141
142
**Usage Examples:**
143
144
```typescript
145
// Track loading process
146
wavesurfer.on("load", (url) => {
147
console.log(`Loading audio from: ${url}`);
148
showLoadingSpinner();
149
});
150
151
wavesurfer.on("loading", (percent) => {
152
console.log(`Loading progress: ${percent}%`);
153
updateProgressBar(percent);
154
});
155
156
wavesurfer.on("decode", (duration) => {
157
console.log(`Audio decoded, duration: ${duration}s`);
158
hideLoadingSpinner();
159
});
160
161
function showLoadingSpinner() {
162
document.getElementById("spinner").style.display = "block";
163
}
164
165
function updateProgressBar(percent) {
166
document.getElementById("progress-bar").style.width = `${percent}%`;
167
}
168
169
function hideLoadingSpinner() {
170
document.getElementById("spinner").style.display = "none";
171
document.getElementById("progress-bar").style.width = "0%";
172
}
173
```
174
175
### Playback Events
176
177
Events related to audio playback state changes and position updates.
178
179
```typescript { .api }
180
interface WaveSurferEvents {
181
/** Fired when audio playback starts */
182
play: [];
183
184
/** Fired when audio playback is paused */
185
pause: [];
186
187
/** Fired when audio playback finishes naturally */
188
finish: [];
189
190
/** Fired continuously during playback with current time */
191
timeupdate: [currentTime: number];
192
193
/** Alias for timeupdate, but only fired when audio is playing */
194
audioprocess: [currentTime: number];
195
196
/** Fired when user seeks to a new position */
197
seeking: [currentTime: number];
198
}
199
```
200
201
**Usage Examples:**
202
203
```typescript
204
// Playback state management
205
wavesurfer.on("play", () => {
206
document.getElementById("play-button").textContent = "Pause";
207
document.body.classList.add("playing");
208
});
209
210
wavesurfer.on("pause", () => {
211
document.getElementById("play-button").textContent = "Play";
212
document.body.classList.remove("playing");
213
});
214
215
wavesurfer.on("finish", () => {
216
document.getElementById("play-button").textContent = "Play";
217
document.body.classList.remove("playing");
218
console.log("Playback finished");
219
});
220
221
// Time tracking and display
222
wavesurfer.on("timeupdate", (currentTime) => {
223
const duration = wavesurfer.getDuration();
224
const progress = (currentTime / duration) * 100;
225
226
document.getElementById("current-time").textContent = formatTime(currentTime);
227
document.getElementById("progress").style.width = `${progress}%`;
228
});
229
230
// Seeking feedback
231
wavesurfer.on("seeking", (currentTime) => {
232
console.log(`Seeking to: ${formatTime(currentTime)}`);
233
showSeekingIndicator();
234
setTimeout(hideSeekingIndicator, 500);
235
});
236
```
237
238
### User Interaction Events
239
240
Events triggered by user interactions with the waveform.
241
242
```typescript { .api }
243
interface WaveSurferEvents {
244
/** Fired when user interacts with the waveform (clicks or drags) */
245
interaction: [newTime: number];
246
247
/** Fired when user clicks on the waveform */
248
click: [relativeX: number, relativeY: number];
249
250
/** Fired when user double-clicks on the waveform */
251
dblclick: [relativeX: number, relativeY: number];
252
253
/** Fired when user drags the cursor */
254
drag: [relativeX: number];
255
256
/** Fired when user starts dragging the cursor */
257
dragstart: [relativeX: number];
258
259
/** Fired when user ends dragging the cursor */
260
dragend: [relativeX: number];
261
}
262
```
263
264
**Usage Examples:**
265
266
```typescript
267
// General interaction tracking
268
wavesurfer.on("interaction", (newTime) => {
269
console.log(`User interacted, jumped to: ${formatTime(newTime)}`);
270
showInteractionFeedback();
271
});
272
273
// Click handling
274
wavesurfer.on("click", (relativeX, relativeY) => {
275
const duration = wavesurfer.getDuration();
276
const clickTime = relativeX * duration;
277
console.log(`Clicked at ${formatTime(clickTime)} (${(relativeX * 100).toFixed(1)}%)`);
278
});
279
280
// Double-click for special actions
281
wavesurfer.on("dblclick", (relativeX, relativeY) => {
282
const duration = wavesurfer.getDuration();
283
const clickTime = relativeX * duration;
284
console.log(`Double-clicked at ${formatTime(clickTime)}`);
285
286
// Example: Toggle playback or create marker
287
if (wavesurfer.isPlaying()) {
288
wavesurfer.pause();
289
} else {
290
wavesurfer.play();
291
}
292
});
293
294
// Drag interaction feedback
295
wavesurfer.on("dragstart", (relativeX) => {
296
document.body.classList.add("dragging");
297
console.log("Started dragging");
298
});
299
300
wavesurfer.on("drag", (relativeX) => {
301
const duration = wavesurfer.getDuration();
302
const dragTime = relativeX * duration;
303
document.getElementById("drag-time").textContent = formatTime(dragTime);
304
});
305
306
wavesurfer.on("dragend", (relativeX) => {
307
document.body.classList.remove("dragging");
308
document.getElementById("drag-time").textContent = "";
309
console.log("Finished dragging");
310
});
311
```
312
313
### Display and Rendering Events
314
315
Events related to waveform visualization and rendering updates.
316
317
```typescript { .api }
318
interface WaveSurferEvents {
319
/** Fired when the visible waveform is redrawn */
320
redraw: [];
321
322
/** Fired when all audio channel chunks have finished drawing */
323
redrawcomplete: [];
324
325
/** Fired when the waveform is scrolled/panned */
326
scroll: [visibleStartTime: number, visibleEndTime: number, scrollLeft: number, scrollRight: number];
327
328
/** Fired when the zoom level changes */
329
zoom: [minPxPerSec: number];
330
}
331
```
332
333
**Usage Examples:**
334
335
```typescript
336
// Rendering progress tracking
337
wavesurfer.on("redraw", () => {
338
console.log("Waveform redrawing...");
339
showRenderingIndicator();
340
});
341
342
wavesurfer.on("redrawcomplete", () => {
343
console.log("Waveform rendering complete");
344
hideRenderingIndicator();
345
});
346
347
// Scroll tracking for custom controls
348
wavesurfer.on("scroll", (startTime, endTime, scrollLeft, scrollRight) => {
349
console.log(`Viewing: ${formatTime(startTime)} - ${formatTime(endTime)}`);
350
updateScrollIndicator(scrollLeft, scrollRight);
351
});
352
353
// Zoom level updates
354
wavesurfer.on("zoom", (minPxPerSec) => {
355
console.log(`Zoom level: ${minPxPerSec} pixels/second`);
356
document.getElementById("zoom-level").textContent = `${minPxPerSec}px/s`;
357
updateZoomControls(minPxPerSec);
358
});
359
360
function updateScrollIndicator(scrollLeft, scrollRight) {
361
const totalWidth = wavesurfer.getWidth();
362
const viewportStart = (scrollLeft / totalWidth) * 100;
363
const viewportEnd = (scrollRight / totalWidth) * 100;
364
365
document.getElementById("scroll-indicator").style.left = `${viewportStart}%`;
366
document.getElementById("scroll-indicator").style.width = `${viewportEnd - viewportStart}%`;
367
}
368
```
369
370
### Error Handling
371
372
Handle errors that occur during audio loading, decoding, or playback.
373
374
```typescript { .api }
375
interface WaveSurferEvents {
376
/** Fired when an error occurs during loading, decoding, or playback */
377
error: [error: Error];
378
}
379
```
380
381
**Usage Examples:**
382
383
```typescript
384
// Comprehensive error handling
385
wavesurfer.on("error", (error) => {
386
console.error("WaveSurfer error:", error);
387
388
// Handle different error types
389
if (error.message.includes("fetch")) {
390
showError("Failed to load audio file. Please check the URL and try again.");
391
} else if (error.message.includes("decode")) {
392
showError("Unable to decode audio file. The file may be corrupted or in an unsupported format.");
393
} else if (error.message.includes("Media")) {
394
showError("Media playback error. Please try refreshing the page.");
395
} else {
396
showError(`An error occurred: ${error.message}`);
397
}
398
399
// Reset UI state
400
hideLoadingSpinner();
401
document.getElementById("play-button").disabled = true;
402
});
403
404
function showError(message) {
405
const errorDiv = document.getElementById("error-message");
406
errorDiv.textContent = message;
407
errorDiv.style.display = "block";
408
409
// Auto-hide after 5 seconds
410
setTimeout(() => {
411
errorDiv.style.display = "none";
412
}, 5000);
413
}
414
415
// Retry mechanism
416
let retryCount = 0;
417
const maxRetries = 3;
418
419
wavesurfer.on("error", async (error) => {
420
if (retryCount < maxRetries) {
421
retryCount++;
422
console.log(`Retrying... (${retryCount}/${maxRetries})`);
423
424
// Wait before retry
425
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
426
427
try {
428
await wavesurfer.load(originalUrl);
429
retryCount = 0; // Reset on success
430
} catch (retryError) {
431
console.error(`Retry ${retryCount} failed:`, retryError);
432
}
433
} else {
434
console.error("Max retries exceeded, giving up");
435
showError("Failed to load audio after multiple attempts.");
436
}
437
});
438
```
439
440
### Event Chaining and Complex Workflows
441
442
Combine multiple events for complex application workflows.
443
444
```typescript { .api }
445
// Events can be chained and combined for complex behaviors
446
```
447
448
**Usage Examples:**
449
450
```typescript
451
// Audio loading workflow
452
let loadingStartTime;
453
454
wavesurfer.on("load", (url) => {
455
loadingStartTime = Date.now();
456
console.log(`Starting to load: ${url}`);
457
});
458
459
wavesurfer.on("decode", () => {
460
const loadingTime = Date.now() - loadingStartTime;
461
console.log(`Audio decoded in ${loadingTime}ms`);
462
});
463
464
wavesurfer.on("ready", (duration) => {
465
const totalTime = Date.now() - loadingStartTime;
466
console.log(`Audio ready in ${totalTime}ms, duration: ${duration}s`);
467
468
// Auto-play if requested
469
if (shouldAutoPlay) {
470
wavesurfer.play();
471
}
472
});
473
474
// Playback session tracking
475
let playbackSessions = [];
476
let sessionStart;
477
478
wavesurfer.on("play", () => {
479
sessionStart = {
480
startTime: wavesurfer.getCurrentTime(),
481
timestamp: Date.now(),
482
};
483
});
484
485
wavesurfer.on("pause", () => {
486
if (sessionStart) {
487
playbackSessions.push({
488
...sessionStart,
489
endTime: wavesurfer.getCurrentTime(),
490
duration: Date.now() - sessionStart.timestamp,
491
});
492
}
493
});
494
495
wavesurfer.on("finish", () => {
496
console.log(`Total playback sessions: ${playbackSessions.length}`);
497
const totalListeningTime = playbackSessions.reduce(
498
(sum, session) => sum + (session.endTime - session.startTime), 0
499
);
500
console.log(`Total listening time: ${formatTime(totalListeningTime)}`);
501
});
502
```