wavesurfer.js 7.0.0-alpha.3 → 7.0.0-alpha.30

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 (48) hide show
  1. package/README.md +58 -26
  2. package/dist/base-plugin.d.ts +9 -5
  3. package/dist/base-plugin.js +11 -3
  4. package/dist/decoder.d.ts +2 -4
  5. package/dist/decoder.js +20 -31
  6. package/dist/event-emitter.d.ts +1 -1
  7. package/dist/event-emitter.js +4 -7
  8. package/dist/fetcher.js +2 -13
  9. package/dist/legacy-adapter.d.ts +7 -0
  10. package/dist/legacy-adapter.js +15 -0
  11. package/dist/player.d.ts +39 -9
  12. package/dist/player.js +91 -15
  13. package/dist/plugin/wavesurfer.envelope.min.js +1 -0
  14. package/dist/plugin/wavesurfer.minimap.min.js +1 -0
  15. package/dist/plugin/wavesurfer.multitrack.min.js +1 -0
  16. package/dist/plugin/wavesurfer.regions.min.js +1 -0
  17. package/dist/plugin/wavesurfer.timeline.min.js +1 -0
  18. package/dist/plugins/envelope.d.ts +60 -0
  19. package/dist/plugins/envelope.js +302 -0
  20. package/dist/plugins/envelope.min.js +1 -0
  21. package/dist/plugins/minimap.d.ts +27 -0
  22. package/dist/plugins/minimap.js +83 -0
  23. package/dist/plugins/minimap.min.js +1 -0
  24. package/dist/plugins/multitrack.d.ts +113 -0
  25. package/dist/plugins/multitrack.js +494 -0
  26. package/dist/plugins/multitrack.min.js +1 -0
  27. package/dist/plugins/record.d.ts +23 -0
  28. package/dist/plugins/record.js +119 -0
  29. package/dist/plugins/record.min.js +1 -0
  30. package/dist/plugins/regions.d.ts +23 -9
  31. package/dist/plugins/regions.js +196 -120
  32. package/dist/plugins/regions.min.js +1 -0
  33. package/dist/plugins/timeline.d.ts +38 -0
  34. package/dist/plugins/timeline.js +134 -0
  35. package/dist/plugins/timeline.min.js +1 -0
  36. package/dist/renderer.d.ts +25 -12
  37. package/dist/renderer.js +222 -138
  38. package/dist/timer.d.ts +2 -1
  39. package/dist/timer.js +5 -3
  40. package/dist/wavesurfer.d.ts +131 -0
  41. package/dist/wavesurfer.js +209 -0
  42. package/dist/wavesurfer.min.js +1 -1
  43. package/package.json +50 -20
  44. package/dist/index.d.ts +0 -92
  45. package/dist/index.js +0 -166
  46. package/dist/player-webaudio.d.ts +0 -8
  47. package/dist/player-webaudio.js +0 -30
  48. package/dist/wavesurfer.Regions.min.js +0 -1
package/dist/renderer.js CHANGED
@@ -1,202 +1,286 @@
1
1
  import EventEmitter from './event-emitter.js';
2
2
  class Renderer extends EventEmitter {
3
- constructor(options) {
3
+ options = {
4
+ height: 0,
5
+ };
6
+ container;
7
+ scrollContainer;
8
+ wrapper;
9
+ canvasWrapper;
10
+ progressWrapper;
11
+ timeout = null;
12
+ isScrolling = false;
13
+ audioData = null;
14
+ resizeObserver = null;
15
+ constructor(params, options) {
4
16
  super();
5
- this.options = options;
17
+ this.options = { ...options };
6
18
  let container = null;
7
- if (typeof this.options.container === 'string') {
8
- container = document.querySelector(this.options.container);
19
+ if (typeof params.container === 'string') {
20
+ container = document.querySelector(params.container);
9
21
  }
10
- else if (this.options.container instanceof HTMLElement) {
11
- container = this.options.container;
22
+ else if (params.container instanceof HTMLElement) {
23
+ container = params.container;
12
24
  }
13
25
  if (!container) {
14
26
  throw new Error('Container not found');
15
27
  }
28
+ const [div, shadow] = this.initHtml();
29
+ container.appendChild(div);
30
+ this.container = div;
31
+ this.scrollContainer = shadow.querySelector('.scroll');
32
+ this.wrapper = shadow.querySelector('.wrapper');
33
+ this.canvasWrapper = shadow.querySelector('.canvases');
34
+ this.progressWrapper = shadow.querySelector('.progress');
35
+ // Set a click handler
36
+ this.wrapper.addEventListener('click', (e) => {
37
+ const rect = this.wrapper.getBoundingClientRect();
38
+ const x = e.clientX - rect.left;
39
+ const relativeX = x / rect.width;
40
+ this.emit('click', { relativeX });
41
+ });
42
+ // Re-render the waveform on container resize
43
+ this.resizeObserver = new ResizeObserver(() => {
44
+ this.delay(() => this.reRender(), 100);
45
+ });
46
+ this.resizeObserver.observe(this.scrollContainer);
47
+ }
48
+ initHtml() {
16
49
  const div = document.createElement('div');
17
50
  const shadow = div.attachShadow({ mode: 'open' });
18
51
  shadow.innerHTML = `
19
52
  <style>
53
+ :host {
54
+ user-select: none;
55
+ }
20
56
  :host .scroll {
21
57
  overflow-x: auto;
22
- overflow-y: visible;
23
- user-select: none;
58
+ overflow-y: hidden;
24
59
  width: 100%;
25
- height: ${this.options.height}px;
26
60
  position: relative;
27
61
  }
62
+ :host .noScrollbar {
63
+ scrollbar-color: transparent;
64
+ }
65
+ :host .noScrollbar::-webkit-scrollbar {
66
+ display: none;
67
+ }
28
68
  :host .wrapper {
29
69
  position: relative;
30
- width: fit-content;
31
70
  min-width: 100%;
32
- height: 100%;
33
71
  z-index: 2;
34
72
  }
73
+ :host .canvases {
74
+ position: relative;
75
+ height: ${this.options.height}px;
76
+ }
35
77
  :host canvas {
36
78
  display: block;
37
- height: 100%;
38
- min-width: 100%;
79
+ position: absolute;
80
+ top: 0;
39
81
  image-rendering: pixelated;
82
+ height: ${this.options.height}px;
40
83
  }
41
84
  :host .progress {
42
- position: absolute;
43
- z-index: 2;
44
- top: 0;
45
- left: 0;
46
- height: 100%;
47
85
  pointer-events: none;
48
- clip-path: inset(100%);
49
- }
50
- :host .cursor {
51
86
  position: absolute;
52
- z-index: 3;
87
+ z-index: 2;
53
88
  top: 0;
54
89
  left: 0;
90
+ width: 0;
55
91
  height: 100%;
56
- border-right: 1px solid ${this.options.progressColor};
92
+ overflow: hidden;
93
+ box-sizing: border-box;
57
94
  }
58
95
  </style>
59
96
 
60
97
  <div class="scroll">
61
98
  <div class="wrapper">
62
- <canvas></canvas>
63
- <canvas class="progress"></canvas>
64
- <div class="cursor"></div>
99
+ <div class="canvases"></div>
100
+ <div class="progress"></div>
65
101
  </div>
66
102
  </div>
67
103
  `;
68
- this.container = div;
69
- this.scrollContainer = shadow.querySelector('.scroll');
70
- this.mainCanvas = shadow.querySelector('canvas');
71
- this.ctx = this.mainCanvas.getContext('2d', { desynchronized: true });
72
- this.progressCanvas = shadow.querySelector('.progress');
73
- this.cursor = shadow.querySelector('.cursor');
74
- container.appendChild(div);
75
- this.mainCanvas.addEventListener('click', (e) => {
76
- const rect = this.mainCanvas.getBoundingClientRect();
77
- const x = e.clientX - rect.left;
78
- const relativeX = x / rect.width;
79
- this.emit('click', { relativeX });
80
- });
104
+ return [div, shadow];
105
+ }
106
+ setOptions(options) {
107
+ this.options = options;
108
+ // Re-render the waveform if it has alredy been rendered
109
+ if (this.audioData)
110
+ this.reRender();
111
+ }
112
+ getContainer() {
113
+ return this.scrollContainer;
114
+ }
115
+ getWrapper() {
116
+ return this.wrapper;
81
117
  }
82
118
  destroy() {
83
119
  this.container.remove();
120
+ this.resizeObserver?.disconnect();
121
+ }
122
+ delay(fn, delayMs = 10) {
123
+ if (this.timeout) {
124
+ clearTimeout(this.timeout);
125
+ }
126
+ return new Promise((resolve) => {
127
+ this.timeout = setTimeout(() => {
128
+ resolve(fn());
129
+ }, delayMs);
130
+ });
84
131
  }
85
- renderLinePeaks(channelData, width, height) {
86
- const { ctx } = this;
87
- ctx.clearRect(0, 0, width, height);
88
- ctx.beginPath();
89
- ctx.moveTo(0, height / 2);
90
- // Channel 0 is left, 1 is right
132
+ async renderPeaks(channelData, width, height, pixelRatio) {
133
+ const barWidth = this.options.barWidth != null ? this.options.barWidth * pixelRatio : 1;
134
+ const barGap = this.options.barGap != null ? this.options.barGap * pixelRatio : this.options.barWidth ? barWidth / 2 : 0;
135
+ const barRadius = this.options.barRadius ?? 0;
91
136
  const leftChannel = channelData[0];
137
+ const len = leftChannel.length;
138
+ const barCount = Math.floor(width / (barWidth + barGap));
139
+ const barIndexScale = barCount / len;
140
+ const halfHeight = height / 2;
92
141
  const isMono = channelData.length === 1;
93
142
  const rightChannel = isMono ? leftChannel : channelData[1];
94
- // Draw left channel in the top half of the canvas
95
- let prevX = -1;
96
- let prevY = 0;
97
- for (let i = 0, len = leftChannel.length; i < len; i++) {
98
- const x = Math.round((i / leftChannel.length) * width);
99
- const y = Math.round(((1 - leftChannel[i]) * height) / 2);
100
- if (x !== prevX || y > prevY) {
101
- ctx.lineTo(x, y);
102
- prevX = x;
103
- prevY = y;
143
+ const useNegative = isMono && rightChannel.some((v) => v < 0);
144
+ const draw = (start, end) => {
145
+ let prevX = 0;
146
+ let prevLeft = 0;
147
+ let prevRight = 0;
148
+ const canvas = document.createElement('canvas');
149
+ canvas.width = Math.round((width * (end - start)) / len);
150
+ canvas.height = this.options.height;
151
+ canvas.style.width = `${Math.floor(canvas.width / pixelRatio)}px`;
152
+ canvas.style.height = `${this.options.height}px`;
153
+ canvas.style.left = `${Math.floor((start * width) / pixelRatio / len)}px`;
154
+ this.canvasWrapper.appendChild(canvas);
155
+ const ctx = canvas.getContext('2d', {
156
+ desynchronized: true,
157
+ });
158
+ ctx.beginPath();
159
+ ctx.fillStyle = this.options.waveColor ?? '';
160
+ // Firefox shim until 2023.04.11
161
+ if (!ctx.roundRect)
162
+ ctx.roundRect = ctx.fillRect;
163
+ for (let i = start; i < end; i++) {
164
+ const barIndex = Math.round((i - start) * barIndexScale);
165
+ if (barIndex > prevX) {
166
+ const leftBarHeight = Math.round(prevLeft * halfHeight);
167
+ const rightBarHeight = Math.round(prevRight * halfHeight);
168
+ ctx.roundRect(prevX * (barWidth + barGap), halfHeight - leftBarHeight, barWidth, leftBarHeight + rightBarHeight || 1, barRadius);
169
+ prevX = barIndex;
170
+ prevLeft = 0;
171
+ prevRight = 0;
172
+ }
173
+ const leftValue = useNegative ? leftChannel[i] : Math.abs(leftChannel[i]);
174
+ const rightValue = useNegative ? rightChannel[i] : Math.abs(rightChannel[i]);
175
+ if (leftValue > prevLeft) {
176
+ prevLeft = leftValue;
177
+ }
178
+ // If stereo, both channels are drawn as max values
179
+ // If mono with negative values, the bottom channel will be the min negative values
180
+ if (useNegative ? rightValue < -prevRight : rightValue > prevRight) {
181
+ prevRight = rightValue < 0 ? -rightValue : rightValue;
182
+ }
104
183
  }
105
- }
106
- // Draw right channel in the bottom half of the canvas
107
- prevX = -1;
108
- prevY = 0;
109
- for (let i = rightChannel.length - 1; i >= 0; i--) {
110
- const x = Math.round((i / rightChannel.length) * width);
111
- const y = Math.round(((1 + rightChannel[i]) * height) / 2);
112
- if (x !== prevX || (isMono ? y < -prevY : y > prevY)) {
113
- ctx.lineTo(x, y);
114
- prevX = x;
115
- prevY = y;
184
+ ctx.fill();
185
+ ctx.closePath();
186
+ // Draw a progress canvas
187
+ const progressCanvas = canvas.cloneNode();
188
+ this.progressWrapper.appendChild(progressCanvas);
189
+ const progressCtx = progressCanvas.getContext('2d', {
190
+ desynchronized: true,
191
+ });
192
+ if (canvas.width > 0 && canvas.height > 0) {
193
+ progressCtx.drawImage(canvas, 0, 0);
116
194
  }
195
+ // Set the composition method to draw only where the waveform is drawn
196
+ progressCtx.globalCompositeOperation = 'source-in';
197
+ progressCtx.fillStyle = this.options.progressColor ?? '';
198
+ // This rectangle acts as a mask thanks to the composition method
199
+ progressCtx.fillRect(0, 0, canvas.width, canvas.height);
200
+ };
201
+ // Clear the canvas
202
+ this.canvasWrapper.innerHTML = '';
203
+ this.progressWrapper.innerHTML = '';
204
+ // Determine the currently visible part of the waveform
205
+ const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
206
+ const scale = len / scrollWidth;
207
+ const start = Math.floor(scrollLeft * scale);
208
+ const end = Math.ceil(Math.min(scrollWidth, scrollLeft + clientWidth) * scale);
209
+ // Draw the visible portion of the waveform
210
+ draw(start, end);
211
+ // Draw the rest of the waveform with a timeout for better performance
212
+ const step = end - start;
213
+ for (let i = end; i < len; i += step) {
214
+ await this.delay(() => {
215
+ draw(i, Math.min(len, i + step));
216
+ });
217
+ }
218
+ for (let i = start - 1; i >= 0; i -= step) {
219
+ await this.delay(() => {
220
+ draw(Math.max(0, i - step), i);
221
+ });
117
222
  }
118
- ctx.strokeStyle = ctx.fillStyle = this.options.waveColor;
119
- ctx.stroke();
120
- ctx.fill();
121
- }
122
- renderBarPeaks(channelData, width, height) {
123
- var _a, _b;
124
- const { devicePixelRatio } = window;
125
- const { ctx } = this;
126
- ctx.clearRect(0, 0, width, height);
127
- const barWidthOption = this.options.barWidth || 1;
128
- const barWidth = barWidthOption * devicePixelRatio;
129
- const barGap = ((_a = this.options.barGap) !== null && _a !== void 0 ? _a : barWidthOption / 2) * devicePixelRatio;
130
- const barRadius = (_b = this.options.barRadius) !== null && _b !== void 0 ? _b : 0;
131
- const leftChannel = channelData[0];
132
- const isMono = channelData.length === 1;
133
- const rightChannel = isMono ? leftChannel : channelData[1];
134
- const barCount = Math.floor(width / (barWidth + barGap));
135
- const leftChannelBars = new Float32Array(barCount);
136
- const rightChannelBars = new Float32Array(barCount);
137
- const barIndexScale = barCount / leftChannel.length;
138
- const leftChannelLength = leftChannel.length;
139
- for (let i = 0; i < leftChannelLength; i++) {
140
- const barIndex = Math.round(i * barIndexScale);
141
- leftChannelBars[barIndex] = Math.max(leftChannelBars[barIndex], leftChannel[i]);
142
- rightChannelBars[barIndex] = (isMono ? Math.min : Math.max)(rightChannelBars[barIndex], rightChannel[i]);
143
- }
144
- ctx.beginPath();
145
- for (let i = 0; i < barCount; i++) {
146
- const leftBarHeight = Math.max(1, Math.round((leftChannelBars[i] * height) / 2));
147
- const rightBarHeight = Math.max(1, Math.round(Math.abs(rightChannelBars[i] * height) / 2));
148
- ctx.roundRect(i * (barWidth + barGap), height / 2 - leftBarHeight, barWidth, leftBarHeight + rightBarHeight, barRadius);
149
- }
150
- ctx.fillStyle = this.options.waveColor;
151
- ctx.fill();
152
- }
153
- createProgressMask() {
154
- const progressCtx = this.progressCanvas.getContext('2d', { desynchronized: true });
155
- if (!progressCtx) {
156
- throw new Error('Failed to get canvas context');
157
- }
158
- this.progressCanvas.width = this.mainCanvas.width;
159
- this.progressCanvas.height = this.mainCanvas.height;
160
- this.progressCanvas.style.width = this.mainCanvas.style.width;
161
- this.progressCanvas.style.height = this.mainCanvas.style.height;
162
- progressCtx.drawImage(this.mainCanvas, 0, 0);
163
- progressCtx.globalCompositeOperation = 'source-in';
164
- progressCtx.fillStyle = this.options.progressColor;
165
- progressCtx.fillRect(0, 0, this.progressCanvas.width, this.progressCanvas.height);
166
223
  }
167
- render(channelData, duration) {
168
- const { devicePixelRatio } = window;
169
- const width = Math.max(this.container.clientWidth * devicePixelRatio, duration * this.options.minPxPerSec);
224
+ render(audioData) {
225
+ // Determine the width of the waveform
226
+ const pixelRatio = window.devicePixelRatio || 1;
227
+ const parentWidth = this.options.fillParent ? this.scrollContainer.clientWidth * pixelRatio : 0;
228
+ const scrollWidth = this.options.minPxPerSec ? Math.max(1, audioData.duration * this.options.minPxPerSec) : 0;
229
+ const width = scrollWidth > parentWidth ? scrollWidth : parentWidth;
170
230
  const { height } = this.options;
171
- this.mainCanvas.width = width;
172
- this.mainCanvas.height = height;
173
- this.mainCanvas.style.width = Math.round(width / devicePixelRatio) + 'px';
174
- const renderingFn = this.options.barWidth ? this.renderBarPeaks : this.renderLinePeaks;
175
- renderingFn.call(this, channelData, width, height);
176
- this.createProgressMask();
231
+ this.isScrolling = width > parentWidth;
232
+ // Set the width of the container
233
+ this.wrapper.style.width = `${Math.floor(width / pixelRatio)}px`;
234
+ // Set additional styles
235
+ this.scrollContainer.style.overflowX = this.isScrolling ? 'auto' : 'hidden';
236
+ this.scrollContainer.classList.toggle('noScrollbar', !!this.options.hideScrollbar);
237
+ this.progressWrapper.style.borderRightStyle = 'solid';
238
+ this.progressWrapper.style.borderRightColor = `${this.options.cursorColor || this.options.progressColor}`;
239
+ this.progressWrapper.style.borderRightWidth = `${this.options.cursorWidth}px`;
240
+ this.canvasWrapper.style.height = `${this.options.height}px`;
241
+ // Only the first two channels are used
242
+ const channelData = [audioData.getChannelData(0)];
243
+ if (audioData.numberOfChannels > 1) {
244
+ channelData.push(audioData.getChannelData(1));
245
+ }
246
+ // Render the waveform
247
+ this.renderPeaks(channelData, width, height, pixelRatio);
248
+ this.audioData = audioData;
177
249
  }
178
- zoom(channelData, duration, minPxPerSec) {
250
+ reRender() {
251
+ if (!this.audioData)
252
+ return;
179
253
  // Remember the current cursor position
180
- const oldCursorPosition = this.cursor.getBoundingClientRect().left;
181
- this.options.minPxPerSec = minPxPerSec;
182
- this.render(channelData, duration);
254
+ const oldCursorPosition = this.progressWrapper.clientWidth;
255
+ // Set the new zoom level and re-render the waveform
256
+ this.render(this.audioData);
183
257
  // Adjust the scroll position so that the cursor stays in the same place
184
- const newCursortPosition = this.cursor.getBoundingClientRect().left;
258
+ const newCursortPosition = this.progressWrapper.clientWidth;
185
259
  this.scrollContainer.scrollLeft += newCursortPosition - oldCursorPosition;
186
260
  }
261
+ zoom(minPxPerSec) {
262
+ this.options.minPxPerSec = minPxPerSec;
263
+ this.reRender();
264
+ }
187
265
  renderProgress(progress, autoCenter = false) {
188
- this.progressCanvas.style.clipPath = `inset(0 ${100 - progress * 100}% 0 0)`;
189
- this.cursor.style.left = `${progress * 100}%`;
190
- if (autoCenter) {
191
- const center = this.container.clientWidth / 2;
192
- const fullWidth = this.mainCanvas.clientWidth;
193
- if (fullWidth * progress >= center) {
194
- this.scrollContainer.scrollLeft = fullWidth * progress - center;
266
+ this.progressWrapper.style.width = `${progress * 100}%`;
267
+ if (this.isScrolling && this.options.autoCenter) {
268
+ const { clientWidth, scrollLeft, scrollWidth } = this.scrollContainer;
269
+ const progressWidth = scrollWidth * progress;
270
+ const center = clientWidth / 2;
271
+ const minScroll = autoCenter ? center : clientWidth;
272
+ const minDiff = center / 20;
273
+ if (progressWidth > scrollLeft + minScroll || progressWidth < scrollLeft) {
274
+ // If the cursor is in viewport but not centered, scroll to the center slowly
275
+ if (progressWidth - (scrollLeft + center) >= minDiff && progressWidth < scrollLeft + clientWidth) {
276
+ this.scrollContainer.scrollLeft += minDiff;
277
+ }
278
+ else {
279
+ // Otherwise, scroll to the center immediately
280
+ this.scrollContainer.scrollLeft = progressWidth - center;
281
+ }
195
282
  }
196
283
  }
197
284
  }
198
- getContainer() {
199
- return this.scrollContainer;
200
- }
201
285
  }
202
286
  export default Renderer;
package/dist/timer.d.ts CHANGED
@@ -4,7 +4,8 @@ type TimerEvents = {
4
4
  };
5
5
  declare class Timer extends EventEmitter<TimerEvents> {
6
6
  private unsubscribe;
7
- constructor();
7
+ start(): void;
8
+ stop(): void;
8
9
  destroy(): void;
9
10
  }
10
11
  export default Timer;
package/dist/timer.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import EventEmitter from './event-emitter.js';
2
2
  class Timer extends EventEmitter {
3
- constructor() {
4
- super();
5
- this.unsubscribe = () => undefined;
3
+ unsubscribe = () => undefined;
4
+ start() {
6
5
  this.unsubscribe = this.on('tick', () => {
7
6
  requestAnimationFrame(() => {
8
7
  this.emit('tick');
@@ -10,6 +9,9 @@ class Timer extends EventEmitter {
10
9
  });
11
10
  this.emit('tick');
12
11
  }
12
+ stop() {
13
+ this.unsubscribe();
14
+ }
13
15
  destroy() {
14
16
  this.unsubscribe();
15
17
  }
@@ -0,0 +1,131 @@
1
+ import { type RendererStyleOptions } from './renderer.js';
2
+ import Player from './player.js';
3
+ import type { GenericPlugin } from './base-plugin.js';
4
+ export type WaveSurferOptions = {
5
+ /** HTML element or CSS selector */
6
+ container: HTMLElement | string | null;
7
+ /** Height of the waveform in pixels */
8
+ height?: number;
9
+ /** The color of the waveform */
10
+ waveColor?: string;
11
+ /** The color of the progress mask */
12
+ progressColor?: string;
13
+ /** The color of the playpack cursor */
14
+ cursorColor?: string;
15
+ /** The cursor with */
16
+ cursorWidth?: number;
17
+ /** If set, the waveform will be rendered in bars like so: ▁ ▂ ▇ ▃ ▅ ▂ */
18
+ barWidth?: number;
19
+ /** Spacing between bars in pixels */
20
+ barGap?: number;
21
+ /** Rounded borders for bars */
22
+ barRadius?: number;
23
+ /** Minimum pixels per second of audio (zoom) */
24
+ minPxPerSec?: number;
25
+ /** Stretch the waveform to fill the container, true by default */
26
+ fillParent?: boolean;
27
+ /** Audio URL */
28
+ url?: string;
29
+ /** Pre-computed audio data */
30
+ peaks?: Float32Array[] | Array<number[]>;
31
+ /** Pre-computed duration */
32
+ duration?: number;
33
+ /** Use an existing media element instead of creating one */
34
+ media?: HTMLMediaElement;
35
+ /** Play the audio on load */
36
+ autoplay?: boolean;
37
+ /** Is the waveform clickable? */
38
+ interact?: boolean;
39
+ /** Hide scrollbar **/
40
+ hideScrollbar?: boolean;
41
+ /** Audio rate */
42
+ audioRate?: number;
43
+ /** Keep scroll to the center of the waveform during playback */
44
+ autoCenter?: boolean;
45
+ /** Initialize plugins */
46
+ plugins?: GenericPlugin[];
47
+ };
48
+ declare const defaultOptions: {
49
+ height: number;
50
+ waveColor: string;
51
+ progressColor: string;
52
+ cursorWidth: number;
53
+ minPxPerSec: number;
54
+ fillParent: boolean;
55
+ interact: boolean;
56
+ autoCenter: boolean;
57
+ };
58
+ export type WaveSurferEvents = {
59
+ decode: {
60
+ duration: number;
61
+ };
62
+ canplay: {
63
+ duration: number;
64
+ };
65
+ ready: {
66
+ duration: number;
67
+ };
68
+ play: void;
69
+ pause: void;
70
+ finish: void;
71
+ timeupdate: {
72
+ currentTime: number;
73
+ };
74
+ seeking: {
75
+ currentTime: number;
76
+ };
77
+ interaction: void;
78
+ zoom: {
79
+ minPxPerSec: number;
80
+ };
81
+ destroy: void;
82
+ };
83
+ export type WaveSurferPluginParams = {
84
+ wavesurfer: WaveSurfer;
85
+ container: HTMLElement;
86
+ wrapper: HTMLElement;
87
+ };
88
+ declare class WaveSurfer extends Player<WaveSurferEvents> {
89
+ options: WaveSurferOptions & typeof defaultOptions;
90
+ private fetcher;
91
+ private decoder;
92
+ private renderer;
93
+ private timer;
94
+ private plugins;
95
+ private decodedData;
96
+ private canPlay;
97
+ /** Create a new WaveSurfer instance */
98
+ static create(options: WaveSurferOptions): WaveSurfer;
99
+ /** Create a new WaveSurfer instance */
100
+ constructor(options: WaveSurferOptions);
101
+ setOptions(options: Partial<RendererStyleOptions>): void;
102
+ private initPlayerEvents;
103
+ private initRendererEvents;
104
+ private initTimerEvents;
105
+ private initReadyEvent;
106
+ private initPlugins;
107
+ /** Register a wavesurfer.js plugin */
108
+ registerPlugin<T extends GenericPlugin>(plugin: T): T;
109
+ /** Load an audio file by URL, with optional pre-decoded audio data */
110
+ load(url: string, channelData?: WaveSurferOptions['peaks'], duration?: number): Promise<void>;
111
+ /** Zoom in or out */
112
+ zoom(minPxPerSec: number): void;
113
+ /** Get the decoded audio data */
114
+ getDecodedData(): AudioBuffer | null;
115
+ getDuration(): number;
116
+ /** Toggle if the waveform should react to clicks */
117
+ toggleInteraction(isInteractive: boolean): void;
118
+ /** Seeks to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
119
+ seekTo(progress: number): void;
120
+ /** Play or pause the audio */
121
+ playPause(): Promise<void>;
122
+ /** Stop the audio and go to the beginning */
123
+ stop(): void;
124
+ /** Skip N or -N seconds from the current positions */
125
+ skip(seconds: number): void;
126
+ /** Empty the waveform by loading a tiny silent audio */
127
+ empty(): void;
128
+ /** Unmount wavesurfer */
129
+ destroy(): void;
130
+ }
131
+ export default WaveSurfer;