wavesurfer.js 7.0.0-beta.5 → 7.0.0-beta.6

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.
Files changed (52) hide show
  1. package/README.md +1 -5
  2. package/dist/cypress/support/commands.d.ts +1 -0
  3. package/dist/cypress/support/commands.js +39 -0
  4. package/dist/cypress/support/e2e.d.ts +1 -0
  5. package/dist/cypress/support/e2e.js +18 -0
  6. package/dist/cypress.config.d.ts +2 -0
  7. package/dist/cypress.config.js +10 -0
  8. package/dist/decoder.d.ts +1 -1
  9. package/dist/draggable.d.ts +1 -0
  10. package/dist/draggable.js +57 -0
  11. package/dist/plugins/envelope.js +4 -25
  12. package/dist/plugins/envelope.min.js +1 -1
  13. package/dist/plugins/minimap.min.js +1 -1
  14. package/dist/plugins/regions.js +13 -51
  15. package/dist/plugins/regions.min.js +1 -1
  16. package/dist/plugins/timeline.min.js +1 -1
  17. package/dist/renderer.d.ts +1 -0
  18. package/dist/renderer.js +64 -66
  19. package/dist/src/base-plugin.d.ts +13 -0
  20. package/dist/src/base-plugin.js +22 -0
  21. package/dist/src/decoder.d.ts +9 -0
  22. package/dist/src/decoder.js +48 -0
  23. package/dist/src/event-emitter.d.ts +19 -0
  24. package/dist/src/event-emitter.js +45 -0
  25. package/dist/src/fetcher.d.ts +5 -0
  26. package/dist/src/fetcher.js +7 -0
  27. package/dist/src/player.d.ts +45 -0
  28. package/dist/src/player.js +114 -0
  29. package/dist/src/plugins/envelope.d.ts +71 -0
  30. package/dist/src/plugins/envelope.js +350 -0
  31. package/dist/src/plugins/minimap.d.ts +39 -0
  32. package/dist/src/plugins/minimap.js +117 -0
  33. package/dist/src/plugins/record.d.ts +26 -0
  34. package/dist/src/plugins/record.js +124 -0
  35. package/dist/src/plugins/regions.d.ts +93 -0
  36. package/dist/src/plugins/regions.js +395 -0
  37. package/dist/src/plugins/spectrogram-fft.d.ts +9 -0
  38. package/dist/src/plugins/spectrogram-fft.js +150 -0
  39. package/dist/src/plugins/spectrogram.d.ts +69 -0
  40. package/dist/src/plugins/spectrogram.js +340 -0
  41. package/dist/src/plugins/timeline.d.ts +45 -0
  42. package/dist/src/plugins/timeline.js +162 -0
  43. package/dist/src/renderer.d.ts +41 -0
  44. package/dist/src/renderer.js +420 -0
  45. package/dist/src/timer.d.ts +11 -0
  46. package/dist/src/timer.js +19 -0
  47. package/dist/src/wavesurfer.d.ts +149 -0
  48. package/dist/src/wavesurfer.js +229 -0
  49. package/dist/wavesurfer.d.ts +5 -1
  50. package/dist/wavesurfer.js +2 -8
  51. package/dist/wavesurfer.min.js +1 -1
  52. package/package.json +4 -1
@@ -0,0 +1,420 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ class Renderer extends EventEmitter {
3
+ static MAX_CANVAS_WIDTH = 4000;
4
+ options;
5
+ container;
6
+ scrollContainer;
7
+ wrapper;
8
+ canvasWrapper;
9
+ progressWrapper;
10
+ cursor;
11
+ timeouts = [];
12
+ isScrolling = false;
13
+ audioData = null;
14
+ resizeObserver = null;
15
+ isDragging = false;
16
+ constructor(options) {
17
+ super();
18
+ this.options = options;
19
+ let container;
20
+ if (typeof options.container === 'string') {
21
+ container = document.querySelector(options.container);
22
+ }
23
+ else if (options.container instanceof HTMLElement) {
24
+ container = options.container;
25
+ }
26
+ if (!container) {
27
+ throw new Error('Container not found');
28
+ }
29
+ const [div, shadow] = this.initHtml();
30
+ container.appendChild(div);
31
+ this.container = div;
32
+ this.scrollContainer = shadow.querySelector('.scroll');
33
+ this.wrapper = shadow.querySelector('.wrapper');
34
+ this.canvasWrapper = shadow.querySelector('.canvases');
35
+ this.progressWrapper = shadow.querySelector('.progress');
36
+ this.cursor = shadow.querySelector('.cursor');
37
+ this.initEvents();
38
+ }
39
+ initEvents() {
40
+ // Add a click listener
41
+ this.wrapper.addEventListener('click', (e) => {
42
+ const rect = this.wrapper.getBoundingClientRect();
43
+ const x = e.clientX - rect.left;
44
+ const relativeX = x / rect.width;
45
+ this.emit('click', relativeX);
46
+ });
47
+ // Drag
48
+ this.initDrag();
49
+ // Add a scroll listener
50
+ this.scrollContainer.addEventListener('scroll', () => {
51
+ const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
52
+ const startX = scrollLeft / scrollWidth;
53
+ const endX = (scrollLeft + clientWidth) / scrollWidth;
54
+ this.emit('scroll', startX, endX);
55
+ });
56
+ // Re-render the waveform on container resize
57
+ const delay = this.createDelay(100);
58
+ this.resizeObserver = new ResizeObserver(() => {
59
+ delay(() => this.reRender());
60
+ });
61
+ this.resizeObserver.observe(this.scrollContainer);
62
+ }
63
+ initDrag() {
64
+ this.wrapper.addEventListener('mousedown', (e) => {
65
+ const minDx = 5;
66
+ const x = e.clientX;
67
+ const move = (e) => {
68
+ const diff = Math.abs(e.clientX - x);
69
+ if (diff >= minDx) {
70
+ this.isDragging = true;
71
+ const rect = this.wrapper.getBoundingClientRect();
72
+ this.emit('drag', Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)));
73
+ }
74
+ };
75
+ const up = () => {
76
+ document.removeEventListener('mousemove', move);
77
+ document.removeEventListener('mouseup', up);
78
+ this.isDragging = false;
79
+ };
80
+ document.addEventListener('mousemove', move);
81
+ document.addEventListener('mouseup', up);
82
+ });
83
+ }
84
+ initHtml() {
85
+ const div = document.createElement('div');
86
+ const shadow = div.attachShadow({ mode: 'open' });
87
+ shadow.innerHTML = `
88
+ <style>
89
+ :host {
90
+ user-select: none;
91
+ }
92
+ :host .scroll {
93
+ overflow-x: auto;
94
+ overflow-y: hidden;
95
+ width: 100%;
96
+ position: relative;
97
+ }
98
+ :host .noScrollbar {
99
+ scrollbar-color: transparent;
100
+ scrollbar-width: none;
101
+ }
102
+ :host .noScrollbar::-webkit-scrollbar {
103
+ display: none;
104
+ -webkit-appearance: none;
105
+ }
106
+ :host .wrapper {
107
+ position: relative;
108
+ overflow: visible;
109
+ z-index: 2;
110
+ }
111
+ :host .canvases {
112
+ min-height: ${this.options.height}px;
113
+ }
114
+ :host .canvases > div {
115
+ position: relative;
116
+ }
117
+ :host canvas {
118
+ display: block;
119
+ position: absolute;
120
+ top: 0;
121
+ image-rendering: crisp-edges;
122
+ }
123
+ :host .progress {
124
+ pointer-events: none;
125
+ position: absolute;
126
+ z-index: 2;
127
+ top: 0;
128
+ left: 0;
129
+ width: 0;
130
+ height: 100%;
131
+ overflow: hidden;
132
+ }
133
+ :host .progress > div {
134
+ position: relative;
135
+ }
136
+ :host .cursor {
137
+ pointer-events: none;
138
+ position: absolute;
139
+ z-index: 5;
140
+ top: 0;
141
+ left: 0;
142
+ height: 100%;
143
+ border-radius: 2px;
144
+ }
145
+ </style>
146
+
147
+ <div class="scroll" part="scroll">
148
+ <div class="wrapper">
149
+ <div class="canvases"></div>
150
+ <div class="progress"></div>
151
+ <div class="cursor" part="cursor"></div>
152
+ </div>
153
+ </div>
154
+ `;
155
+ return [div, shadow];
156
+ }
157
+ setOptions(options) {
158
+ this.options = options;
159
+ // Re-render the waveform
160
+ this.reRender();
161
+ }
162
+ getWrapper() {
163
+ return this.wrapper;
164
+ }
165
+ getScroll() {
166
+ return this.scrollContainer.scrollLeft;
167
+ }
168
+ destroy() {
169
+ this.container.remove();
170
+ this.resizeObserver?.disconnect();
171
+ }
172
+ createDelay(delayMs = 10) {
173
+ const context = {};
174
+ this.timeouts.push(context);
175
+ return (callback) => {
176
+ context.timeout && clearTimeout(context.timeout);
177
+ context.timeout = setTimeout(callback, delayMs);
178
+ };
179
+ }
180
+ // Convert array of color values to linear gradient
181
+ convertColorValues(color) {
182
+ if (!Array.isArray(color))
183
+ return color || '';
184
+ if (color.length < 2)
185
+ return color.length === 1 ? color[0] : '';
186
+ const canvasElement = document.createElement('canvas');
187
+ const ctx = canvasElement.getContext('2d');
188
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvasElement.height);
189
+ const colorStopPercentage = 1 / (color.length - 1);
190
+ color.forEach((color, index) => {
191
+ const offset = index * colorStopPercentage;
192
+ gradient.addColorStop(offset, color);
193
+ });
194
+ return gradient;
195
+ }
196
+ renderSingleCanvas(channelData, options, width, start, end, canvasContainer, progressContainer) {
197
+ const pixelRatio = window.devicePixelRatio || 1;
198
+ const height = options.height || 0;
199
+ const barWidth = options.barWidth != null && !isNaN(options.barWidth) ? options.barWidth * pixelRatio : 1;
200
+ const barGap = options.barGap != null && !isNaN(options.barGap)
201
+ ? options.barGap * pixelRatio
202
+ : options.barWidth
203
+ ? barWidth / 2
204
+ : 0;
205
+ const barRadius = options.barRadius || 0;
206
+ const barScale = options.barHeight || 1;
207
+ const isMono = channelData.length === 1;
208
+ const leftChannel = channelData[0];
209
+ const rightChannel = isMono ? leftChannel : channelData[1];
210
+ const useNegative = isMono && rightChannel.some((v) => v < 0);
211
+ const length = leftChannel.length;
212
+ const barCount = Math.floor(width / (barWidth + barGap));
213
+ const barIndexScale = barCount / length;
214
+ const halfHeight = height / 2;
215
+ let prevX = 0;
216
+ let prevLeft = 0;
217
+ let prevRight = 0;
218
+ const canvas = document.createElement('canvas');
219
+ canvas.width = Math.round((width * (end - start)) / length);
220
+ canvas.height = height * pixelRatio;
221
+ canvas.style.width = `${Math.floor(canvas.width / pixelRatio)}px`;
222
+ canvas.style.height = `${options.height}px`;
223
+ canvas.style.left = `${Math.floor((start * width) / pixelRatio / length)}px`;
224
+ canvasContainer.appendChild(canvas);
225
+ const ctx = canvas.getContext('2d', {
226
+ desynchronized: true,
227
+ });
228
+ ctx.beginPath();
229
+ ctx.fillStyle = this.convertColorValues(options.waveColor);
230
+ // Firefox shim until 2023.04.11
231
+ if (!ctx.roundRect)
232
+ ctx.roundRect = ctx.fillRect;
233
+ for (let i = start; i < end; i++) {
234
+ const barIndex = Math.round((i - start) * barIndexScale);
235
+ if (barIndex > prevX) {
236
+ const leftBarHeight = Math.round(prevLeft * halfHeight * barScale);
237
+ const rightBarHeight = Math.round(prevRight * halfHeight * barScale);
238
+ ctx.roundRect(prevX * (barWidth + barGap), halfHeight - leftBarHeight, barWidth, leftBarHeight + (rightBarHeight || 1), barRadius);
239
+ prevX = barIndex;
240
+ prevLeft = 0;
241
+ prevRight = 0;
242
+ }
243
+ const leftValue = useNegative ? leftChannel[i] : Math.abs(leftChannel[i]);
244
+ const rightValue = useNegative ? rightChannel[i] : Math.abs(rightChannel[i]);
245
+ if (leftValue > prevLeft) {
246
+ prevLeft = leftValue;
247
+ }
248
+ // If stereo, both channels are drawn as max values
249
+ // If mono with negative values, the bottom channel will be the min negative values
250
+ if (useNegative ? rightValue < -prevRight : rightValue > prevRight) {
251
+ prevRight = rightValue < 0 ? -rightValue : rightValue;
252
+ }
253
+ }
254
+ ctx.fill();
255
+ ctx.closePath();
256
+ // Draw a progress canvas
257
+ const progressCanvas = canvas.cloneNode();
258
+ progressContainer.appendChild(progressCanvas);
259
+ const progressCtx = progressCanvas.getContext('2d', {
260
+ desynchronized: true,
261
+ });
262
+ if (canvas.width > 0 && canvas.height > 0) {
263
+ progressCtx.drawImage(canvas, 0, 0);
264
+ }
265
+ // Set the composition method to draw only where the waveform is drawn
266
+ progressCtx.globalCompositeOperation = 'source-in';
267
+ progressCtx.fillStyle = this.convertColorValues(options.progressColor);
268
+ // This rectangle acts as a mask thanks to the composition method
269
+ progressCtx.fillRect(0, 0, canvas.width, canvas.height);
270
+ }
271
+ renderWaveform(channelData, options, width) {
272
+ // A container for canvases
273
+ const canvasContainer = document.createElement('div');
274
+ canvasContainer.style.height = `${options.height}px`;
275
+ this.canvasWrapper.appendChild(canvasContainer);
276
+ // A container for progress canvases
277
+ const progressContainer = canvasContainer.cloneNode();
278
+ this.progressWrapper.appendChild(progressContainer);
279
+ // Determine the currently visible part of the waveform
280
+ const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
281
+ const len = channelData[0].length;
282
+ const scale = len / scrollWidth;
283
+ const viewportWidth = Math.min(Renderer.MAX_CANVAS_WIDTH, clientWidth);
284
+ const start = Math.floor(Math.abs(scrollLeft) * scale);
285
+ const end = Math.ceil(start + viewportWidth * scale);
286
+ const viewportLen = end - start;
287
+ // Draw a portion of the waveform from start peak to end peak
288
+ const draw = (start, end) => {
289
+ this.renderSingleCanvas(channelData, options, width, Math.max(0, start), Math.min(end, len), canvasContainer, progressContainer);
290
+ };
291
+ // Draw the waveform in viewport chunks, each with a delay
292
+ const headDelay = this.createDelay();
293
+ const tailDelay = this.createDelay();
294
+ const renderHead = (fromIndex, toIndex) => {
295
+ draw(fromIndex, toIndex);
296
+ if (fromIndex > 0) {
297
+ headDelay(() => {
298
+ renderHead(fromIndex - viewportLen, toIndex - viewportLen);
299
+ });
300
+ }
301
+ };
302
+ const renderTail = (fromIndex, toIndex) => {
303
+ draw(fromIndex, toIndex);
304
+ if (toIndex < len) {
305
+ tailDelay(() => {
306
+ renderTail(fromIndex + viewportLen, toIndex + viewportLen);
307
+ });
308
+ }
309
+ };
310
+ renderHead(start, end);
311
+ if (end < len) {
312
+ renderTail(end, end + viewportLen);
313
+ }
314
+ }
315
+ render(audioData) {
316
+ // Clear previous timeouts
317
+ this.timeouts.forEach((context) => context.timeout && clearTimeout(context.timeout));
318
+ this.timeouts = [];
319
+ // Determine the width of the waveform
320
+ const pixelRatio = window.devicePixelRatio || 1;
321
+ const parentWidth = this.scrollContainer.clientWidth;
322
+ const scrollWidth = Math.ceil(audioData.duration * (this.options.minPxPerSec || 0));
323
+ // Whether the container should scroll
324
+ this.isScrolling = scrollWidth > parentWidth;
325
+ const useParentWidth = this.options.fillParent && !this.isScrolling;
326
+ // Width and height of the waveform in pixels
327
+ const width = (useParentWidth ? parentWidth : scrollWidth) * pixelRatio;
328
+ // Set the width of the wrapper
329
+ this.wrapper.style.width = useParentWidth ? '100%' : `${scrollWidth}px`;
330
+ // Set additional styles
331
+ this.scrollContainer.style.overflowX = this.isScrolling ? 'auto' : 'hidden';
332
+ this.scrollContainer.classList.toggle('noScrollbar', !!this.options.hideScrollbar);
333
+ this.cursor.style.backgroundColor = `${this.options.cursorColor || this.options.progressColor}`;
334
+ this.cursor.style.width = `${this.options.cursorWidth}px`;
335
+ // Clear the canvases
336
+ this.canvasWrapper.innerHTML = '';
337
+ this.progressWrapper.innerHTML = '';
338
+ // Render the waveform
339
+ if (this.options.splitChannels) {
340
+ // Render a waveform for each channel
341
+ for (let i = 0; i < audioData.numberOfChannels; i++) {
342
+ const options = { ...this.options, ...this.options.splitChannels[i] };
343
+ this.renderWaveform([audioData.getChannelData(i)], options, width);
344
+ }
345
+ }
346
+ else {
347
+ // Render a single waveform for the first two channels (left and right)
348
+ const channels = [audioData.getChannelData(0)];
349
+ if (audioData.numberOfChannels > 1)
350
+ channels.push(audioData.getChannelData(1));
351
+ this.renderWaveform(channels, this.options, width);
352
+ }
353
+ this.audioData = audioData;
354
+ this.emit('render');
355
+ }
356
+ reRender() {
357
+ // Return if the waveform has not been rendered yet
358
+ if (!this.audioData)
359
+ return;
360
+ // Remember the current cursor position
361
+ const oldCursorPosition = this.progressWrapper.clientWidth;
362
+ // Set the new zoom level and re-render the waveform
363
+ this.render(this.audioData);
364
+ // Adjust the scroll position so that the cursor stays in the same place
365
+ const newCursortPosition = this.progressWrapper.clientWidth;
366
+ this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
367
+ }
368
+ zoom(minPxPerSec) {
369
+ this.options.minPxPerSec = minPxPerSec;
370
+ this.reRender();
371
+ }
372
+ scrollIntoView(progress, isPlaying = false) {
373
+ const { clientWidth, scrollLeft, scrollWidth } = this.scrollContainer;
374
+ const progressWidth = scrollWidth * progress;
375
+ const center = clientWidth / 2;
376
+ const minScroll = isPlaying && this.options.autoCenter && !this.isDragging ? center : clientWidth;
377
+ if (progressWidth > scrollLeft + minScroll || progressWidth < scrollLeft) {
378
+ // Scroll to the center
379
+ if (this.options.autoCenter && !this.isDragging) {
380
+ // If the cursor is in viewport but not centered, scroll to the center slowly
381
+ const minDiff = center / 20;
382
+ if (progressWidth - (scrollLeft + center) >= minDiff && progressWidth < scrollLeft + clientWidth) {
383
+ this.scrollContainer.scrollLeft += minDiff;
384
+ }
385
+ else {
386
+ // Otherwise, scroll to the center immediately
387
+ this.scrollContainer.scrollLeft = progressWidth - center;
388
+ }
389
+ }
390
+ else if (this.isDragging) {
391
+ // Scroll just a little bit to allow for some space between the cursor and the edge
392
+ const gap = 10;
393
+ this.scrollContainer.scrollLeft =
394
+ progressWidth < scrollLeft ? progressWidth - gap : progressWidth - clientWidth + gap;
395
+ }
396
+ else {
397
+ // Scroll to the beginning
398
+ this.scrollContainer.scrollLeft = progressWidth;
399
+ }
400
+ }
401
+ // Emit the scroll event
402
+ {
403
+ const { scrollLeft } = this.scrollContainer;
404
+ const startX = scrollLeft / scrollWidth;
405
+ const endX = (scrollLeft + clientWidth) / scrollWidth;
406
+ this.emit('scroll', startX, endX);
407
+ }
408
+ }
409
+ renderProgress(progress, isPlaying) {
410
+ if (isNaN(progress))
411
+ return;
412
+ this.progressWrapper.style.width = `${progress * 100}%`;
413
+ this.cursor.style.left = `${progress * 100}%`;
414
+ this.cursor.style.marginLeft = Math.round(progress * 100) === 100 ? `-${this.options.cursorWidth}px` : '';
415
+ if (this.isScrolling && this.options.autoScroll) {
416
+ this.scrollIntoView(progress, isPlaying);
417
+ }
418
+ }
419
+ }
420
+ export default Renderer;
@@ -0,0 +1,11 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ type TimerEvents = {
3
+ tick: [];
4
+ };
5
+ declare class Timer extends EventEmitter<TimerEvents> {
6
+ private unsubscribe;
7
+ start(): void;
8
+ stop(): void;
9
+ destroy(): void;
10
+ }
11
+ export default Timer;
@@ -0,0 +1,19 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ class Timer extends EventEmitter {
3
+ unsubscribe = () => undefined;
4
+ start() {
5
+ this.unsubscribe = this.on('tick', () => {
6
+ requestAnimationFrame(() => {
7
+ this.emit('tick');
8
+ });
9
+ });
10
+ this.emit('tick');
11
+ }
12
+ stop() {
13
+ this.unsubscribe();
14
+ }
15
+ destroy() {
16
+ this.unsubscribe();
17
+ }
18
+ }
19
+ export default Timer;
@@ -0,0 +1,149 @@
1
+ import type { GenericPlugin } from './base-plugin.js';
2
+ import Player from './player.js';
3
+ export type WaveSurferColor = string | string[] | CanvasGradient;
4
+ export type WaveSurferOptions = {
5
+ /** HTML element or CSS selector */
6
+ container: HTMLElement | string;
7
+ /** The height of the waveform in pixels */
8
+ height?: number;
9
+ /** The color of the waveform */
10
+ waveColor?: WaveSurferColor;
11
+ /** The color of the progress mask */
12
+ progressColor?: WaveSurferColor;
13
+ /** The color of the playpack cursor */
14
+ cursorColor?: string;
15
+ /** The cursor width */
16
+ cursorWidth?: number;
17
+ /** Render the waveform with bars like this: ▁ ▂ ▇ ▃ ▅ ▂ */
18
+ barWidth?: number;
19
+ /** Spacing between bars in pixels */
20
+ barGap?: number;
21
+ /** Rounded borders for bars */
22
+ barRadius?: number;
23
+ /** A vertical scaling factor for the waveform */
24
+ barHeight?: number;
25
+ /** Minimum pixels per second of audio (i.e. zoom level) */
26
+ minPxPerSec?: number;
27
+ /** Stretch the waveform to fill the container, true by default */
28
+ fillParent?: boolean;
29
+ /** Audio URL */
30
+ url?: string;
31
+ /** Pre-computed audio data */
32
+ peaks?: Float32Array[] | Array<number[]>;
33
+ /** Pre-computed duration */
34
+ duration?: number;
35
+ /** Use an existing media element instead of creating one */
36
+ media?: HTMLMediaElement;
37
+ /** Play the audio on load */
38
+ autoplay?: boolean;
39
+ /** Pass false to disable clicks on the waveform */
40
+ interact?: boolean;
41
+ /** Hide the scrollbar */
42
+ hideScrollbar?: boolean;
43
+ /** Audio rate */
44
+ audioRate?: number;
45
+ /** Automatically scroll the container to keep the current position in viewport */
46
+ autoScroll?: boolean;
47
+ /** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
48
+ autoCenter?: boolean;
49
+ /** Decoding sample rate. Doesn't affect the playback. Defaults to 8000 */
50
+ sampleRate?: number;
51
+ /** Render each audio channel as a separate waveform */
52
+ splitChannels?: WaveSurferOptions[];
53
+ /** The list of plugins to initialize on start */
54
+ plugins?: GenericPlugin[];
55
+ };
56
+ declare const defaultOptions: {
57
+ height: number;
58
+ waveColor: WaveSurferColor;
59
+ progressColor: WaveSurferColor;
60
+ cursorWidth: number;
61
+ minPxPerSec: number;
62
+ fillParent: boolean;
63
+ interact: boolean;
64
+ autoScroll: boolean;
65
+ autoCenter: boolean;
66
+ sampleRate: number;
67
+ };
68
+ export type WaveSurferEvents = {
69
+ /** When audio starts loading */
70
+ load: [url: string];
71
+ /** When the audio has been decoded */
72
+ decode: [duration: number];
73
+ /** When the audio is both decoded and can play */
74
+ ready: [duration: number];
75
+ /** When a waveform is drawn */
76
+ redraw: [];
77
+ /** When the audio starts playing */
78
+ play: [];
79
+ /** When the audio pauses */
80
+ pause: [];
81
+ /** When the audio finishes playing */
82
+ finish: [];
83
+ /** On audio position change, fires continuously during playback */
84
+ timeupdate: [currentTime: number];
85
+ /** An alias of timeupdate but only when the audio is playing */
86
+ audioprocess: [currentTime: number];
87
+ /** When the user seeks to a new position */
88
+ seeking: [currentTime: number];
89
+ /** When the user interacts with the waveform (i.g. clicks or drags on it) */
90
+ interaction: [];
91
+ /** When the user clicks on the waveform */
92
+ click: [relativeX: number];
93
+ /** When the user drags the cursor */
94
+ drag: [relativeX: number];
95
+ /** When the waveform is scrolled (panned) */
96
+ scroll: [visibleStartTime: number, visibleEndTime: number];
97
+ /** When the zoom level changes */
98
+ zoom: [minPxPerSec: number];
99
+ /** Just before the waveform is destroyed so you can clean up your events */
100
+ destroy: [];
101
+ };
102
+ declare class WaveSurfer extends Player<WaveSurferEvents> {
103
+ options: WaveSurferOptions & typeof defaultOptions;
104
+ private renderer;
105
+ private timer;
106
+ private plugins;
107
+ private decodedData;
108
+ protected subscriptions: Array<() => void>;
109
+ /** Create a new WaveSurfer instance */
110
+ static create(options: WaveSurferOptions): WaveSurfer;
111
+ /** Create a new WaveSurfer instance */
112
+ constructor(options: WaveSurferOptions);
113
+ setOptions(options: Partial<WaveSurferOptions>): void;
114
+ private initPlayerEvents;
115
+ private initRendererEvents;
116
+ private initTimerEvents;
117
+ private initPlugins;
118
+ /** Register a wavesurfer.js plugin */
119
+ registerPlugin<T extends GenericPlugin>(plugin: T): T;
120
+ /** For plugins only: get the waveform wrapper div */
121
+ getWrapper(): HTMLElement;
122
+ /** Get the current scroll position in pixels */
123
+ getScroll(): number;
124
+ /** Get all registered plugins */
125
+ getActivePlugins(): GenericPlugin[];
126
+ /** Load an audio file by URL, with optional pre-decoded audio data */
127
+ load(url: string, channelData?: WaveSurferOptions['peaks'], duration?: number): Promise<void>;
128
+ /** Zoom in or out */
129
+ zoom(minPxPerSec: number): void;
130
+ /** Get the decoded audio data */
131
+ getDecodedData(): AudioBuffer | null;
132
+ /** Get the duration of the audio in seconds */
133
+ getDuration(): number;
134
+ /** Toggle if the waveform should react to clicks */
135
+ toggleInteraction(isInteractive: boolean): void;
136
+ /** Seeks to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
137
+ seekTo(progress: number): void;
138
+ /** Play or pause the audio */
139
+ playPause(): Promise<void>;
140
+ /** Stop the audio and go to the beginning */
141
+ stop(): void;
142
+ /** Skip N or -N seconds from the current positions */
143
+ skip(seconds: number): void;
144
+ /** Empty the waveform by loading a tiny silent audio */
145
+ empty(): void;
146
+ /** Unmount wavesurfer */
147
+ destroy(): void;
148
+ }
149
+ export default WaveSurfer;