wavesurfer.js 7.0.0-alpha.4 → 7.0.0-alpha.40

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 (46) hide show
  1. package/README.md +64 -17
  2. package/dist/base-plugin.d.ts +14 -5
  3. package/dist/base-plugin.js +5 -1
  4. package/dist/decoder.d.ts +8 -10
  5. package/dist/decoder.js +37 -44
  6. package/dist/event-emitter.d.ts +16 -10
  7. package/dist/event-emitter.js +38 -16
  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 +32 -11
  12. package/dist/player.js +55 -39
  13. package/dist/plugins/envelope.d.ts +57 -0
  14. package/dist/plugins/envelope.js +305 -0
  15. package/dist/plugins/envelope.min.js +1 -0
  16. package/dist/plugins/minimap.d.ts +34 -0
  17. package/dist/plugins/minimap.js +99 -0
  18. package/dist/plugins/minimap.min.js +1 -0
  19. package/dist/plugins/multitrack.d.ts +117 -0
  20. package/dist/plugins/multitrack.js +506 -0
  21. package/dist/plugins/multitrack.min.js +1 -0
  22. package/dist/plugins/record.d.ts +26 -0
  23. package/dist/plugins/record.js +125 -0
  24. package/dist/plugins/record.min.js +1 -0
  25. package/dist/plugins/regions.d.ts +88 -41
  26. package/dist/plugins/regions.js +365 -170
  27. package/dist/plugins/regions.min.js +1 -0
  28. package/dist/plugins/spectrogram.d.ts +24 -0
  29. package/dist/plugins/spectrogram.js +165 -0
  30. package/dist/plugins/timeline.d.ts +41 -0
  31. package/dist/plugins/timeline.js +135 -0
  32. package/dist/plugins/timeline.min.js +1 -0
  33. package/dist/renderer.d.ts +25 -12
  34. package/dist/renderer.js +211 -157
  35. package/dist/timer.d.ts +1 -1
  36. package/dist/wavesurfer.d.ts +134 -0
  37. package/dist/wavesurfer.js +213 -0
  38. package/dist/wavesurfer.min.js +1 -1
  39. package/package.json +49 -24
  40. package/dist/index.d.ts +0 -104
  41. package/dist/index.js +0 -188
  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
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Envelope is a visual UI for controlling the audio volume and add fade-in and fade-out effects.
3
+ */
4
+ import BasePlugin, { type WaveSurferPluginParams } 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
+ export type EnvelopePluginEvents = {
29
+ 'fade-in-change': [time: number];
30
+ 'fade-out-change': [time: number];
31
+ 'volume-change': [volume: number];
32
+ };
33
+ declare class EnvelopePlugin extends BasePlugin<EnvelopePluginEvents, EnvelopePluginOptions> {
34
+ protected options: EnvelopePluginOptions & typeof defaultOptions;
35
+ private svg;
36
+ private audioContext;
37
+ private gainNode;
38
+ private volume;
39
+ private isFadingIn;
40
+ private isFadingOut;
41
+ constructor(options: EnvelopePluginOptions);
42
+ static create(options: EnvelopePluginOptions): EnvelopePlugin;
43
+ /** Called by wavesurfer, don't call manually */
44
+ init(params: WaveSurferPluginParams): void;
45
+ private makeDraggable;
46
+ private renderPolyline;
47
+ private initSvg;
48
+ destroy(): void;
49
+ private initWebAudio;
50
+ private naturalVolume;
51
+ private onVolumeChange;
52
+ private initFadeEffects;
53
+ getCurrentVolume(): number;
54
+ setStartTime(time: number): void;
55
+ setEndTime(time: number): void;
56
+ }
57
+ export default EnvelopePlugin;
@@ -0,0 +1,305 @@
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
+ const defaultOptions = {
6
+ fadeInStart: 0,
7
+ fadeOutEnd: 0,
8
+ fadeInEnd: 0,
9
+ fadeOutStart: 0,
10
+ lineWidth: 4,
11
+ lineColor: 'rgba(0, 0, 255, 0.5)',
12
+ dragPointSize: 10,
13
+ dragPointFill: 'rgba(255, 255, 255, 0.8)',
14
+ dragPointStroke: 'rgba(255, 255, 255, 0.8)',
15
+ };
16
+ class EnvelopePlugin extends BasePlugin {
17
+ constructor(options) {
18
+ super(options);
19
+ this.svg = null;
20
+ this.audioContext = null;
21
+ this.gainNode = null;
22
+ this.volume = 1;
23
+ this.isFadingIn = false;
24
+ this.isFadingOut = false;
25
+ this.options = Object.assign({}, defaultOptions, options);
26
+ this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
27
+ this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
28
+ this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
29
+ this.volume = this.options.volume ?? 1;
30
+ }
31
+ static create(options) {
32
+ return new EnvelopePlugin(options);
33
+ }
34
+ /** Called by wavesurfer, don't call manually */
35
+ init(params) {
36
+ super.init(params);
37
+ if (!this.wavesurfer) {
38
+ throw Error('WaveSurfer is not initialized');
39
+ }
40
+ this.subscriptions.push(this.wavesurfer.once('decode', (duration) => {
41
+ this.options.fadeInStart = this.options.fadeInStart || 0;
42
+ this.options.fadeOutEnd = this.options.fadeOutEnd || duration;
43
+ this.options.fadeInEnd = this.options.fadeInEnd || this.options.fadeInStart;
44
+ this.options.fadeOutStart = this.options.fadeOutStart || this.options.fadeOutEnd;
45
+ this.initWebAudio();
46
+ this.initSvg();
47
+ this.initFadeEffects();
48
+ }));
49
+ let delay;
50
+ this.subscriptions.push(this.wavesurfer.on('zoom', () => {
51
+ if (delay)
52
+ clearTimeout(delay);
53
+ delay = setTimeout(() => {
54
+ this.svg?.remove();
55
+ this.initSvg();
56
+ }, 100);
57
+ }));
58
+ }
59
+ makeDraggable(draggable, onDrag) {
60
+ draggable.addEventListener('mousedown', (e) => {
61
+ let x = e.clientX;
62
+ let y = e.clientY;
63
+ const wasInteractive = this.wavesurfer?.options.interact || true;
64
+ let delay;
65
+ // Make the wavesurfer ignore clicks when we're dragging
66
+ this.wavesurfer?.toggleInteraction(false);
67
+ const move = (e) => {
68
+ const dx = e.clientX - x;
69
+ const dy = e.clientY - y;
70
+ x = e.clientX;
71
+ y = e.clientY;
72
+ onDrag(dx, dy);
73
+ };
74
+ const up = () => {
75
+ document.removeEventListener('mousemove', move);
76
+ document.removeEventListener('mouseup', up);
77
+ // Restore interactive state
78
+ if (delay)
79
+ clearTimeout(delay);
80
+ delay = setTimeout(() => {
81
+ this.wavesurfer?.toggleInteraction(wasInteractive);
82
+ }, 100);
83
+ };
84
+ document.addEventListener('mousemove', move);
85
+ document.addEventListener('mouseup', up);
86
+ e.preventDefault();
87
+ e.stopPropagation();
88
+ });
89
+ }
90
+ renderPolyline() {
91
+ if (!this.svg || !this.wrapper || !this.wavesurfer)
92
+ return;
93
+ const polyline = this.svg.querySelector('polyline');
94
+ const points = polyline.points;
95
+ const top = points.getItem(1).y;
96
+ const width = this.wrapper.clientWidth;
97
+ const duration = this.wavesurfer.getDuration();
98
+ points.getItem(0).x = (this.options.fadeInStart / duration) * width;
99
+ points.getItem(3).x = (this.options.fadeOutEnd / duration) * width;
100
+ const line = this.svg.querySelector('line');
101
+ line.setAttribute('x1', points.getItem(1).x.toString());
102
+ line.setAttribute('x2', points.getItem(2).x.toString());
103
+ line.setAttribute('y1', top.toString());
104
+ line.setAttribute('y2', top.toString());
105
+ const circles = this.svg.querySelectorAll('circle');
106
+ for (let i = 0; i < circles.length; i++) {
107
+ const circle = circles[i];
108
+ const point = polyline.points.getItem(i + 1);
109
+ circle.setAttribute('cx', point.x.toString());
110
+ circle.setAttribute('cy', top.toString());
111
+ }
112
+ }
113
+ initSvg() {
114
+ if (!this.wrapper || !this.wavesurfer)
115
+ return;
116
+ const width = this.wrapper.clientWidth;
117
+ const height = this.wrapper.clientHeight;
118
+ const duration = this.wavesurfer.getDuration();
119
+ // SVG element
120
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
121
+ svg.setAttribute('width', '100%');
122
+ svg.setAttribute('height', '100%');
123
+ svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
124
+ svg.setAttribute('preserveAspectRatio', 'none');
125
+ svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;');
126
+ this.svg = svg;
127
+ // A polyline representing the envelope
128
+ const polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
129
+ polyline.setAttribute('points', '0,0 0,0 0,0 0,0');
130
+ polyline.setAttribute('stroke', this.options.lineColor);
131
+ polyline.setAttribute('stroke-width', this.options.lineWidth);
132
+ polyline.setAttribute('fill', 'none');
133
+ polyline.setAttribute('style', 'pointer-events: none');
134
+ svg.appendChild(polyline);
135
+ // Draggable top line
136
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
137
+ line.setAttribute('stroke', 'none');
138
+ line.setAttribute('stroke-width', (this.options.lineWidth * 3).toString());
139
+ line.setAttribute('style', 'cursor: ns-resize; pointer-events: all;');
140
+ svg.appendChild(line);
141
+ const points = polyline.points;
142
+ const offset = this.options.dragPointSize / 2;
143
+ const top = height - this.volume * height + offset;
144
+ points.getItem(0).x = (this.options.fadeInStart / duration) * width;
145
+ points.getItem(0).y = height;
146
+ points.getItem(1).x = (this.options.fadeInEnd / duration) * width;
147
+ points.getItem(1).y = top;
148
+ points.getItem(2).x = (this.options.fadeOutStart / duration) * width;
149
+ points.getItem(2).y = top;
150
+ points.getItem(3).x = (this.options.fadeOutEnd / duration) * width;
151
+ points.getItem(3).y = height;
152
+ // Drag points
153
+ const dragPoints = [1, 2];
154
+ dragPoints.forEach(() => {
155
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
156
+ circle.setAttribute('r', (this.options.dragPointSize / 2).toString());
157
+ circle.setAttribute('fill', this.options.dragPointFill);
158
+ circle.setAttribute('stroke', this.options.dragPointStroke || this.options.dragPointFill);
159
+ circle.setAttribute('stroke-width', '2');
160
+ circle.setAttribute('style', 'cursor: ew-resize; pointer-events: all;');
161
+ svg.appendChild(circle);
162
+ });
163
+ this.wrapper.appendChild(svg);
164
+ // Initial polyline
165
+ this.renderPolyline();
166
+ // On top line drag
167
+ const onDragY = (dy) => {
168
+ const newTop = points.getItem(1).y + dy - offset;
169
+ if (newTop < -0.5 || newTop > height)
170
+ return;
171
+ points.getItem(1).y = newTop + offset;
172
+ points.getItem(2).y = newTop + offset;
173
+ this.renderPolyline();
174
+ const newVolume = Math.min(1, Math.max(0, (height - newTop) / height));
175
+ this.onVolumeChange(newVolume);
176
+ this.renderPolyline();
177
+ };
178
+ // On points drag
179
+ const onDragX = (dx, dy, index) => {
180
+ const point = polyline.points.getItem(index);
181
+ const newX = point.x + dx;
182
+ const newTime = (newX / width) * duration;
183
+ if ((index === 1 && newTime > this.options.fadeOutStart) || newTime < this.options.fadeInStart)
184
+ return;
185
+ if ((index === 2 && newTime < this.options.fadeInEnd) || newTime > this.options.fadeOutEnd)
186
+ return;
187
+ point.x = newX;
188
+ if (index === 1) {
189
+ this.options.fadeInEnd = newTime;
190
+ this.emit('fade-in-change', newTime);
191
+ }
192
+ else if (index === 2) {
193
+ this.options.fadeOutStart = newTime;
194
+ this.emit('fade-out-change', newTime);
195
+ }
196
+ // Also allow dragging points vertically
197
+ if (dy > 1 || dy < -1) {
198
+ onDragY(dy);
199
+ }
200
+ else {
201
+ this.renderPolyline();
202
+ }
203
+ };
204
+ // Draggable top line of the polyline
205
+ this.makeDraggable(line, (_, dy) => onDragY(dy));
206
+ // Make each point draggable
207
+ const draggables = svg.querySelectorAll('circle');
208
+ for (let i = 0; i < draggables.length; i++) {
209
+ const index = i + 1;
210
+ this.makeDraggable(draggables[i], (dx, dy) => onDragX(dx, dy, index));
211
+ }
212
+ }
213
+ destroy() {
214
+ this.svg?.remove();
215
+ super.destroy();
216
+ }
217
+ initWebAudio() {
218
+ const audio = this.wavesurfer?.getMediaElement();
219
+ if (!audio)
220
+ return null;
221
+ this.volume = this.options.volume ?? audio.volume;
222
+ // Create an AudioContext
223
+ const audioContext = new window.AudioContext();
224
+ // Create a GainNode for controlling the volume
225
+ this.gainNode = audioContext.createGain();
226
+ this.gainNode.gain.value = this.volume;
227
+ // Create a MediaElementAudioSourceNode using the audio element
228
+ const source = audioContext.createMediaElementSource(audio);
229
+ // Connect the source to the GainNode, and the GainNode to the destination (speakers)
230
+ source.connect(this.gainNode);
231
+ this.gainNode.connect(audioContext.destination);
232
+ this.audioContext = audioContext;
233
+ }
234
+ naturalVolume(value) {
235
+ const minValue = 0.0001;
236
+ const maxValue = 1;
237
+ const exponent = 3; // Adjust the exponent to change the curve of the volume control
238
+ const interpolatedValue = minValue + (maxValue - minValue) * Math.pow(value, exponent);
239
+ return interpolatedValue;
240
+ }
241
+ onVolumeChange(volume) {
242
+ volume = this.naturalVolume(volume);
243
+ this.volume = volume;
244
+ this.emit('volume-change', volume);
245
+ if (!this.gainNode)
246
+ return;
247
+ this.gainNode.gain.value = volume;
248
+ }
249
+ initFadeEffects() {
250
+ if (!this.audioContext || !this.wavesurfer)
251
+ return;
252
+ const unsub = this.wavesurfer.on('timeupdate', (currentTime) => {
253
+ if (!this.audioContext || !this.gainNode)
254
+ return;
255
+ if (!this.wavesurfer?.isPlaying())
256
+ return;
257
+ if (this.audioContext.state === 'suspended') {
258
+ this.audioContext.resume();
259
+ }
260
+ // Fade in
261
+ if (!this.isFadingIn && currentTime >= this.options.fadeInStart && currentTime <= this.options.fadeInEnd) {
262
+ this.isFadingIn = true;
263
+ // Set the initial gain (volume) to 0 (silent)
264
+ this.gainNode.gain.setValueAtTime(0, this.audioContext.currentTime);
265
+ // Set the target gain (volume) to 1 (full volume) over N seconds
266
+ this.gainNode.gain.linearRampToValueAtTime(this.volume, this.audioContext.currentTime + (this.options.fadeInEnd - currentTime));
267
+ return;
268
+ }
269
+ // Fade out
270
+ if (!this.isFadingOut && currentTime >= this.options.fadeOutStart && currentTime <= this.options.fadeOutEnd) {
271
+ this.isFadingOut = true;
272
+ // Set the target gain (volume) to 0 (silent) over N seconds
273
+ this.gainNode.gain.linearRampToValueAtTime(0, this.audioContext.currentTime + (this.options.fadeOutEnd - currentTime));
274
+ return;
275
+ }
276
+ // Reset fade in/out
277
+ let cancelRamp = false;
278
+ if (this.isFadingIn && (currentTime < this.options.fadeInStart || currentTime > this.options.fadeInEnd)) {
279
+ this.isFadingIn = false;
280
+ cancelRamp = true;
281
+ }
282
+ if (this.isFadingOut && (currentTime < this.options.fadeOutStart || currentTime >= this.options.fadeOutEnd)) {
283
+ this.isFadingOut = false;
284
+ cancelRamp = true;
285
+ }
286
+ if (cancelRamp) {
287
+ this.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime);
288
+ this.gainNode.gain.value = this.volume;
289
+ }
290
+ });
291
+ this.subscriptions.push(unsub);
292
+ }
293
+ getCurrentVolume() {
294
+ return this.gainNode ? this.gainNode.gain.value : this.volume;
295
+ }
296
+ setStartTime(time) {
297
+ this.options.fadeInStart = time;
298
+ this.renderPolyline();
299
+ }
300
+ setEndTime(time) {
301
+ this.options.fadeOutEnd = time;
302
+ this.renderPolyline();
303
+ }
304
+ }
305
+ 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}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}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:()=>o});var t=i(284);const e={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 n extends t.Z{constructor(t){super(t),this.svg=null,this.audioContext=null,this.gainNode=null,this.volume=1,this.isFadingIn=!1,this.isFadingOut=!1,this.options=Object.assign({},e,t),this.options.lineColor=this.options.lineColor||e.lineColor,this.options.dragPointFill=this.options.dragPointFill||e.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||e.dragPointStroke,this.volume=this.options.volume??1}static create(t){return new n(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");let e;this.subscriptions.push(this.wavesurfer.once("decode",(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.initWebAudio(),this.initSvg(),this.initFadeEffects()}))),this.subscriptions.push(this.wavesurfer.on("zoom",(()=>{e&&clearTimeout(e),e=setTimeout((()=>{this.svg?.remove(),this.initSvg()}),100)})))}makeDraggable(t,e){t.addEventListener("mousedown",(t=>{let i=t.clientX,s=t.clientY;const n=this.wavesurfer?.options.interact||!0;let o;this.wavesurfer?.toggleInteraction(!1);const r=t=>{const n=t.clientX-i,o=t.clientY-s;i=t.clientX,s=t.clientY,e(n,o)},a=()=>{document.removeEventListener("mousemove",r),document.removeEventListener("mouseup",a),o&&clearTimeout(o),o=setTimeout((()=>{this.wavesurfer?.toggleInteraction(n)}),100)};document.addEventListener("mousemove",r),document.addEventListener("mouseup",a),t.preventDefault(),t.stopPropagation()}))}renderPolyline(){if(!this.svg||!this.wrapper||!this.wavesurfer)return;const t=this.svg.querySelector("polyline"),e=t.points,i=e.getItem(1).y,s=this.wrapper.clientWidth,n=this.wavesurfer.getDuration();e.getItem(0).x=this.options.fadeInStart/n*s,e.getItem(3).x=this.options.fadeOutEnd/n*s;const o=this.svg.querySelector("line");o.setAttribute("x1",e.getItem(1).x.toString()),o.setAttribute("x2",e.getItem(2).x.toString()),o.setAttribute("y1",i.toString()),o.setAttribute("y2",i.toString());const r=this.svg.querySelectorAll("circle");for(let e=0;e<r.length;e++){const s=r[e],n=t.points.getItem(e+1);s.setAttribute("cx",n.x.toString()),s.setAttribute("cy",i.toString())}}initSvg(){if(!this.wrapper||!this.wavesurfer)return;const t=this.wrapper.clientWidth,e=this.wrapper.clientHeight,i=this.wavesurfer.getDuration(),s=document.createElementNS("http://www.w3.org/2000/svg","svg");s.setAttribute("width","100%"),s.setAttribute("height","100%"),s.setAttribute("viewBox",`0 0 ${t} ${e}`),s.setAttribute("preserveAspectRatio","none"),s.setAttribute("style","position: absolute; left: 0; top: 0; z-index: 4; pointer-events: none;"),this.svg=s;const n=document.createElementNS("http://www.w3.org/2000/svg","polyline");n.setAttribute("points","0,0 0,0 0,0 0,0"),n.setAttribute("stroke",this.options.lineColor),n.setAttribute("stroke-width",this.options.lineWidth),n.setAttribute("fill","none"),n.setAttribute("style","pointer-events: none"),s.appendChild(n);const o=document.createElementNS("http://www.w3.org/2000/svg","line");o.setAttribute("stroke","none"),o.setAttribute("stroke-width",(3*this.options.lineWidth).toString()),o.setAttribute("style","cursor: ns-resize; pointer-events: all;"),s.appendChild(o);const r=n.points,a=this.options.dragPointSize/2,h=e-this.volume*e+a;r.getItem(0).x=this.options.fadeInStart/i*t,r.getItem(0).y=e,r.getItem(1).x=this.options.fadeInEnd/i*t,r.getItem(1).y=h,r.getItem(2).x=this.options.fadeOutStart/i*t,r.getItem(2).y=h,r.getItem(3).x=this.options.fadeOutEnd/i*t,r.getItem(3).y=e,[1,2].forEach((()=>{const t=document.createElementNS("http://www.w3.org/2000/svg","circle");t.setAttribute("r",(this.options.dragPointSize/2).toString()),t.setAttribute("fill",this.options.dragPointFill),t.setAttribute("stroke",this.options.dragPointStroke||this.options.dragPointFill),t.setAttribute("stroke-width","2"),t.setAttribute("style","cursor: ew-resize; pointer-events: all;"),s.appendChild(t)})),this.wrapper.appendChild(s),this.renderPolyline();const u=t=>{const i=r.getItem(1).y+t-a;if(i<-.5||i>e)return;r.getItem(1).y=i+a,r.getItem(2).y=i+a,this.renderPolyline();const s=Math.min(1,Math.max(0,(e-i)/e));this.onVolumeChange(s),this.renderPolyline()},d=(e,s,o)=>{const r=n.points.getItem(o),a=r.x+e,h=a/t*i;1===o&&h>this.options.fadeOutStart||h<this.options.fadeInStart||2===o&&h<this.options.fadeInEnd||h>this.options.fadeOutEnd||(r.x=a,1===o?(this.options.fadeInEnd=h,this.emit("fade-in-change",h)):2===o&&(this.options.fadeOutStart=h,this.emit("fade-out-change",h)),s>1||s<-1?u(s):this.renderPolyline())};this.makeDraggable(o,((t,e)=>u(e)));const l=s.querySelectorAll("circle");for(let t=0;t<l.length;t++){const e=t+1;this.makeDraggable(l[t],((t,i)=>d(t,i,e)))}}destroy(){this.svg?.remove(),super.destroy()}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.gainNode.gain.value=this.volume,e.createMediaElementSource(t).connect(this.gainNode),this.gainNode.connect(e.destination),this.audioContext=e}naturalVolume(t){return 1e-4+.9999*Math.pow(t,3)}onVolumeChange(t){t=this.naturalVolume(t),this.volume=t,this.emit("volume-change",t),this.gainNode&&(this.gainNode.gain.value=t)}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,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.gainNode.gain.value=this.volume)}));this.subscriptions.push(t)}getCurrentVolume(){return this.gainNode?this.gainNode.gain.value:this.volume}setStartTime(t){this.options.fadeInStart=t,this.renderPolyline()}setEndTime(t){this.options.fadeOutEnd=t,this.renderPolyline()}}const o=n})(),s.default})()));
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Minimap is a tiny copy of the main waveform serving as a navigation tool.
3
+ */
4
+ import BasePlugin, { type WaveSurferPluginParams } 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
+ constructor(options: MinimapPluginOptions);
25
+ static create(options: MinimapPluginOptions): MinimapPlugin;
26
+ /** Called by wavesurfer, don't call manually */
27
+ init(params: WaveSurferPluginParams): void;
28
+ private initMinimapWrapper;
29
+ private initOverlay;
30
+ private initMinimap;
31
+ /** Unmount */
32
+ destroy(): void;
33
+ }
34
+ export default MinimapPlugin;
@@ -0,0 +1,99 @@
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.options = Object.assign({}, defaultOptions, options);
16
+ this.minimapWrapper = this.initMinimapWrapper();
17
+ this.overlay = this.initOverlay();
18
+ }
19
+ static create(options) {
20
+ return new MinimapPlugin(options);
21
+ }
22
+ /** Called by wavesurfer, don't call manually */
23
+ init(params) {
24
+ super.init(params);
25
+ if (!this.wavesurfer) {
26
+ throw Error('WaveSurfer is not initialized');
27
+ }
28
+ if (this.options.container) {
29
+ let container = null;
30
+ if (typeof this.options.container === 'string') {
31
+ container = document.querySelector(this.options.container);
32
+ }
33
+ else if (this.options.container instanceof HTMLElement) {
34
+ container = this.options.container;
35
+ }
36
+ container?.appendChild(this.minimapWrapper);
37
+ }
38
+ else {
39
+ this.container?.insertAdjacentElement(this.options.insertPosition, this.minimapWrapper);
40
+ }
41
+ this.subscriptions.push(this.wavesurfer.on('decode', () => {
42
+ this.initMinimap();
43
+ }));
44
+ }
45
+ initMinimapWrapper() {
46
+ const div = document.createElement('div');
47
+ div.style.position = 'relative';
48
+ return div;
49
+ }
50
+ initOverlay() {
51
+ const div = document.createElement('div');
52
+ div.setAttribute('style', 'position: absolute; z-index: 2; left: 0; top: 0; bottom: 0;');
53
+ div.style.backgroundColor = this.options.overlayColor;
54
+ this.minimapWrapper.appendChild(div);
55
+ return div;
56
+ }
57
+ initMinimap() {
58
+ if (!this.wavesurfer || !this.wrapper)
59
+ return;
60
+ const data = this.wavesurfer.getDecodedData();
61
+ const media = this.wavesurfer.getMediaElement();
62
+ if (!data || !media)
63
+ return;
64
+ this.miniWavesurfer = WaveSurfer.create({
65
+ ...this.options,
66
+ container: this.minimapWrapper,
67
+ minPxPerSec: 0,
68
+ fillParent: true,
69
+ media,
70
+ peaks: [data.getChannelData(0)],
71
+ duration: data.duration,
72
+ });
73
+ const overlayWidth = Math.round((this.minimapWrapper.clientWidth / this.wrapper.clientWidth) * 100);
74
+ this.overlay.style.width = `${overlayWidth}%`;
75
+ this.subscriptions.push(this.wavesurfer.on('timeupdate', (currentTime) => {
76
+ const offset = Math.max(0, Math.min((currentTime / data.duration) * 100 - overlayWidth / 2, 100 - overlayWidth)).toFixed(2);
77
+ this.overlay.style.left = `${offset}%`;
78
+ }));
79
+ this.subscriptions.push(this.miniWavesurfer.on('interaction', () => {
80
+ if (this.wavesurfer && this.miniWavesurfer) {
81
+ this.wavesurfer.setTime(this.miniWavesurfer.getCurrentTime());
82
+ }
83
+ if (this.container) {
84
+ this.container.scrollLeft =
85
+ (this.minimapWrapper.scrollLeft / this.minimapWrapper.scrollWidth) * this.container.scrollWidth;
86
+ }
87
+ this.emit('interaction');
88
+ }), this.miniWavesurfer.on('ready', () => {
89
+ this.emit('ready');
90
+ }));
91
+ }
92
+ /** Unmount */
93
+ destroy() {
94
+ this.miniWavesurfer?.destroy();
95
+ this.minimapWrapper.remove();
96
+ super.destroy();
97
+ }
98
+ }
99
+ 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:()=>m});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}init(t){this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper}destroy(){this.subscriptions.forEach((t=>t()))}},n={decode:async function(t){let e;try{e=new AudioContext({sampleRate:3e3})}catch(t){e=new AudioContext({sampleRate:8e3})}const i=e.decodeAudioData(t);return i.finally((()=>e.close())),i},createBuffer:function(t,e){if("number"==typeof t[0]&&(t=[t]),t[0].some((t=>t>1||t<-1))){const e=Math.max(...t[0]);t=t.map((t=>t.map((t=>t/e))))}return{length:t[0].length,duration:e,numberOfChannels:t.length,sampleRate:t[0].length/e,getChannelData:e=>t?.[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};class r extends i{constructor(t,e){super(),this.options={height:0},this.timeout=null,this.isScrolling=!1,this.channelData=null,this.duration=null,this.resizeObserver=null,this.options={...e};let i=null;if("string"==typeof t.container?i=document.querySelector(t.container):t.container instanceof HTMLElement&&(i=t.container),!i)throw new Error("Container not found");const[s,n]=this.initHtml();i.appendChild(s),this.container=s,this.scrollContainer=n.querySelector(".scroll"),this.wrapper=n.querySelector(".wrapper"),this.canvasWrapper=n.querySelector(".canvases"),this.progressWrapper=n.querySelector(".progress"),this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",i)})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.reRender()),100)})),this.resizeObserver.observe(this.scrollContainer)}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 box-sizing: border-box;\n }\n </style>\n\n <div class="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress"></div>\n </div>\n </div>\n `,[t,e]}setOptions(t){this.options=t,this.reRender()}getContainer(){return this.scrollContainer}getWrapper(){return this.wrapper}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 n=null!=this.options.barWidth?this.options.barWidth*s:1,o=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?n/2:0,a=this.options.barRadius??0,h=t[0],l=h.length,d=Math.floor(e/(n+o))/l,c=i/2,p=1===t.length,u=p?h:t[1],m=p&&u.some((t=>t<0)),g=(t,i)=>{let r=0,p=0,g=0;const y=document.createElement("canvas");y.width=Math.round(e*(i-t)/l),y.height=this.options.height,y.style.width=`${Math.floor(y.width/s)}px`,y.style.height=`${this.options.height}px`,y.style.left=`${Math.floor(t*e/s/l)}px`,this.canvasWrapper.appendChild(y);const f=y.getContext("2d",{desynchronized:!0});f.beginPath(),f.fillStyle=this.options.waveColor??"",f.roundRect||(f.roundRect=f.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*d);if(i>r){const t=Math.round(p*c),e=Math.round(g*c);f.roundRect(r*(n+o),c-t,n,t+e||1,a),r=i,p=0,g=0}const s=m?h[e]:Math.abs(h[e]),l=m?u[e]:Math.abs(u[e]);s>p&&(p=s),(m?l<-g:l>g)&&(g=l<0?-l:l)}f.fill(),f.closePath();const v=y.cloneNode();this.progressWrapper.appendChild(v);const b=v.getContext("2d",{desynchronized:!0});y.width>0&&y.height>0&&b.drawImage(y,0,0),b.globalCompositeOperation="source-in",b.fillStyle=this.options.progressColor??"",b.fillRect(0,0,y.width,y.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:y,scrollWidth:f,clientWidth:v}=this.scrollContainer,b=l/f;let w=Math.min(r.MAX_CANVAS_WIDTH,v);w-=w%((n+o)/s);const C=Math.floor(Math.abs(y)*b),W=Math.ceil(C+w*b);g(C,W);const M=W-C;for(let t=W;t<l;t+=M)await this.delay((()=>{g(t,Math.min(l,t+M))}));for(let t=C-1;t>=0;t-=M)await this.delay((()=>{g(Math.max(0,t-M),t)}))}render(t,e){const i=window.devicePixelRatio||1,s=this.scrollContainer.clientWidth,n=Math.ceil(e*(this.options.minPxPerSec||0));this.isScrolling=n>s;const r=this.options.fillParent&&!this.isScrolling,o=(r?s:n)*i,{height:a}=this.options;this.wrapper.style.width=r?"100%":`${n}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.progressWrapper.style.borderRightStyle="solid",this.progressWrapper.style.borderRightColor=`${this.options.cursorColor||this.options.progressColor}`,this.progressWrapper.style.borderRightWidth=`${this.options.cursorWidth}px`,this.canvasWrapper.style.height=`${this.options.height}px`,this.renderPeaks(t,o,a,i),this.channelData=t,this.duration=e}reRender(){if(!this.channelData||!this.duration)return;const t=this.progressWrapper.clientWidth;this.render(this.channelData,this.duration);const e=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=e-t}zoom(t){this.options.minPxPerSec=t,this.reRender()}renderProgress(t,e=!1){if(!isNaN(t)&&(this.progressWrapper.style.width=100*t+"%",this.isScrolling&&this.options.autoCenter)){const{clientWidth:i,scrollLeft:s,scrollWidth:n}=this.scrollContainer,r=n*t,o=i/2,a=o/20;(r>s+(e?o:i)||r<s)&&(r-(s+o)>=a&&r<s+i?this.scrollContainer.scrollLeft+=a:this.scrollContainer.scrollLeft=r-o)}}}r.MAX_CANVAS_WIDTH=4e3;const o=r,a=class extends i{constructor(t){super(),this.subscriptions=[],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})}loadUrl(t){this.media.src=t}destroy(){this.media.pause(),this.subscriptions.forEach((t=>t())),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)}},h=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()}},l={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoCenter:!0};class d extends a{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.options=Object.assign({},l,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.timer=new h,this.renderer=new o({container:this.options.container},this.options),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent(),this.initPlugins();const e=this.options.url||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)}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.getCurrentTime()>=this.getDuration()&&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"))})))}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({wavesurfer:this,container:this.renderer.getContainer(),wrapper:this.renderer.getWrapper()}),this.plugins.push(t),t}getActivePlugins(){return this.plugins}async load(t,e,i){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),this.emit("load",t),e)i||(i=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getMediaElement().duration)))}))||0),this.decodedData=n.createBuffer(e,i);else{const e=await this.fetcher.load(t);this.decodedData=await n.decode(e)}this.renderAudio(),this.emit("decode",this.getDuration()),this.emit("redraw")}renderAudio(){if(!this.decodedData)return;const t=[this.decodedData.getChannelData(0)];this.decodedData.numberOfChannels>1&&t.push(this.decodedData.getChannelData(1)),this.renderer.render(t,this.decodedData.duration)}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.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.renderer.destroy(),super.destroy()}}const c=d,p={height:50,overlayColor:"rgba(100, 100, 100, 0.1)",insertPosition:"afterend"};class u extends s{constructor(t){super(t),this.miniWavesurfer=null,this.options=Object.assign({},p,t),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay()}static create(t){return new u(t)}init(t){if(super.init(t),!this.wavesurfer)throw Error("WaveSurfer is not initialized");if(this.options.container){let t=null;"string"==typeof this.options.container?t=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(t=this.options.container),t?.appendChild(this.minimapWrapper)}else this.container?.insertAdjacentElement(this.options.insertPosition,this.minimapWrapper);this.subscriptions.push(this.wavesurfer.on("decode",(()=>{this.initMinimap()})))}initMinimapWrapper(){const t=document.createElement("div");return t.style.position="relative",t}initOverlay(){const t=document.createElement("div");return t.setAttribute("style","position: absolute; z-index: 2; left: 0; top: 0; bottom: 0;"),t.style.backgroundColor=this.options.overlayColor,this.minimapWrapper.appendChild(t),t}initMinimap(){if(!this.wavesurfer||!this.wrapper)return;const t=this.wavesurfer.getDecodedData(),e=this.wavesurfer.getMediaElement();if(!t||!e)return;this.miniWavesurfer=c.create({...this.options,container:this.minimapWrapper,minPxPerSec:0,fillParent:!0,media:e,peaks:[t.getChannelData(0)],duration:t.duration});const i=Math.round(this.minimapWrapper.clientWidth/this.wrapper.clientWidth*100);this.overlay.style.width=`${i}%`,this.subscriptions.push(this.wavesurfer.on("timeupdate",(e=>{const s=Math.max(0,Math.min(e/t.duration*100-i/2,100-i)).toFixed(2);this.overlay.style.left=`${s}%`}))),this.subscriptions.push(this.miniWavesurfer.on("interaction",(()=>{this.wavesurfer&&this.miniWavesurfer&&this.wavesurfer.setTime(this.miniWavesurfer.getCurrentTime()),this.container&&(this.container.scrollLeft=this.minimapWrapper.scrollLeft/this.minimapWrapper.scrollWidth*this.container.scrollWidth),this.emit("interaction")})),this.miniWavesurfer.on("ready",(()=>{this.emit("ready")})))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}}const m=u;return e.default})()));
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Multitrack isn't a plugin, but rather a helper class for creating a multitrack audio player.
3
+ * Individual tracks are synced and played together. They can be dragged to set their start position.
4
+ */
5
+ import { type WaveSurferOptions } from '../wavesurfer.js';
6
+ import { type EnvelopePluginOptions } from './envelope.js';
7
+ import EventEmitter from '../event-emitter.js';
8
+ export type TrackId = string | number;
9
+ export type TrackOptions = {
10
+ id: TrackId;
11
+ url?: string;
12
+ peaks?: WaveSurferOptions['peaks'];
13
+ draggable?: boolean;
14
+ startPosition: number;
15
+ startCue?: number;
16
+ endCue?: number;
17
+ fadeInEnd?: number;
18
+ fadeOutStart?: number;
19
+ volume?: number;
20
+ markers?: Array<{
21
+ time: number;
22
+ label?: string;
23
+ color?: string;
24
+ }>;
25
+ intro?: {
26
+ endTime: number;
27
+ label?: string;
28
+ color?: string;
29
+ };
30
+ options?: WaveSurferOptions;
31
+ };
32
+ export type MultitrackOptions = {
33
+ container: HTMLElement;
34
+ minPxPerSec?: number;
35
+ cursorColor?: string;
36
+ cursorWidth?: number;
37
+ trackBackground?: string;
38
+ trackBorderColor?: string;
39
+ rightButtonDrag?: boolean;
40
+ envelopeOptions?: EnvelopePluginOptions;
41
+ };
42
+ export type MultitrackEvents = {
43
+ canplay: [];
44
+ 'start-position-change': [{
45
+ id: TrackId;
46
+ startPosition: number;
47
+ }];
48
+ 'start-cue-change': [{
49
+ id: TrackId;
50
+ startCue: number;
51
+ }];
52
+ 'end-cue-change': [{
53
+ id: TrackId;
54
+ endCue: number;
55
+ }];
56
+ 'fade-in-change': [{
57
+ id: TrackId;
58
+ fadeInEnd: number;
59
+ }];
60
+ 'fade-out-change': [{
61
+ id: TrackId;
62
+ fadeOutStart: number;
63
+ }];
64
+ 'volume-change': [{
65
+ id: TrackId;
66
+ volume: number;
67
+ }];
68
+ 'intro-end-change': [{
69
+ id: TrackId;
70
+ endTime: number;
71
+ }];
72
+ drop: [{
73
+ id: TrackId;
74
+ }];
75
+ };
76
+ export type MultitrackTracks = Array<TrackOptions>;
77
+ declare class MultiTrack extends EventEmitter<MultitrackEvents> {
78
+ private tracks;
79
+ private options;
80
+ private audios;
81
+ private wavesurfers;
82
+ private durations;
83
+ private currentTime;
84
+ private maxDuration;
85
+ private rendering;
86
+ private isDragging;
87
+ private frameRequest;
88
+ private timer;
89
+ private subscriptions;
90
+ private timeline;
91
+ static create(tracks: MultitrackTracks, options: MultitrackOptions): MultiTrack;
92
+ constructor(tracks: MultitrackTracks, options: MultitrackOptions);
93
+ private initDurations;
94
+ private initAudio;
95
+ private initAllAudios;
96
+ private initWavesurfer;
97
+ private initAllWavesurfers;
98
+ private initTimeline;
99
+ private updatePosition;
100
+ private setIsDragging;
101
+ private onDrag;
102
+ private findCurrentTracks;
103
+ private startSync;
104
+ play(): void;
105
+ pause(): void;
106
+ isPlaying(): boolean;
107
+ getCurrentTime(): number;
108
+ /** Position percentage from 0 to 1 */
109
+ seekTo(position: number): void;
110
+ /** Set time in seconds */
111
+ setTime(time: number): void;
112
+ zoom(pxPerSec: number): void;
113
+ addTrack(track: TrackOptions): void;
114
+ destroy(): void;
115
+ setSinkId(sinkId: string): Promise<void[]>;
116
+ }
117
+ export default MultiTrack;