wavesurfer.js 7.0.0-alpha.8 → 7.0.0-beta.1

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 (47) hide show
  1. package/README.md +90 -18
  2. package/dist/base-plugin.d.ts +6 -4
  3. package/dist/base-plugin.js +9 -3
  4. package/dist/decoder.d.ts +8 -7
  5. package/dist/decoder.js +43 -38
  6. package/dist/event-emitter.d.ts +16 -10
  7. package/dist/event-emitter.js +38 -16
  8. package/dist/fetcher.d.ts +4 -3
  9. package/dist/fetcher.js +5 -15
  10. package/dist/player.d.ts +31 -11
  11. package/dist/player.js +58 -31
  12. package/dist/plugins/envelope.d.ts +71 -0
  13. package/dist/plugins/envelope.js +347 -0
  14. package/dist/plugins/envelope.min.js +1 -0
  15. package/dist/plugins/minimap.d.ts +39 -0
  16. package/dist/plugins/minimap.js +117 -0
  17. package/dist/plugins/minimap.min.js +1 -0
  18. package/dist/plugins/multitrack.d.ts +117 -0
  19. package/dist/plugins/multitrack.js +507 -0
  20. package/dist/plugins/multitrack.min.js +1 -0
  21. package/dist/plugins/record.d.ts +26 -0
  22. package/dist/plugins/record.js +126 -0
  23. package/dist/plugins/record.min.js +1 -0
  24. package/dist/plugins/regions.d.ts +83 -43
  25. package/dist/plugins/regions.js +362 -190
  26. package/dist/plugins/regions.min.js +1 -0
  27. package/dist/plugins/spectrogram.d.ts +69 -0
  28. package/dist/plugins/spectrogram.js +340 -0
  29. package/dist/plugins/spectrogram.min.js +1 -0
  30. package/dist/plugins/timeline.d.ts +26 -9
  31. package/dist/plugins/timeline.js +65 -22
  32. package/dist/plugins/timeline.min.js +1 -0
  33. package/dist/renderer.d.ts +29 -13
  34. package/dist/renderer.js +280 -161
  35. package/dist/timer.d.ts +1 -1
  36. package/dist/wavesurfer.d.ts +151 -0
  37. package/dist/wavesurfer.js +241 -0
  38. package/dist/wavesurfer.min.js +1 -1
  39. package/package.json +54 -27
  40. package/dist/index.d.ts +0 -117
  41. package/dist/index.js +0 -227
  42. package/dist/player-webaudio.d.ts +0 -8
  43. package/dist/player-webaudio.js +0 -32
  44. package/dist/react/useWavesurfer.d.ts +0 -5
  45. package/dist/react/useWavesurfer.js +0 -20
  46. package/dist/wavesurfer.Regions.min.js +0 -1
  47. package/dist/wavesurfer.Timeline.min.js +0 -1
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Envelope is a visual UI for controlling the audio volume and add fade-in and fade-out effects.
3
+ */
4
+ import BasePlugin from '../base-plugin.js';
5
+ export type EnvelopePluginOptions = {
6
+ fadeInStart?: number;
7
+ fadeInEnd?: number;
8
+ fadeOutStart?: number;
9
+ fadeOutEnd?: number;
10
+ volume?: number;
11
+ lineWidth?: string;
12
+ lineColor?: string;
13
+ dragPointSize?: number;
14
+ dragPointFill?: string;
15
+ dragPointStroke?: string;
16
+ };
17
+ declare const defaultOptions: {
18
+ fadeInStart: number;
19
+ fadeOutEnd: number;
20
+ fadeInEnd: number;
21
+ fadeOutStart: number;
22
+ lineWidth: number;
23
+ lineColor: string;
24
+ dragPointSize: number;
25
+ dragPointFill: string;
26
+ dragPointStroke: string;
27
+ };
28
+ type Options = EnvelopePluginOptions & typeof defaultOptions;
29
+ export type EnvelopePluginEvents = {
30
+ 'fade-in-change': [time: number];
31
+ 'fade-out-change': [time: number];
32
+ 'volume-change': [volume: number];
33
+ };
34
+ declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePluginOptions> {
35
+ protected options: Options;
36
+ private polyline;
37
+ private audioContext;
38
+ private gainNode;
39
+ private volume;
40
+ private isFadingIn;
41
+ private isFadingOut;
42
+ private readonly naturalVolumeExponent;
43
+ constructor(options: EnvelopePluginOptions);
44
+ static create(options: EnvelopePluginOptions): EnvelopePlugin;
45
+ destroy(): void;
46
+ /** Called by wavesurfer, don't call manually */
47
+ onInit(): void;
48
+ private initSvg;
49
+ private renderPolyline;
50
+ private initWebAudio;
51
+ private invertNaturalVolume;
52
+ private naturalVolume;
53
+ private setGainValue;
54
+ private initFadeEffects;
55
+ /** Get the current audio volume */
56
+ getCurrentVolume(): number;
57
+ /**
58
+ * Set the fade-in start time.
59
+ * @param time The time (in seconds) to set the fade-in start time to
60
+ * @param moveFadeInEnd Whether to move the drag point to the new time (default: false)
61
+ */
62
+ setStartTime(time: number, moveFadeInEnd?: boolean): void;
63
+ /** Set the fade-in end time.
64
+ * @param time The time (in seconds) to set the fade-in end time to
65
+ * @param moveFadeOutStart Whether to move the drag point to the new time (default: false)
66
+ */
67
+ setEndTime(time: number, moveFadeOutStart?: boolean): void;
68
+ /** Set the volume of the audio */
69
+ setVolume(volume: number): void;
70
+ }
71
+ export default EnvelopePlugin;
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Envelope is a visual UI for controlling the audio volume and add fade-in and fade-out effects.
3
+ */
4
+ import BasePlugin from '../base-plugin.js';
5
+ import EventEmitter from '../event-emitter.js';
6
+ const defaultOptions = {
7
+ fadeInStart: 0,
8
+ fadeOutEnd: 0,
9
+ fadeInEnd: 0,
10
+ fadeOutStart: 0,
11
+ lineWidth: 4,
12
+ lineColor: 'rgba(0, 0, 255, 0.5)',
13
+ dragPointSize: 10,
14
+ dragPointFill: 'rgba(255, 255, 255, 0.8)',
15
+ dragPointStroke: 'rgba(255, 255, 255, 0.8)',
16
+ };
17
+ class Polyline extends EventEmitter {
18
+ constructor(options, wrapper) {
19
+ super();
20
+ this.top = 0;
21
+ // An padding to make the envelope fit into the SVG
22
+ this.padding = options.dragPointSize / 2 + 1;
23
+ // SVG element
24
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
25
+ svg.setAttribute('width', '100%');
26
+ svg.setAttribute('height', '100%');
27
+ svg.setAttribute('viewBox', '0 0 0 0');
28
+ svg.setAttribute('preserveAspectRatio', 'none');
29
+ svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;');
30
+ svg.setAttribute('part', 'envelope');
31
+ this.svg = svg;
32
+ // A polyline representing the envelope
33
+ const polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
34
+ polyline.setAttribute('points', '0,0 0,0 0,0 0,0');
35
+ polyline.setAttribute('stroke', options.lineColor);
36
+ polyline.setAttribute('stroke-width', options.lineWidth);
37
+ polyline.setAttribute('fill', 'none');
38
+ polyline.setAttribute('style', 'pointer-events: none;');
39
+ polyline.setAttribute('part', 'polyline');
40
+ svg.appendChild(polyline);
41
+ // Draggable top line
42
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
43
+ line.setAttribute('stroke', 'transparent');
44
+ line.setAttribute('stroke-width', (options.lineWidth * 3).toString());
45
+ line.setAttribute('style', 'cursor: ns-resize; pointer-events: all;');
46
+ line.setAttribute('part', 'line');
47
+ svg.appendChild(line);
48
+ [0, 1].forEach(() => {
49
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
50
+ circle.setAttribute('r', (options.dragPointSize / 2).toString());
51
+ circle.setAttribute('fill', options.dragPointFill);
52
+ circle.setAttribute('stroke', options.dragPointStroke || options.dragPointFill);
53
+ circle.setAttribute('stroke-width', '2');
54
+ circle.setAttribute('style', 'cursor: ew-resize; pointer-events: all;');
55
+ circle.setAttribute('part', 'circle');
56
+ svg.appendChild(circle);
57
+ });
58
+ wrapper.appendChild(svg);
59
+ // Init dtagging
60
+ {
61
+ // On top line drag
62
+ const onDragY = (dy) => {
63
+ const newTop = this.top + dy;
64
+ const { height } = svg.viewBox.baseVal;
65
+ if (newTop < -0.5 || newTop > height)
66
+ return;
67
+ const relativeY = Math.min(1, Math.max(0, (height - newTop) / height));
68
+ this.emit('line-move', relativeY);
69
+ };
70
+ // On points drag
71
+ const onDragX = (index, dx) => {
72
+ const point = polyline.points.getItem(index);
73
+ const newX = point.x + dx;
74
+ const { width } = svg.viewBox.baseVal;
75
+ this.emit('point-move', index, newX / width);
76
+ };
77
+ // Draggable top line of the polyline
78
+ this.makeDraggable(line, (_, dy) => onDragY(dy));
79
+ // Make each point draggable
80
+ const draggables = this.svg.querySelectorAll('circle');
81
+ Array.from(draggables).forEach((draggable, index) => {
82
+ this.makeDraggable(draggable, (dx) => onDragX(index + 1, dx));
83
+ });
84
+ }
85
+ }
86
+ makeDraggable(draggable, onDrag) {
87
+ draggable.addEventListener('click', (e) => {
88
+ e.preventDefault();
89
+ e.stopPropagation();
90
+ });
91
+ draggable.addEventListener('mousedown', (e) => {
92
+ e.preventDefault();
93
+ e.stopPropagation();
94
+ let x = e.clientX;
95
+ let y = e.clientY;
96
+ const move = (e) => {
97
+ const dx = e.clientX - x;
98
+ const dy = e.clientY - y;
99
+ x = e.clientX;
100
+ y = e.clientY;
101
+ onDrag(dx, dy);
102
+ };
103
+ const up = () => {
104
+ document.removeEventListener('mousemove', move);
105
+ document.removeEventListener('mouseup', up);
106
+ };
107
+ document.addEventListener('mousemove', move);
108
+ document.addEventListener('mouseup', up);
109
+ });
110
+ }
111
+ update({ x1, x2, x3, x4, y }) {
112
+ const width = this.svg.clientWidth;
113
+ const height = this.svg.clientHeight;
114
+ this.top = height - y * height;
115
+ const paddedTop = Math.max(this.padding, Math.min(this.top, height - this.padding));
116
+ this.svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
117
+ const polyline = this.svg.querySelector('polyline');
118
+ const { points } = polyline;
119
+ points.getItem(0).x = x1 * width;
120
+ points.getItem(0).y = height;
121
+ points.getItem(1).x = x2 * width;
122
+ points.getItem(1).y = paddedTop;
123
+ points.getItem(2).x = x3 * width;
124
+ points.getItem(2).y = paddedTop;
125
+ points.getItem(3).x = x4 * width;
126
+ points.getItem(3).y = height;
127
+ const line = this.svg.querySelector('line');
128
+ line.setAttribute('x1', points.getItem(1).x.toString());
129
+ line.setAttribute('x2', points.getItem(2).x.toString());
130
+ line.setAttribute('y1', paddedTop.toString());
131
+ line.setAttribute('y2', paddedTop.toString());
132
+ const circles = this.svg.querySelectorAll('circle');
133
+ Array.from(circles).forEach((circle, i) => {
134
+ const point = points.getItem(i + 1);
135
+ circle.setAttribute('cx', point.x.toString());
136
+ circle.setAttribute('cy', point.y.toString());
137
+ });
138
+ }
139
+ destroy() {
140
+ this.svg.remove();
141
+ }
142
+ }
143
+ class EnvelopePlugin extends BasePlugin {
144
+ constructor(options) {
145
+ super(options);
146
+ this.polyline = null;
147
+ this.audioContext = null;
148
+ this.gainNode = null;
149
+ this.volume = 1;
150
+ this.isFadingIn = false;
151
+ this.isFadingOut = false;
152
+ // Adjust the exponent to change the curve of the volume control
153
+ this.naturalVolumeExponent = 1.5;
154
+ this.options = Object.assign({}, defaultOptions, options);
155
+ this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
156
+ this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
157
+ this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
158
+ this.volume = this.options.volume ?? 1;
159
+ }
160
+ static create(options) {
161
+ return new EnvelopePlugin(options);
162
+ }
163
+ destroy() {
164
+ this.polyline?.destroy();
165
+ super.destroy();
166
+ }
167
+ /** Called by wavesurfer, don't call manually */
168
+ onInit() {
169
+ if (!this.wavesurfer) {
170
+ throw Error('WaveSurfer is not initialized');
171
+ }
172
+ this.initWebAudio();
173
+ this.initSvg();
174
+ this.initFadeEffects();
175
+ this.subscriptions.push(this.wavesurfer.on('redraw', () => {
176
+ const duration = this.wavesurfer?.getDuration();
177
+ if (!duration)
178
+ return;
179
+ this.options.fadeInStart = this.options.fadeInStart || 0;
180
+ this.options.fadeOutEnd = this.options.fadeOutEnd || duration;
181
+ this.options.fadeInEnd = this.options.fadeInEnd || this.options.fadeInStart;
182
+ this.options.fadeOutStart = this.options.fadeOutStart || this.options.fadeOutEnd;
183
+ this.renderPolyline();
184
+ }));
185
+ }
186
+ initSvg() {
187
+ if (!this.wavesurfer)
188
+ return;
189
+ const wrapper = this.wavesurfer.getWrapper();
190
+ this.polyline = new Polyline(this.options, wrapper);
191
+ this.subscriptions.push(this.polyline.on('line-move', (relativeY) => {
192
+ this.setVolume(this.naturalVolume(relativeY));
193
+ }), this.polyline.on('point-move', (index, relativeX) => {
194
+ const duration = this.wavesurfer?.getDuration() || 0;
195
+ const newTime = relativeX * duration;
196
+ // Fade-in end point
197
+ if (index === 1) {
198
+ if (newTime < this.options.fadeInStart || newTime > this.options.fadeOutStart)
199
+ return;
200
+ this.options.fadeInEnd = newTime;
201
+ this.emit('fade-in-change', newTime);
202
+ }
203
+ else if (index === 2) {
204
+ // Fade-out start point
205
+ if (newTime > this.options.fadeOutEnd || newTime < this.options.fadeInEnd)
206
+ return;
207
+ this.options.fadeOutStart = newTime;
208
+ this.emit('fade-out-change', newTime);
209
+ }
210
+ this.renderPolyline();
211
+ }));
212
+ }
213
+ renderPolyline() {
214
+ if (!this.polyline || !this.wavesurfer)
215
+ return;
216
+ const duration = this.wavesurfer.getDuration();
217
+ if (!duration)
218
+ return;
219
+ this.polyline.update({
220
+ x1: this.options.fadeInStart / duration,
221
+ x2: this.options.fadeInEnd / duration,
222
+ x3: this.options.fadeOutStart / duration,
223
+ x4: this.options.fadeOutEnd / duration,
224
+ y: this.invertNaturalVolume(this.volume),
225
+ });
226
+ }
227
+ initWebAudio() {
228
+ const audio = this.wavesurfer?.getMediaElement();
229
+ if (!audio)
230
+ return null;
231
+ this.volume = this.options.volume ?? audio.volume;
232
+ // Create an AudioContext
233
+ const audioContext = new window.AudioContext();
234
+ // Create a GainNode for controlling the volume
235
+ this.gainNode = audioContext.createGain();
236
+ this.setGainValue();
237
+ // Create a MediaElementAudioSourceNode using the audio element
238
+ const source = audioContext.createMediaElementSource(audio);
239
+ // Connect the source to the GainNode, and the GainNode to the destination (speakers)
240
+ source.connect(this.gainNode);
241
+ this.gainNode.connect(audioContext.destination);
242
+ this.audioContext = audioContext;
243
+ }
244
+ invertNaturalVolume(value) {
245
+ const minValue = 0.0001;
246
+ const maxValue = 1;
247
+ const interpolatedValue = Math.pow((value - minValue) / (maxValue - minValue), 1 / this.naturalVolumeExponent);
248
+ return interpolatedValue;
249
+ }
250
+ naturalVolume(value) {
251
+ const minValue = 0.0001;
252
+ const maxValue = 1;
253
+ const interpolatedValue = minValue + (maxValue - minValue) * Math.pow(value, this.naturalVolumeExponent);
254
+ return interpolatedValue;
255
+ }
256
+ setGainValue() {
257
+ if (this.gainNode) {
258
+ this.gainNode.gain.value = this.volume;
259
+ }
260
+ }
261
+ initFadeEffects() {
262
+ if (!this.audioContext || !this.wavesurfer)
263
+ return;
264
+ const unsub = this.wavesurfer.on('timeupdate', (currentTime) => {
265
+ if (!this.audioContext || !this.gainNode)
266
+ return;
267
+ if (!this.wavesurfer?.isPlaying())
268
+ return;
269
+ if (this.audioContext.state === 'suspended') {
270
+ this.audioContext.resume();
271
+ }
272
+ // Fade in
273
+ if (!this.isFadingIn && currentTime >= this.options.fadeInStart && currentTime <= this.options.fadeInEnd) {
274
+ this.isFadingIn = true;
275
+ // Set the initial gain (volume) to 0 (silent)
276
+ this.gainNode.gain.setValueAtTime(0, this.audioContext.currentTime);
277
+ // Set the target gain (volume) to 1 (full volume) over N seconds
278
+ this.gainNode.gain.linearRampToValueAtTime(this.volume, this.audioContext.currentTime + (this.options.fadeInEnd - currentTime));
279
+ return;
280
+ }
281
+ // Fade out
282
+ if (!this.isFadingOut && currentTime >= this.options.fadeOutStart && currentTime <= this.options.fadeOutEnd) {
283
+ this.isFadingOut = true;
284
+ /**
285
+ * Set the gain at this point in time to the current volume, otherwise
286
+ * the audio will start fading out from the fade-in point.
287
+ */
288
+ this.gainNode.gain.setValueAtTime(this.volume, this.audioContext.currentTime);
289
+ // Set the target gain (volume) to 0 (silent) over N seconds
290
+ this.gainNode.gain.linearRampToValueAtTime(0, this.audioContext.currentTime + (this.options.fadeOutEnd - currentTime));
291
+ return;
292
+ }
293
+ // Reset fade in/out
294
+ let cancelRamp = false;
295
+ if (this.isFadingIn && (currentTime < this.options.fadeInStart || currentTime > this.options.fadeInEnd)) {
296
+ this.isFadingIn = false;
297
+ cancelRamp = true;
298
+ }
299
+ if (this.isFadingOut && (currentTime < this.options.fadeOutStart || currentTime >= this.options.fadeOutEnd)) {
300
+ this.isFadingOut = false;
301
+ cancelRamp = true;
302
+ }
303
+ if (cancelRamp) {
304
+ this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime);
305
+ this.setGainValue();
306
+ }
307
+ });
308
+ this.subscriptions.push(unsub);
309
+ }
310
+ /** Get the current audio volume */
311
+ getCurrentVolume() {
312
+ return this.gainNode ? this.gainNode.gain.value : this.volume;
313
+ }
314
+ /**
315
+ * Set the fade-in start time.
316
+ * @param time The time (in seconds) to set the fade-in start time to
317
+ * @param moveFadeInEnd Whether to move the drag point to the new time (default: false)
318
+ */
319
+ setStartTime(time, moveFadeInEnd = false) {
320
+ if (moveFadeInEnd) {
321
+ const rampLength = this.options.fadeInEnd - this.options.fadeInStart;
322
+ this.options.fadeInEnd = time + rampLength;
323
+ }
324
+ this.options.fadeInStart = time;
325
+ this.renderPolyline();
326
+ }
327
+ /** Set the fade-in end time.
328
+ * @param time The time (in seconds) to set the fade-in end time to
329
+ * @param moveFadeOutStart Whether to move the drag point to the new time (default: false)
330
+ */
331
+ setEndTime(time, moveFadeOutStart = false) {
332
+ if (moveFadeOutStart) {
333
+ const rampLength = this.options.fadeOutEnd - this.options.fadeOutStart;
334
+ this.options.fadeOutStart = time - rampLength;
335
+ }
336
+ this.options.fadeOutEnd = time;
337
+ this.renderPolyline();
338
+ }
339
+ /** Set the volume of the audio */
340
+ setVolume(volume) {
341
+ this.volume = volume;
342
+ this.setGainValue();
343
+ this.renderPolyline();
344
+ this.emit('volume-change', volume);
345
+ }
346
+ }
347
+ export default EnvelopePlugin;
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Envelope=e():t.Envelope=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={284:(t,e,i)=>{i.d(e,{Z:()=>o});var s=i(139);class n extends s.Z{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}}const o=n},139:(t,e,i)=>{i.d(e,{Z:()=>s});const s=class{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),s=this.on(t,(()=>{i(),s()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}}},e={};function i(s){var n=e[s];if(void 0!==n)return n.exports;var o=e[s]={exports:{}};return t[s](o,o.exports,i),o.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var s={};return(()=>{i.d(s,{default:()=>a});var t=i(284),e=i(139);const n={fadeInStart:0,fadeOutEnd:0,fadeInEnd:0,fadeOutStart:0,lineWidth:4,lineColor:"rgba(0, 0, 255, 0.5)",dragPointSize:10,dragPointFill:"rgba(255, 255, 255, 0.8)",dragPointStroke:"rgba(255, 255, 255, 0.8)"};class o extends e.Z{constructor(t,e){super(),this.top=0,this.padding=t.dragPointSize/2+1;const i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.setAttribute("width","100%"),i.setAttribute("height","100%"),i.setAttribute("viewBox","0 0 0 0"),i.setAttribute("preserveAspectRatio","none"),i.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),i.setAttribute("part","envelope"),this.svg=i;const s=document.createElementNS("http://www.w3.org/2000/svg","polyline");s.setAttribute("points","0,0 0,0 0,0 0,0"),s.setAttribute("stroke",t.lineColor),s.setAttribute("stroke-width",t.lineWidth),s.setAttribute("fill","none"),s.setAttribute("style","pointer-events: none;"),s.setAttribute("part","polyline"),i.appendChild(s);const n=document.createElementNS("http://www.w3.org/2000/svg","line");n.setAttribute("stroke","transparent"),n.setAttribute("stroke-width",(3*t.lineWidth).toString()),n.setAttribute("style","cursor: ns-resize; pointer-events: all;"),n.setAttribute("part","line"),i.appendChild(n),[0,1].forEach((()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","circle");e.setAttribute("r",(t.dragPointSize/2).toString()),e.setAttribute("fill",t.dragPointFill),e.setAttribute("stroke",t.dragPointStroke||t.dragPointFill),e.setAttribute("stroke-width","2"),e.setAttribute("style","cursor: ew-resize; pointer-events: all;"),e.setAttribute("part","circle"),i.appendChild(e)})),e.appendChild(i);{const t=t=>{const e=this.top+t,{height:s}=i.viewBox.baseVal;if(e<-.5||e>s)return;const n=Math.min(1,Math.max(0,(s-e)/s));this.emit("line-move",n)},e=(t,e)=>{const n=s.points.getItem(t).x+e,{width:o}=i.viewBox.baseVal;this.emit("point-move",t,n/o)};this.makeDraggable(n,((e,i)=>t(i)));const o=this.svg.querySelectorAll("circle");Array.from(o).forEach(((t,i)=>{this.makeDraggable(t,(t=>e(i+1,t)))}))}}makeDraggable(t,e){t.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation()})),t.addEventListener("mousedown",(t=>{t.preventDefault(),t.stopPropagation();let i=t.clientX,s=t.clientY;const n=t=>{const n=t.clientX-i,o=t.clientY-s;i=t.clientX,s=t.clientY,e(n,o)},o=()=>{document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o)};document.addEventListener("mousemove",n),document.addEventListener("mouseup",o)}))}update({x1:t,x2:e,x3:i,x4:s,y:n}){const o=this.svg.clientWidth,r=this.svg.clientHeight;this.top=r-n*r;const a=Math.max(this.padding,Math.min(this.top,r-this.padding));this.svg.setAttribute("viewBox",`0 0 ${o} ${r}`);const u=this.svg.querySelector("polyline"),{points:h}=u;h.getItem(0).x=t*o,h.getItem(0).y=r,h.getItem(1).x=e*o,h.getItem(1).y=a,h.getItem(2).x=i*o,h.getItem(2).y=a,h.getItem(3).x=s*o,h.getItem(3).y=r;const d=this.svg.querySelector("line");d.setAttribute("x1",h.getItem(1).x.toString()),d.setAttribute("x2",h.getItem(2).x.toString()),d.setAttribute("y1",a.toString()),d.setAttribute("y2",a.toString());const l=this.svg.querySelectorAll("circle");Array.from(l).forEach(((t,e)=>{const i=h.getItem(e+1);t.setAttribute("cx",i.x.toString()),t.setAttribute("cy",i.y.toString())}))}destroy(){this.svg.remove()}}class r extends t.Z{constructor(t){super(t),this.polyline=null,this.audioContext=null,this.gainNode=null,this.volume=1,this.isFadingIn=!1,this.isFadingOut=!1,this.naturalVolumeExponent=1.5,this.options=Object.assign({},n,t),this.options.lineColor=this.options.lineColor||n.lineColor,this.options.dragPointFill=this.options.dragPointFill||n.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||n.dragPointStroke,this.volume=this.options.volume??1}static create(t){return new r(t)}destroy(){this.polyline?.destroy(),super.destroy()}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.initWebAudio(),this.initSvg(),this.initFadeEffects(),this.subscriptions.push(this.wavesurfer.on("redraw",(()=>{const t=this.wavesurfer?.getDuration();t&&(this.options.fadeInStart=this.options.fadeInStart||0,this.options.fadeOutEnd=this.options.fadeOutEnd||t,this.options.fadeInEnd=this.options.fadeInEnd||this.options.fadeInStart,this.options.fadeOutStart=this.options.fadeOutStart||this.options.fadeOutEnd,this.renderPolyline())})))}initSvg(){if(!this.wavesurfer)return;const t=this.wavesurfer.getWrapper();this.polyline=new o(this.options,t),this.subscriptions.push(this.polyline.on("line-move",(t=>{this.setVolume(this.naturalVolume(t))})),this.polyline.on("point-move",((t,e)=>{const i=e*(this.wavesurfer?.getDuration()||0);if(1===t){if(i<this.options.fadeInStart||i>this.options.fadeOutStart)return;this.options.fadeInEnd=i,this.emit("fade-in-change",i)}else if(2===t){if(i>this.options.fadeOutEnd||i<this.options.fadeInEnd)return;this.options.fadeOutStart=i,this.emit("fade-out-change",i)}this.renderPolyline()})))}renderPolyline(){if(!this.polyline||!this.wavesurfer)return;const t=this.wavesurfer.getDuration();t&&this.polyline.update({x1:this.options.fadeInStart/t,x2:this.options.fadeInEnd/t,x3:this.options.fadeOutStart/t,x4:this.options.fadeOutEnd/t,y:this.invertNaturalVolume(this.volume)})}initWebAudio(){const t=this.wavesurfer?.getMediaElement();if(!t)return null;this.volume=this.options.volume??t.volume;const e=new window.AudioContext;this.gainNode=e.createGain(),this.setGainValue(),e.createMediaElementSource(t).connect(this.gainNode),this.gainNode.connect(e.destination),this.audioContext=e}invertNaturalVolume(t){return Math.pow((t-1e-4)/.9999,1/this.naturalVolumeExponent)}naturalVolume(t){return 1e-4+.9999*Math.pow(t,this.naturalVolumeExponent)}setGainValue(){this.gainNode&&(this.gainNode.gain.value=this.volume)}initFadeEffects(){if(!this.audioContext||!this.wavesurfer)return;const t=this.wavesurfer.on("timeupdate",(t=>{if(!this.audioContext||!this.gainNode)return;if(!this.wavesurfer?.isPlaying())return;if("suspended"===this.audioContext.state&&this.audioContext.resume(),!this.isFadingIn&&t>=this.options.fadeInStart&&t<=this.options.fadeInEnd)return this.isFadingIn=!0,this.gainNode.gain.setValueAtTime(0,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(this.volume,this.audioContext.currentTime+(this.options.fadeInEnd-t));if(!this.isFadingOut&&t>=this.options.fadeOutStart&&t<=this.options.fadeOutEnd)return this.isFadingOut=!0,this.gainNode.gain.setValueAtTime(this.volume,this.audioContext.currentTime),void this.gainNode.gain.linearRampToValueAtTime(0,this.audioContext.currentTime+(this.options.fadeOutEnd-t));let e=!1;this.isFadingIn&&(t<this.options.fadeInStart||t>this.options.fadeInEnd)&&(this.isFadingIn=!1,e=!0),this.isFadingOut&&(t<this.options.fadeOutStart||t>=this.options.fadeOutEnd)&&(this.isFadingOut=!1,e=!0),e&&(this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime),this.setGainValue())}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t,e=!1){if(e){const e=this.options.fadeInEnd-this.options.fadeInStart;this.options.fadeInEnd=t+e}this.options.fadeInStart=t,this.renderPolyline()}setEndTime(t,e=!1){if(e){const e=this.options.fadeOutEnd-this.options.fadeOutStart;this.options.fadeOutStart=t-e}this.options.fadeOutEnd=t,this.renderPolyline()}setVolume(t){this.volume=t,this.setGainValue(),this.renderPolyline(),this.emit("volume-change",t)}}const a=r})(),s.default})()));
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Minimap is a tiny copy of the main waveform serving as a navigation tool.
3
+ */
4
+ import BasePlugin from '../base-plugin.js';
5
+ import { type WaveSurferOptions } from '../wavesurfer.js';
6
+ export type MinimapPluginOptions = {
7
+ overlayColor?: string;
8
+ insertPosition?: InsertPosition;
9
+ } & WaveSurferOptions;
10
+ declare const defaultOptions: {
11
+ height: number;
12
+ overlayColor: string;
13
+ insertPosition: string;
14
+ };
15
+ export type MinimapPluginEvents = {
16
+ ready: [];
17
+ interaction: [];
18
+ };
19
+ declare class MinimapPlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions> {
20
+ protected options: MinimapPluginOptions & typeof defaultOptions;
21
+ private minimapWrapper;
22
+ private miniWavesurfer;
23
+ private overlay;
24
+ private container;
25
+ constructor(options: MinimapPluginOptions);
26
+ static create(options: MinimapPluginOptions): MinimapPlugin;
27
+ /** Called by wavesurfer, don't call manually */
28
+ onInit(): void;
29
+ private initMinimapWrapper;
30
+ private initOverlay;
31
+ private initMinimap;
32
+ private getOverlayWidth;
33
+ private onRedraw;
34
+ private onScroll;
35
+ private initWaveSurferEvents;
36
+ /** Unmount */
37
+ destroy(): void;
38
+ }
39
+ export default MinimapPlugin;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Minimap is a tiny copy of the main waveform serving as a navigation tool.
3
+ */
4
+ import BasePlugin from '../base-plugin.js';
5
+ import WaveSurfer from '../wavesurfer.js';
6
+ const defaultOptions = {
7
+ height: 50,
8
+ overlayColor: 'rgba(100, 100, 100, 0.1)',
9
+ insertPosition: 'afterend',
10
+ };
11
+ class MinimapPlugin extends BasePlugin {
12
+ constructor(options) {
13
+ super(options);
14
+ this.miniWavesurfer = null;
15
+ this.container = null;
16
+ this.options = Object.assign({}, defaultOptions, options);
17
+ this.minimapWrapper = this.initMinimapWrapper();
18
+ this.overlay = this.initOverlay();
19
+ }
20
+ static create(options) {
21
+ return new MinimapPlugin(options);
22
+ }
23
+ /** Called by wavesurfer, don't call manually */
24
+ onInit() {
25
+ if (!this.wavesurfer) {
26
+ throw Error('WaveSurfer is not initialized');
27
+ }
28
+ if (this.options.container) {
29
+ if (typeof this.options.container === 'string') {
30
+ this.container = document.querySelector(this.options.container);
31
+ }
32
+ else if (this.options.container instanceof HTMLElement) {
33
+ this.container = this.options.container;
34
+ }
35
+ this.container?.appendChild(this.minimapWrapper);
36
+ }
37
+ else {
38
+ this.container = this.wavesurfer.getWrapper().parentElement;
39
+ this.container?.insertAdjacentElement(this.options.insertPosition, this.minimapWrapper);
40
+ }
41
+ this.initWaveSurferEvents();
42
+ }
43
+ initMinimapWrapper() {
44
+ const div = document.createElement('div');
45
+ div.style.position = 'relative';
46
+ div.setAttribute('part', 'minimap');
47
+ return div;
48
+ }
49
+ initOverlay() {
50
+ const div = document.createElement('div');
51
+ div.setAttribute('style', 'position: absolute; z-index: 2; left: 0; top: 0; bottom: 0; transition: left 100ms ease-out; pointer-events: none;');
52
+ div.style.backgroundColor = this.options.overlayColor;
53
+ this.minimapWrapper.appendChild(div);
54
+ return div;
55
+ }
56
+ initMinimap() {
57
+ if (this.miniWavesurfer) {
58
+ this.miniWavesurfer.destroy();
59
+ this.miniWavesurfer = null;
60
+ }
61
+ if (!this.wavesurfer)
62
+ return;
63
+ const data = this.wavesurfer.getDecodedData();
64
+ const media = this.wavesurfer.getMediaElement();
65
+ if (!data || !media)
66
+ return;
67
+ this.miniWavesurfer = WaveSurfer.create({
68
+ ...this.options,
69
+ container: this.minimapWrapper,
70
+ minPxPerSec: 0,
71
+ fillParent: true,
72
+ media,
73
+ peaks: [data.getChannelData(0)],
74
+ duration: data.duration,
75
+ });
76
+ this.subscriptions.push(this.miniWavesurfer.on('ready', () => {
77
+ this.emit('ready');
78
+ }), this.miniWavesurfer.on('interaction', () => {
79
+ if (this.wavesurfer && this.miniWavesurfer) {
80
+ this.wavesurfer.setTime(this.miniWavesurfer.getCurrentTime());
81
+ }
82
+ this.emit('interaction');
83
+ }));
84
+ }
85
+ getOverlayWidth() {
86
+ const waveformWidth = this.wavesurfer?.getWrapper().clientWidth || 1;
87
+ return Math.round((this.minimapWrapper.clientWidth / waveformWidth) * 100);
88
+ }
89
+ onRedraw() {
90
+ const overlayWidth = this.getOverlayWidth();
91
+ this.overlay.style.width = `${overlayWidth}%`;
92
+ }
93
+ onScroll(startTime) {
94
+ if (!this.wavesurfer)
95
+ return;
96
+ const duration = this.wavesurfer.getDuration();
97
+ this.overlay.style.left = `${(startTime / duration) * 100}%`;
98
+ }
99
+ initWaveSurferEvents() {
100
+ if (!this.wavesurfer)
101
+ return;
102
+ this.subscriptions.push(this.wavesurfer.on('decode', () => {
103
+ this.initMinimap();
104
+ }), this.wavesurfer.on('scroll', (startTime) => {
105
+ this.onScroll(startTime);
106
+ }), this.wavesurfer.on('redraw', () => {
107
+ this.onRedraw();
108
+ }));
109
+ }
110
+ /** Unmount */
111
+ destroy() {
112
+ this.miniWavesurfer?.destroy();
113
+ this.minimapWrapper.remove();
114
+ super.destroy();
115
+ }
116
+ }
117
+ export default MinimapPlugin;
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Minimap=e():t.Minimap=e()}(WaveSurfer,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var s in i)t.o(i,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:i[s]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>g});const i=class{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),s=this.on(t,(()=>{i(),s()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}},s=class extends i{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}init(t){this.wavesurfer=t,this.onInit()}destroy(){this.subscriptions.forEach((t=>t()))}},r={decode:async function(t,e){const i=new AudioContext({sampleRate:e}),s=i.decodeAudioData(t);return s.finally((()=>i.close())),s},createBuffer:function(t,e){return"number"==typeof t[0]&&(t=[t]),function(t){const e=t[0];if(e.some((t=>t>1||t<-1))){const i=e.length;let s=0;for(let t=0;t<i;t++){const i=Math.abs(e[t]);i>s&&(s=i)}for(const e of t)for(let t=0;t<i;t++)e[t]/=s}}(t),{duration:e,length:t[0].length,sampleRate:t[0].length/e,numberOfChannels:t.length,getChannelData:e=>t?.[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}},n=async function(t){return fetch(t).then((t=>t.arrayBuffer()))},o=class extends i{constructor(t){super(),this.isExternalMedia=!1,t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}onceMediaEvent(t,e){return this.onMediaEvent(t,e,{once:!0})}revokeSrc(){(this.media.currentSrc||this.media.src||"").startsWith("blob:")&&URL.revokeObjectURL(this.media.currentSrc)}setSrc(t,e){if((this.media.currentSrc||this.media.src||"")===t)return;this.revokeSrc();const i=e?URL.createObjectURL(new Blob([e],{type:"audio/wav"})):t;this.media.src=i}destroy(){this.media.pause(),this.revokeSrc(),this.isExternalMedia||this.media.remove()}play(){return this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}setTime(t){this.media.currentTime=t}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}setSinkId(t){return this.media.setSinkId(t)}};class a extends i{constructor(t,e){if(super(),this.options={height:0},this.timeout=null,this.isScrolling=!1,this.audioData=null,this.resizeObserver=null,this.isDragging=!1,this.options={...e},"string"==typeof t&&(t=document.querySelector(t)),!t)throw new Error("Container not found");const[i,s]=this.initHtml();t.appendChild(i),this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),this.cursor=s.querySelector(".cursor"),this.initEvents()}initEvents(){this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",i)})),this.initDrag(),this.scrollContainer.addEventListener("scroll",(()=>{const{scrollLeft:t,scrollWidth:e,clientWidth:i}=this.scrollContainer,s=t/e,r=(t+i)/e;this.emit("scroll",s,r)})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.reRender()),100)})),this.resizeObserver.observe(this.scrollContainer)}initDrag(){this.wrapper.addEventListener("mousedown",(t=>{const e=t.clientX,i=t=>{if(Math.abs(t.clientX-e)>=5){this.isDragging=!0;const e=this.wrapper.getBoundingClientRect();this.emit("drag",Math.max(0,Math.min(1,(t.clientX-e.left)/e.width)))}},s=()=>{document.removeEventListener("mousemove",i),document.removeEventListener("mouseup",s),this.isDragging=!1};document.addEventListener("mousemove",i),document.addEventListener("mouseup",s)}))}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"});return e.innerHTML=`\n <style>\n :host {\n user-select: none;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n }\n :host .noScrollbar {\n scrollbar-color: transparent;\n scrollbar-width: none;\n }\n :host .noScrollbar::-webkit-scrollbar {\n display: none;\n -webkit-appearance: none;\n }\n :host .wrapper {\n position: relative;\n overflow: visible;\n z-index: 2;\n }\n :host .canvases {\n position: relative;\n height: ${this.options.height}px;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n height: ${this.options.height}px;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n overflow: hidden;\n }\n :host .cursor {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n width: ${this.options.cursorWidth}px;\n background-color: ${this.options.cursorColor||this.options.progressColor};\n }\n </style>\n\n <div class="scroll" part="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress"></div>\n <div class="cursor" part="cursor"></div>\n </div>\n </div>\n `,[t,e]}setOptions(t){this.options=t,this.reRender()}getWrapper(){return this.wrapper}getScroll(){return this.scrollContainer.scrollLeft}destroy(){this.container.remove(),this.resizeObserver?.disconnect()}delay(t,e=10){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}async renderPeaks(t,e,i,s){const r=null==this.options.barWidth||isNaN(this.options.barWidth)?1:this.options.barWidth*s,n=null==this.options.barGap||isNaN(this.options.barGap)?this.options.barWidth?r/2:0:this.options.barGap*s,o=this.options.barRadius||0,h=this.options.barHeight||1,l=t.getChannelData(0),c=l.length,d=Math.floor(e/(r+n))/c,p=i/2,u=1===t.numberOfChannels,m=u?l:t.getChannelData(1),g=u&&m.some((t=>t<0)),v=(t,i)=>{let a=0,u=0,v=0;const f=document.createElement("canvas");f.width=Math.round(e*(i-t)/c),f.height=this.options.height,f.style.width=`${Math.floor(f.width/s)}px`,f.style.height=`${this.options.height}px`,f.style.left=`${Math.floor(t*e/s/c)}px`,this.canvasWrapper.appendChild(f);const y=f.getContext("2d",{desynchronized:!0});y.beginPath(),y.fillStyle=this.options.waveColor??"",y.roundRect||(y.roundRect=y.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*d);if(i>a){const t=Math.round(u*p*h),e=Math.round(v*p*h);y.roundRect(a*(r+n),p-t,r,t+(e||1),o),a=i,u=0,v=0}const s=g?l[e]:Math.abs(l[e]),c=g?m[e]:Math.abs(m[e]);s>u&&(u=s),(g?c<-v:c>v)&&(v=c<0?-c:c)}y.fill(),y.closePath();const b=f.cloneNode();this.progressWrapper.appendChild(b);const w=b.getContext("2d",{desynchronized:!0});f.width>0&&f.height>0&&w.drawImage(f,0,0),w.globalCompositeOperation="source-in",w.fillStyle=this.options.progressColor??"",w.fillRect(0,0,f.width,f.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:f,scrollWidth:y,clientWidth:b}=this.scrollContainer,w=c/y;let W=Math.min(a.MAX_CANVAS_WIDTH,b);W-=W%((r+n)/s);const C=Math.floor(Math.abs(f)*w),E=Math.ceil(C+W*w);v(C,E);const M=E-C;for(let t=E;t<c;t+=M)await this.delay((()=>{v(t,Math.min(c,t+M))}));for(let t=C-1;t>=0;t-=M)await this.delay((()=>{v(Math.max(0,t-M),t)}))}render(t){const e=window.devicePixelRatio||1,i=this.scrollContainer.clientWidth,s=Math.ceil(t.duration*(this.options.minPxPerSec||0));this.isScrolling=s>i;const r=this.options.fillParent&&!this.isScrolling,n=(r?i:s)*e,{height:o}=this.options;this.wrapper.style.width=r?"100%":`${s}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.cursor.style.backgroundColor=`${this.options.cursorColor||this.options.progressColor}`,this.cursor.style.width=`${this.options.cursorWidth}px`,this.canvasWrapper.style.height=`${this.options.height}px`,this.renderPeaks(t,n,o,e),this.audioData=t,this.emit("render")}reRender(){if(!this.audioData)return;const t=this.progressWrapper.clientWidth;this.render(this.audioData);const e=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=e-t}zoom(t){this.options.minPxPerSec=t,this.reRender()}scrollIntoView(t,e=!1){const{clientWidth:i,scrollLeft:s,scrollWidth:r}=this.scrollContainer,n=r*t,o=i/2;if(n>s+(e&&this.options.autoCenter&&!this.isDragging?o:i)||n<s)if(this.options.autoCenter&&!this.isDragging){const t=o/20;n-(s+o)>=t&&n<s+i?this.scrollContainer.scrollLeft+=t:this.scrollContainer.scrollLeft=n-o}else if(this.isDragging){const t=10;this.scrollContainer.scrollLeft=n<s?n-t:n-i+t}else this.scrollContainer.scrollLeft=n;{const{scrollLeft:t}=this.scrollContainer,e=t/r,s=(t+i)/r;this.emit("scroll",e,s)}}renderProgress(t,e){isNaN(t)||(this.progressWrapper.style.width=100*t+"%",this.cursor.style.left=100*t+"%",this.cursor.style.marginLeft=100===Math.round(100*t)?`-${this.options.cursorWidth}px`:"",this.isScrolling&&this.options.autoScroll&&this.scrollIntoView(t,e))}}a.MAX_CANVAS_WIDTH=4e3;const h=a,l=class extends i{constructor(){super(...arguments),this.unsubscribe=()=>{}}start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}},c={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class d extends o{static create(t){return new d(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.canPlay=!1,this.subscriptions=[],this.options=Object.assign({},c,t),this.timer=new l,this.renderer=new h(this.options.container,this.options),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent(),this.initPlugins();const e=this.options.url||this.options.media?.currentSrc||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}setOptions(t){this.options={...this.options,...t},this.renderer.setOptions(this.options),t.audioRate&&this.setPlaybackRate(t.audioRate)}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",t)})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.onMediaEvent("ended",(()=>{this.emit("finish")})),this.onMediaEvent("canplay",(()=>{this.canPlay=!0,this.emit("canplay",this.getDuration())})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(t=>{this.options.interact&&(this.canPlay&&this.seekTo(t),this.emit("interaction"),this.emit("click",t))})),this.renderer.on("scroll",((t,e)=>{const i=this.getDuration();this.emit("scroll",t*i,e*i)})),this.renderer.on("render",(()=>{this.emit("redraw")})));{let t;this.subscriptions.push(this.renderer.on("drag",(e=>{this.options.interact&&(this.canPlay&&(this.renderer.renderProgress(e),clearTimeout(t),t=setTimeout((()=>{this.seekTo(e)}),this.isPlaying()?0:200)),this.emit("interaction"),this.emit("drag",e))})))}}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",t),this.emit("audioprocess",t)})))}initReadyEvent(){const t=()=>{this.decodedData&&this.canPlay&&this.emit("ready",this.getDuration())};this.subscriptions.push(this.on("decode",t),this.on("canplay",t))}initPlugins(){this.options.plugins?.length&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init(this),this.plugins.push(t),t}getWrapper(){return this.renderer.getWrapper()}getScroll(){return this.renderer.getScroll()}getActivePlugins(){return this.plugins}async load(t,e,i){if(this.decodedData=null,this.canPlay=!1,this.emit("load",t),e)this.setSrc(t),i||(i=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getMediaElement().duration)))}))||0),this.decodedData=r.createBuffer(e,i);else{const e=await n(t);this.setSrc(t,e),this.decodedData=await r.decode(e,this.options.sampleRate)}this.emit("decode",this.getDuration()),this.renderer.render(this.decodedData)}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}getDuration(){const t=super.getDuration();return t>0&&t<1/0?t:this.decodedData?.duration||0}toggleInteraction(t){this.options.interact=t}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}playPause(){return this.isPlaying()?(this.pause(),Promise.resolve()):this.play()}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.renderer.destroy(),super.destroy()}}const p=d,u={height:50,overlayColor:"rgba(100, 100, 100, 0.1)",insertPosition:"afterend"};class m extends s{constructor(t){super(t),this.miniWavesurfer=null,this.container=null,this.options=Object.assign({},u,t),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay()}static create(t){return new m(t)}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");this.options.container?("string"==typeof this.options.container?this.container=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(this.container=this.options.container),this.container?.appendChild(this.minimapWrapper)):(this.container=this.wavesurfer.getWrapper().parentElement,this.container?.insertAdjacentElement(this.options.insertPosition,this.minimapWrapper)),this.initWaveSurferEvents()}initMinimapWrapper(){const t=document.createElement("div");return t.style.position="relative",t.setAttribute("part","minimap"),t}initOverlay(){const t=document.createElement("div");return t.setAttribute("style","position: absolute; z-index: 2; left: 0; top: 0; bottom: 0; transition: left 100ms ease-out; pointer-events: none;"),t.style.backgroundColor=this.options.overlayColor,this.minimapWrapper.appendChild(t),t}initMinimap(){if(this.miniWavesurfer&&(this.miniWavesurfer.destroy(),this.miniWavesurfer=null),!this.wavesurfer)return;const t=this.wavesurfer.getDecodedData(),e=this.wavesurfer.getMediaElement();t&&e&&(this.miniWavesurfer=p.create({...this.options,container:this.minimapWrapper,minPxPerSec:0,fillParent:!0,media:e,peaks:[t.getChannelData(0)],duration:t.duration}),this.subscriptions.push(this.miniWavesurfer.on("ready",(()=>{this.emit("ready")})),this.miniWavesurfer.on("interaction",(()=>{this.wavesurfer&&this.miniWavesurfer&&this.wavesurfer.setTime(this.miniWavesurfer.getCurrentTime()),this.emit("interaction")}))))}getOverlayWidth(){const t=this.wavesurfer?.getWrapper().clientWidth||1;return Math.round(this.minimapWrapper.clientWidth/t*100)}onRedraw(){const t=this.getOverlayWidth();this.overlay.style.width=`${t}%`}onScroll(t){if(!this.wavesurfer)return;const e=this.wavesurfer.getDuration();this.overlay.style.left=t/e*100+"%"}initWaveSurferEvents(){this.wavesurfer&&this.subscriptions.push(this.wavesurfer.on("decode",(()=>{this.initMinimap()})),this.wavesurfer.on("scroll",(t=>{this.onScroll(t)})),this.wavesurfer.on("redraw",(()=>{this.onRedraw()})))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}}const g=m;return e.default})()));