wavesurfer.js 7.0.0-alpha.19 → 7.0.0-alpha.20
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.
- package/dist/index.d.ts +15 -1
- package/dist/index.js +8 -0
- package/dist/plugins/envelope.d.ts +56 -0
- package/dist/plugins/envelope.js +279 -0
- package/dist/plugins/multitrack.d.ts +55 -8
- package/dist/plugins/multitrack.js +213 -87
- package/dist/plugins/regions.js +15 -0
- package/dist/renderer.d.ts +1 -0
- package/dist/renderer.js +12 -9
- package/dist/wavesurfer.Minimap.min.js +1 -1
- package/dist/wavesurfer.Multitrack.min.js +1 -1
- package/dist/wavesurfer.Regions.min.js +1 -1
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import WaveSurfer from '../index.js';
|
|
2
2
|
import RegionsPlugin from './regions.js';
|
|
3
3
|
import TimelinePlugin from './timeline.js';
|
|
4
|
-
|
|
4
|
+
import EnvelopePlugin from './envelope.js';
|
|
5
|
+
import EventEmitter from '../event-emitter.js';
|
|
6
|
+
class MultiTrack extends EventEmitter {
|
|
5
7
|
tracks;
|
|
6
8
|
options;
|
|
7
9
|
audios = [];
|
|
@@ -12,10 +14,14 @@ class MultiTrack {
|
|
|
12
14
|
rendering;
|
|
13
15
|
isDragging = false;
|
|
14
16
|
frameRequest = null;
|
|
17
|
+
timer = null;
|
|
18
|
+
subscriptions = [];
|
|
19
|
+
timeline = null;
|
|
15
20
|
static create(tracks, options) {
|
|
16
21
|
return new MultiTrack(tracks, options);
|
|
17
22
|
}
|
|
18
23
|
constructor(tracks, options) {
|
|
24
|
+
super();
|
|
19
25
|
this.tracks = tracks.map((track) => ({
|
|
20
26
|
...track,
|
|
21
27
|
startPosition: track.startPosition || 0,
|
|
@@ -23,98 +29,142 @@ class MultiTrack {
|
|
|
23
29
|
}));
|
|
24
30
|
this.options = options;
|
|
25
31
|
this.rendering = initRendering(this.tracks, this.options);
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.
|
|
32
|
-
this.rendering.addClickHandler((position) => {
|
|
33
|
-
if (this.isDragging)
|
|
34
|
-
return;
|
|
35
|
-
this.seekTo(position * maxDuration);
|
|
36
|
-
});
|
|
37
|
-
this.maxDuration = maxDuration;
|
|
38
|
-
this.initWavesurfers();
|
|
39
|
-
this.initTimeline();
|
|
32
|
+
this.rendering.addDropHandler((trackId) => {
|
|
33
|
+
this.emit('drop', { id: trackId });
|
|
34
|
+
});
|
|
35
|
+
this.initAllAudios().then((durations) => {
|
|
36
|
+
this.initDurations(durations);
|
|
37
|
+
this.initAllWavesurfers();
|
|
40
38
|
this.rendering.containers.forEach((container, index) => {
|
|
41
39
|
const drag = initDragging(container, (delta) => this.onDrag(index, delta), options.rightButtonDrag);
|
|
42
|
-
this.wavesurfers[index].once('destroy', () =>
|
|
43
|
-
drag?.destroy();
|
|
44
|
-
});
|
|
40
|
+
this.wavesurfers[index].once('destroy', () => drag?.destroy());
|
|
45
41
|
});
|
|
42
|
+
this.emit('canplay');
|
|
46
43
|
});
|
|
47
44
|
}
|
|
48
|
-
|
|
49
|
-
this.
|
|
50
|
-
|
|
51
|
-
return
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
initDurations(durations) {
|
|
46
|
+
this.durations = durations;
|
|
47
|
+
this.maxDuration = this.tracks.reduce((max, track, index) => {
|
|
48
|
+
return Math.max(max, track.startPosition + durations[index]);
|
|
49
|
+
}, 0);
|
|
50
|
+
this.rendering.setMainWidth(durations, this.maxDuration);
|
|
51
|
+
this.rendering.addClickHandler((position) => {
|
|
52
|
+
if (this.isDragging)
|
|
53
|
+
return;
|
|
54
|
+
this.seekTo(position * this.maxDuration);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
initAudio(track) {
|
|
58
|
+
const audio = new Audio(track.url);
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
if (!audio.src)
|
|
61
|
+
return resolve(audio);
|
|
62
|
+
audio.addEventListener('loadedmetadata', () => resolve(audio), { once: true });
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async initAllAudios() {
|
|
66
|
+
this.audios = await Promise.all(this.tracks.map((track) => this.initAudio(track)));
|
|
67
|
+
return this.audios.map((a) => (a.src ? a.duration : 0));
|
|
68
|
+
}
|
|
69
|
+
initWavesurfer(track, index) {
|
|
70
|
+
const container = this.rendering.containers[index];
|
|
71
|
+
// Create a wavesurfer instance
|
|
72
|
+
const ws = WaveSurfer.create({
|
|
73
|
+
...track.options,
|
|
74
|
+
container,
|
|
75
|
+
minPxPerSec: 0,
|
|
76
|
+
media: this.audios[index],
|
|
77
|
+
peaks: track.peaks,
|
|
78
|
+
cursorColor: 'transparent',
|
|
79
|
+
cursorWidth: 0,
|
|
80
|
+
interactive: false,
|
|
81
|
+
});
|
|
82
|
+
// Regions and markers
|
|
83
|
+
const wsRegions = ws.registerPlugin(RegionsPlugin, {
|
|
84
|
+
draggable: false,
|
|
85
|
+
resizable: true,
|
|
86
|
+
dragSelection: false,
|
|
87
|
+
});
|
|
88
|
+
this.subscriptions.push(ws.once('decode', () => {
|
|
89
|
+
// Start and end cues
|
|
90
|
+
if (track.startCue != null || track.endCue != null) {
|
|
91
|
+
const { startCue = 0, endCue = this.durations[index] } = track;
|
|
92
|
+
const startCueRegion = wsRegions.add(0, startCue, '', 'rgba(0, 0, 0, 0.7)');
|
|
93
|
+
const endCueRegion = wsRegions.add(endCue, endCue + this.durations[index], '', 'rgba(0, 0, 0, 0.7)');
|
|
94
|
+
// Allow resizing only from one side
|
|
95
|
+
startCueRegion.element.firstElementChild?.remove();
|
|
96
|
+
endCueRegion.element.lastChild?.remove();
|
|
97
|
+
// Update the start and end cues on resize
|
|
98
|
+
this.subscriptions.push(wsRegions.on('region-updated', ({ region }) => {
|
|
99
|
+
this.setIsDragging();
|
|
100
|
+
if (region === startCueRegion || region === endCueRegion) {
|
|
101
|
+
if (region === startCueRegion) {
|
|
102
|
+
track.startCue = region.endTime;
|
|
103
|
+
this.emit('start-cue-change', { id: track.id, startCue: track.startCue });
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
track.endCue = region.startTime;
|
|
107
|
+
this.emit('end-cue-change', { id: track.id, endCue: track.endCue });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
// Render regions
|
|
113
|
+
if (track.regions) {
|
|
114
|
+
track.regions.forEach((params) => {
|
|
115
|
+
const region = wsRegions.add(params.startTime || 0, params.endTime, params.label, params.color);
|
|
116
|
+
if (!params.startTime) {
|
|
117
|
+
region.element.firstElementChild?.remove();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Render markers
|
|
122
|
+
if (track.markers) {
|
|
123
|
+
track.markers.forEach((marker) => {
|
|
124
|
+
wsRegions.add(marker.time, marker.time, marker.label, marker.color);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}));
|
|
128
|
+
// Envelope
|
|
129
|
+
const envelope = ws.registerPlugin(EnvelopePlugin, {
|
|
130
|
+
...this.options.envelopeOptions,
|
|
131
|
+
startTime: track.startCue,
|
|
132
|
+
endTime: track.endCue,
|
|
133
|
+
fadeInEnd: track.fadeInEnd,
|
|
134
|
+
fadeOutStart: track.fadeOutStart,
|
|
135
|
+
volume: track.volume,
|
|
136
|
+
});
|
|
137
|
+
this.subscriptions.push(envelope.on('volume-change', ({ volume }) => {
|
|
138
|
+
this.setIsDragging();
|
|
139
|
+
this.emit('volume-change', { id: track.id, volume });
|
|
140
|
+
}), envelope.on('fade-in-change', ({ time }) => {
|
|
141
|
+
this.setIsDragging();
|
|
142
|
+
this.emit('fade-in-change', { id: track.id, fadeInEnd: time });
|
|
143
|
+
}), envelope.on('fade-out-change', ({ time }) => {
|
|
144
|
+
this.setIsDragging();
|
|
145
|
+
this.emit('fade-out-change', { id: track.id, fadeOutStart: time });
|
|
58
146
|
}));
|
|
147
|
+
return ws;
|
|
59
148
|
}
|
|
60
|
-
|
|
149
|
+
initAllWavesurfers() {
|
|
61
150
|
const wavesurfers = this.tracks.map((track, index) => {
|
|
62
|
-
|
|
63
|
-
const ws = WaveSurfer.create({
|
|
64
|
-
...track.options,
|
|
65
|
-
container: this.rendering.containers[index],
|
|
66
|
-
minPxPerSec: 0,
|
|
67
|
-
media: this.audios[index],
|
|
68
|
-
peaks: track.peaks,
|
|
69
|
-
cursorColor: 'transparent',
|
|
70
|
-
cursorWidth: 0,
|
|
71
|
-
interactive: false,
|
|
72
|
-
});
|
|
73
|
-
const wsRegions = ws.registerPlugin(RegionsPlugin, {
|
|
74
|
-
draggable: false,
|
|
75
|
-
resizable: true,
|
|
76
|
-
dragSelection: false,
|
|
77
|
-
});
|
|
78
|
-
ws.once('decode', () => {
|
|
79
|
-
// Render start and end cues
|
|
80
|
-
if (track.startCue) {
|
|
81
|
-
const region = wsRegions.add(0, track.startCue, '', 'rgba(0, 0, 0, 0.7)');
|
|
82
|
-
region.element.firstElementChild?.remove();
|
|
83
|
-
}
|
|
84
|
-
if (track.endCue) {
|
|
85
|
-
const region = wsRegions.add(track.endCue, track.endCue + this.durations[index], '', 'rgba(0, 0, 0, 0.7)');
|
|
86
|
-
region.element.lastChild?.remove();
|
|
87
|
-
}
|
|
88
|
-
// Render regions
|
|
89
|
-
if (track.regions) {
|
|
90
|
-
track.regions.forEach((params) => {
|
|
91
|
-
const region = wsRegions.add(params.startTime || 0, params.endTime, params.label, params.color);
|
|
92
|
-
if (!params.startTime) {
|
|
93
|
-
region.element.firstElementChild?.remove();
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
// Render markers
|
|
98
|
-
if (track.markers) {
|
|
99
|
-
track.markers.forEach((marker) => {
|
|
100
|
-
wsRegions.add(marker.time, marker.time, marker.label, marker.color);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
return ws;
|
|
151
|
+
return this.initWavesurfer(track, index);
|
|
105
152
|
});
|
|
106
153
|
this.wavesurfers = wavesurfers;
|
|
154
|
+
this.initTimeline();
|
|
107
155
|
}
|
|
108
156
|
initTimeline() {
|
|
109
|
-
this.
|
|
157
|
+
if (this.timeline)
|
|
158
|
+
this.timeline.destroy();
|
|
159
|
+
this.timeline = this.wavesurfers[0].registerPlugin(TimelinePlugin, {
|
|
110
160
|
duration: this.maxDuration,
|
|
111
161
|
container: this.rendering.containers[0].parentElement,
|
|
112
162
|
});
|
|
113
163
|
}
|
|
114
|
-
updatePosition(time) {
|
|
164
|
+
updatePosition(time, autoCenter = false) {
|
|
115
165
|
const precisionSeconds = 0.3;
|
|
116
166
|
this.currentTime = time;
|
|
117
|
-
this.rendering.updateCursor(time / this.maxDuration);
|
|
167
|
+
this.rendering.updateCursor(time / this.maxDuration, autoCenter);
|
|
118
168
|
const isPaused = !this.isPlaying();
|
|
119
169
|
// Update the current time of each audio
|
|
120
170
|
this.tracks.forEach((track, index) => {
|
|
@@ -138,10 +188,15 @@ class MultiTrack {
|
|
|
138
188
|
audio.volume = newVolume;
|
|
139
189
|
});
|
|
140
190
|
}
|
|
141
|
-
|
|
191
|
+
setIsDragging() {
|
|
142
192
|
// Prevent click events when dragging
|
|
143
193
|
this.isDragging = true;
|
|
144
|
-
|
|
194
|
+
if (this.timer)
|
|
195
|
+
clearTimeout(this.timer);
|
|
196
|
+
this.timer = setTimeout(() => (this.isDragging = false), 300);
|
|
197
|
+
}
|
|
198
|
+
onDrag(index, delta) {
|
|
199
|
+
this.setIsDragging();
|
|
145
200
|
const newTime = this.tracks[index].startPosition + delta * this.maxDuration;
|
|
146
201
|
this.onMove(index, newTime);
|
|
147
202
|
}
|
|
@@ -157,7 +212,7 @@ class MultiTrack {
|
|
|
157
212
|
track.startPosition = newStartPosition;
|
|
158
213
|
this.rendering.setContainerOffsets();
|
|
159
214
|
this.updatePosition(this.currentTime);
|
|
160
|
-
this.
|
|
215
|
+
this.emit('start-position-change', { id: track.id, startPosition: newStartPosition });
|
|
161
216
|
}
|
|
162
217
|
}
|
|
163
218
|
findCurrentTracks() {
|
|
@@ -185,7 +240,7 @@ class MultiTrack {
|
|
|
185
240
|
return pos;
|
|
186
241
|
}, this.currentTime);
|
|
187
242
|
if (position > this.currentTime) {
|
|
188
|
-
this.updatePosition(position);
|
|
243
|
+
this.updatePosition(position, true);
|
|
189
244
|
}
|
|
190
245
|
this.frameRequest = requestAnimationFrame(onFrame);
|
|
191
246
|
};
|
|
@@ -204,6 +259,9 @@ class MultiTrack {
|
|
|
204
259
|
isPlaying() {
|
|
205
260
|
return this.audios.some((audio) => !audio.paused);
|
|
206
261
|
}
|
|
262
|
+
getCurrentTime() {
|
|
263
|
+
return this.currentTime;
|
|
264
|
+
}
|
|
207
265
|
seekTo(time) {
|
|
208
266
|
const wasPlaying = this.isPlaying();
|
|
209
267
|
this.updatePosition(time);
|
|
@@ -216,6 +274,25 @@ class MultiTrack {
|
|
|
216
274
|
this.rendering.setMainWidth(this.durations, this.maxDuration);
|
|
217
275
|
this.rendering.setContainerOffsets();
|
|
218
276
|
}
|
|
277
|
+
addTrack(track) {
|
|
278
|
+
const index = this.tracks.findIndex((t) => t.id === track.id);
|
|
279
|
+
if (index !== -1) {
|
|
280
|
+
this.tracks[index] = track;
|
|
281
|
+
this.initAudio(track).then((audio) => {
|
|
282
|
+
this.audios[index] = audio;
|
|
283
|
+
this.durations[index] = audio.duration;
|
|
284
|
+
this.initDurations(this.durations);
|
|
285
|
+
const container = this.rendering.containers[index];
|
|
286
|
+
container.innerHTML = '';
|
|
287
|
+
this.wavesurfers[index].destroy();
|
|
288
|
+
this.wavesurfers[index] = this.initWavesurfer(track, index);
|
|
289
|
+
const drag = initDragging(container, (delta) => this.onDrag(index, delta), this.options.rightButtonDrag);
|
|
290
|
+
this.wavesurfers[index].once('destroy', () => drag?.destroy());
|
|
291
|
+
this.initTimeline();
|
|
292
|
+
this.emit('canplay');
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
219
296
|
destroy() {
|
|
220
297
|
if (this.frameRequest)
|
|
221
298
|
cancelAnimationFrame(this.frameRequest);
|
|
@@ -228,10 +305,18 @@ class MultiTrack {
|
|
|
228
305
|
ws.destroy();
|
|
229
306
|
});
|
|
230
307
|
}
|
|
308
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId
|
|
309
|
+
setSinkId(sinkId) {
|
|
310
|
+
return Promise.all(this.audios.map((item) => {
|
|
311
|
+
const audio = item;
|
|
312
|
+
return audio.setSinkId ? audio.setSinkId(sinkId) : Promise.resolve();
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
231
315
|
}
|
|
232
316
|
function initRendering(tracks, options) {
|
|
233
317
|
let pxPerSec = 0;
|
|
234
318
|
let durations = [];
|
|
319
|
+
let mainWidth = 0;
|
|
235
320
|
// Create a common container for all tracks
|
|
236
321
|
const scroll = document.createElement('div');
|
|
237
322
|
scroll.setAttribute('style', 'width: 100%; overflow-x: scroll; overflow-y: hidden; user-select: none;');
|
|
@@ -247,16 +332,35 @@ function initRendering(tracks, options) {
|
|
|
247
332
|
wrapper.appendChild(cursor);
|
|
248
333
|
const { clientWidth } = wrapper;
|
|
249
334
|
// Create containers for each track
|
|
250
|
-
const containers = tracks.map((
|
|
335
|
+
const containers = tracks.map((track, index) => {
|
|
251
336
|
const container = document.createElement('div');
|
|
337
|
+
container.style.position = 'relative';
|
|
252
338
|
if (options.trackBorderColor && index > 0) {
|
|
253
339
|
const borderDiv = document.createElement('div');
|
|
254
340
|
borderDiv.setAttribute('style', `width: 100%; height: 2px; background-color: ${options.trackBorderColor}`);
|
|
255
341
|
wrapper.appendChild(borderDiv);
|
|
256
342
|
}
|
|
257
|
-
if (options.trackBackground) {
|
|
343
|
+
if (options.trackBackground && track.url) {
|
|
258
344
|
container.style.background = options.trackBackground;
|
|
259
345
|
}
|
|
346
|
+
// No audio on this track, so make it droppable
|
|
347
|
+
if (!track.url) {
|
|
348
|
+
const dropArea = document.createElement('div');
|
|
349
|
+
dropArea.setAttribute('style', `position: absolute; z-index: 10; left: 10px; top: 10px; right: 10px; bottom: 10px; border: 2px dashed ${options.trackBorderColor};`);
|
|
350
|
+
dropArea.addEventListener('dragover', (e) => {
|
|
351
|
+
e.preventDefault();
|
|
352
|
+
dropArea.style.background = options.trackBackground || '';
|
|
353
|
+
});
|
|
354
|
+
dropArea.addEventListener('dragleave', (e) => {
|
|
355
|
+
e.preventDefault();
|
|
356
|
+
dropArea.style.background = '';
|
|
357
|
+
});
|
|
358
|
+
dropArea.addEventListener('drop', (e) => {
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
dropArea.style.background = '';
|
|
361
|
+
});
|
|
362
|
+
container.appendChild(dropArea);
|
|
363
|
+
}
|
|
260
364
|
wrapper.appendChild(container);
|
|
261
365
|
return container;
|
|
262
366
|
});
|
|
@@ -264,10 +368,10 @@ function initRendering(tracks, options) {
|
|
|
264
368
|
const setContainerOffsets = () => {
|
|
265
369
|
containers.forEach((container, i) => {
|
|
266
370
|
const offset = tracks[i].startPosition * pxPerSec;
|
|
267
|
-
|
|
371
|
+
if (durations[i]) {
|
|
372
|
+
container.style.width = `${durations[i] * pxPerSec}px`;
|
|
373
|
+
}
|
|
268
374
|
container.style.transform = `translateX(${offset}px)`;
|
|
269
|
-
if (tracks[i].draggable)
|
|
270
|
-
container.style.cursor = 'move';
|
|
271
375
|
});
|
|
272
376
|
};
|
|
273
377
|
return {
|
|
@@ -278,13 +382,21 @@ function initRendering(tracks, options) {
|
|
|
278
382
|
setMainWidth: (trackDurations, maxDuration) => {
|
|
279
383
|
durations = trackDurations;
|
|
280
384
|
pxPerSec = Math.max(options.minPxPerSec || 0, clientWidth / maxDuration);
|
|
281
|
-
|
|
282
|
-
wrapper.style.width = `${
|
|
385
|
+
mainWidth = pxPerSec * maxDuration;
|
|
386
|
+
wrapper.style.width = `${mainWidth}px`;
|
|
283
387
|
setContainerOffsets();
|
|
284
388
|
},
|
|
285
389
|
// Update cursor position
|
|
286
|
-
updateCursor: (position) => {
|
|
390
|
+
updateCursor: (position, autoCenter) => {
|
|
287
391
|
cursor.style.left = `${Math.min(100, position * 100)}%`;
|
|
392
|
+
// Update scroll
|
|
393
|
+
const { clientWidth, scrollLeft } = scroll;
|
|
394
|
+
const center = clientWidth / 2;
|
|
395
|
+
const minScroll = autoCenter ? center : clientWidth;
|
|
396
|
+
const pos = position * mainWidth;
|
|
397
|
+
if (pos > scrollLeft + minScroll || pos < scrollLeft) {
|
|
398
|
+
scroll.scrollLeft = pos - center;
|
|
399
|
+
}
|
|
288
400
|
},
|
|
289
401
|
// Click to seek
|
|
290
402
|
addClickHandler: (onClick) => {
|
|
@@ -299,6 +411,18 @@ function initRendering(tracks, options) {
|
|
|
299
411
|
destroy: () => {
|
|
300
412
|
scroll.remove();
|
|
301
413
|
},
|
|
414
|
+
// Do something on drop
|
|
415
|
+
addDropHandler: (onDrop) => {
|
|
416
|
+
tracks.forEach((track, index) => {
|
|
417
|
+
if (!track.url) {
|
|
418
|
+
const droppable = containers[index].querySelector('div');
|
|
419
|
+
droppable?.addEventListener('drop', (e) => {
|
|
420
|
+
e.preventDefault();
|
|
421
|
+
onDrop(track.id);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
},
|
|
302
426
|
};
|
|
303
427
|
}
|
|
304
428
|
function initDragging(container, onDrag, rightButtonDrag = false) {
|
|
@@ -316,12 +440,14 @@ function initDragging(container, onDrag, rightButtonDrag = false) {
|
|
|
316
440
|
return;
|
|
317
441
|
const rect = wrapper.getBoundingClientRect();
|
|
318
442
|
dragStart = e.clientX - rect.left;
|
|
443
|
+
container.style.cursor = 'grabbing';
|
|
319
444
|
});
|
|
320
445
|
// Drag end
|
|
321
446
|
const onMouseUp = (e) => {
|
|
322
447
|
if (dragStart != null) {
|
|
323
448
|
e.stopPropagation();
|
|
324
449
|
dragStart = null;
|
|
450
|
+
container.style.cursor = '';
|
|
325
451
|
}
|
|
326
452
|
};
|
|
327
453
|
// Drag move
|
package/dist/plugins/regions.js
CHANGED
|
@@ -163,6 +163,21 @@ class RegionsPlugin extends BasePlugin {
|
|
|
163
163
|
}
|
|
164
164
|
});
|
|
165
165
|
this.regionsContainer.appendChild(div);
|
|
166
|
+
// Check that the label doesn't overlap with other labels
|
|
167
|
+
// If it does, push it down
|
|
168
|
+
const labelLeft = div.getBoundingClientRect().left;
|
|
169
|
+
const labelWidth = div.scrollWidth;
|
|
170
|
+
const overlap = this.regions
|
|
171
|
+
.filter((reg) => {
|
|
172
|
+
const { left } = reg.element.getBoundingClientRect();
|
|
173
|
+
const width = reg.element.scrollWidth;
|
|
174
|
+
return labelLeft < left + width && left < labelLeft + labelWidth;
|
|
175
|
+
})
|
|
176
|
+
.map((reg) => parseFloat(reg.element.style.paddingTop))
|
|
177
|
+
.reduce((sum, val) => sum + val, 0);
|
|
178
|
+
if (overlap > 0) {
|
|
179
|
+
div.style.paddingTop = `${overlap + 1}em`;
|
|
180
|
+
}
|
|
166
181
|
return div;
|
|
167
182
|
}
|
|
168
183
|
createRegion(start, end, title = '') {
|
package/dist/renderer.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ declare class Renderer extends EventEmitter<RendererEvents> {
|
|
|
26
26
|
private canvasWrapper;
|
|
27
27
|
private progressWrapper;
|
|
28
28
|
private timeout;
|
|
29
|
+
private isScrolling;
|
|
29
30
|
constructor(options: RendererOptions);
|
|
30
31
|
getContainer(): HTMLElement;
|
|
31
32
|
getWrapper(): HTMLElement;
|
package/dist/renderer.js
CHANGED
|
@@ -7,6 +7,7 @@ class Renderer extends EventEmitter {
|
|
|
7
7
|
canvasWrapper;
|
|
8
8
|
progressWrapper;
|
|
9
9
|
timeout = null;
|
|
10
|
+
isScrolling = false;
|
|
10
11
|
constructor(options) {
|
|
11
12
|
super();
|
|
12
13
|
this.options = options;
|
|
@@ -201,12 +202,12 @@ class Renderer extends EventEmitter {
|
|
|
201
202
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
202
203
|
const parentWidth = this.options.fillParent ? this.scrollContainer.clientWidth * pixelRatio : 0;
|
|
203
204
|
const scrollWidth = audioData.duration * this.options.minPxPerSec;
|
|
204
|
-
|
|
205
|
-
const width = Math.max(1, isScrolling ? scrollWidth : parentWidth);
|
|
205
|
+
this.isScrolling = scrollWidth > parentWidth;
|
|
206
|
+
const width = Math.max(1, this.isScrolling ? scrollWidth : parentWidth);
|
|
206
207
|
const { height } = this.options;
|
|
207
208
|
// Remember the current cursor position
|
|
208
209
|
const oldCursorPosition = this.progressWrapper.clientWidth;
|
|
209
|
-
this.scrollContainer.style.overflowX = isScrolling ? 'auto' : 'hidden';
|
|
210
|
+
this.scrollContainer.style.overflowX = this.isScrolling ? 'auto' : 'hidden';
|
|
210
211
|
this.wrapper.style.width = `${Math.floor(width / pixelRatio)}px`;
|
|
211
212
|
// Adjust the scroll position so that the cursor stays in the same place
|
|
212
213
|
const newCursortPosition = this.progressWrapper.clientWidth;
|
|
@@ -224,12 +225,14 @@ class Renderer extends EventEmitter {
|
|
|
224
225
|
}
|
|
225
226
|
renderProgress(progress, autoCenter = false) {
|
|
226
227
|
this.progressWrapper.style.width = `${progress * 100}%`;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
this.scrollContainer.scrollLeft
|
|
228
|
+
if (this.isScrolling) {
|
|
229
|
+
const containerWidth = this.scrollContainer.clientWidth;
|
|
230
|
+
const center = containerWidth / 2;
|
|
231
|
+
const progressWidth = this.progressWrapper.clientWidth;
|
|
232
|
+
const minScroll = autoCenter ? center : containerWidth;
|
|
233
|
+
if (progressWidth > this.scrollContainer.scrollLeft + minScroll) {
|
|
234
|
+
this.scrollContainer.scrollLeft = progressWidth - center;
|
|
235
|
+
}
|
|
233
236
|
}
|
|
234
237
|
}
|
|
235
238
|
}
|
|
@@ -1 +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:()=>d});const i=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(t,e){const i=new CustomEvent(String(t),{detail:e});this.eventTarget.dispatchEvent(i)}on(t,e,i){const s=t=>{t instanceof CustomEvent&&e(t.detail)},r=String(t);return this.eventTarget.addEventListener(r,s,{once:i}),()=>this.eventTarget.removeEventListener(r,s)}once(t,e){return this.on(t,e,!0)}},s=class extends i{wavesurfer;container;wrapper;subscriptions=[];options;constructor(t,e){super(),this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper,this.options=e}destroy(){this.subscriptions.forEach((t=>t()))}},r=class extends i{options;container;scrollContainer;wrapper;canvasWrapper;progressWrapper;timeout=null;constructor(t){super(),this.options=t;let e=null;if("string"==typeof this.options.container?e=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(e=this.options.container),!e)throw new Error("Container not found");const i=document.createElement("div"),s=i.attachShadow({mode:"open"});s.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 ${this.options.noScrollbar?"scrollbar-color: transparent;":""}\n }\n :host ::-webkit-scrollbar {\n display: ${this.options.noScrollbar?"none":"auto"};\n }\n :host .wrapper {\n position: relative;\n min-width: 100%;\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 height: ${this.options.height}px;\n image-rendering: pixelated;\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: ${this.options.height}px;\n overflow: hidden;\n box-sizing: border-box;\n border-right-style: solid;\n border-right-width: ${this.options.cursorWidth}px;\n border-right-color: ${this.options.cursorColor||this.options.progressColor};\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 `,this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),e.appendChild(i),this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",{relativeX:i})}))}getContainer(){return this.scrollContainer}getWrapper(){return this.wrapper}destroy(){this.container.remove()}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?this.options.barWidth*s:1,n=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?r/2:0,o=this.options.barRadius??0,a=t[0],h=a.length,l=Math.floor(e/(r+n))/h,d=i/2,p=1===t.length,c=p?a:t[1],u=p&&c.some((t=>t<0)),m=(t,i)=>{let p=0,m=0,y=0;const g=document.createElement("canvas");g.width=Math.round(e*(i-t)/h),g.height=this.options.height,g.style.width=`${Math.floor(g.width/s)}px`,g.style.left=`${Math.floor(t*e/s/h)}px`,this.canvasWrapper.appendChild(g);const v=g.getContext("2d",{desynchronized:!0});v.beginPath(),v.fillStyle=this.options.waveColor,v.roundRect||(v.roundRect=v.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*l);if(i>p){const t=Math.round(m*d),e=Math.round(y*d);v.roundRect(p*(r+n),d-t,r,t+e||1,o),p=i,m=0,y=0}const s=u?a[e]:Math.abs(a[e]),h=u?c[e]:Math.abs(c[e]);s>m&&(m=s),(u?h<-y:h>y)&&(y=h<0?-h:h)}v.fill(),v.closePath();const f=g.cloneNode();this.progressWrapper.appendChild(f);const b=f.getContext("2d",{desynchronized:!0});b.drawImage(g,0,0),b.globalCompositeOperation="source-in",b.fillStyle=this.options.progressColor,b.fillRect(0,0,g.width,g.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:y,scrollWidth:g,clientWidth:v}=this.scrollContainer,f=h/g,b=Math.floor(y*f),w=Math.ceil(Math.min(g,y+v)*f);m(b,w);const C=w-b;for(let t=w;t<h;t+=C)await this.delay((()=>{m(t,Math.min(h,t+C))}));for(let t=b-1;t>=0;t-=C)await this.delay((()=>{m(Math.max(0,t-C),t)}))}render(t){const e=window.devicePixelRatio||1,i=this.options.fillParent?this.scrollContainer.clientWidth*e:0,s=t.duration*this.options.minPxPerSec,r=s>i,n=Math.max(1,r?s:i),{height:o}=this.options,a=this.progressWrapper.clientWidth;this.scrollContainer.style.overflowX=r?"auto":"hidden",this.wrapper.style.width=`${Math.floor(n/e)}px`;const h=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=h-a;const l=[t.getChannelData(0)];t.numberOfChannels>1&&l.push(t.getChannelData(1)),this.renderPeaks(l,n,o,e)}zoom(t,e){this.options.minPxPerSec=e,this.render(t)}renderProgress(t,e=!1){this.progressWrapper.style.width=100*t+"%";const i=this.scrollContainer.clientWidth,s=i/2,r=this.progressWrapper.clientWidth,n=e?s:i;r>this.scrollContainer.scrollLeft+n&&(this.scrollContainer.scrollLeft=r-s)}},n=class extends i{unsubscribe=()=>{};start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}},o={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interactive:!0};class a extends i{options;fetcher;decoder;renderer;player;timer;plugins=[];subscriptions=[];decodedData=null;static create(t){return new a(t)}constructor(t){super(),this.options=Object.assign({},o,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.decoder=new class{audioCtx=null;initAudioContext(t){this.audioCtx=new AudioContext({latencyHint:"playback",sampleRate:t})}constructor(){try{this.initAudioContext(3e3)}catch(t){this.initAudioContext(8e3)}}async decode(t){if(!this.audioCtx)throw new Error("AudioContext is not initialized");return this.audioCtx.decodeAudioData(t)}destroy(){this.audioCtx?.close(),this.audioCtx=null}},this.timer=new n,this.player=new class{media;isExternalMedia=!1;hasPlayedOnce=!1;subscriptions=[];constructor({media:t,autoplay:e}){t?(this.media=t,this.isExternalMedia=!0):this.media=document.createElement("audio"),this.subscriptions.push(this.on("play",(()=>{this.hasPlayedOnce=!0}),{once:!0})),e&&(this.media.autoplay=!0)}on(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}destroy(){this.subscriptions.forEach((t=>{t()})),this.media.pause(),this.isExternalMedia||this.media.remove()}loadUrl(t){this.media.src=t}getCurrentTime(){return this.media.currentTime}play(){this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}seekTo(t){!this.hasPlayedOnce&&navigator.userAgent.match(/(iPad|iPhone|iPod)/g)&&this.media.play()?.then?.((()=>{setTimeout((()=>this.media.pause()),10)})),this.media.currentTime=t}getDuration(){return this.media.duration}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}}({media:this.options.media,autoplay:this.options.autoplay}),this.renderer=new r({container:this.options.container,height:this.options.height,waveColor:this.options.waveColor,progressColor:this.options.progressColor,cursorColor:this.options.cursorColor,cursorWidth:this.options.cursorWidth,minPxPerSec:this.options.minPxPerSec,fillParent:this.options.fillParent,barWidth:this.options.barWidth,barGap:this.options.barGap,barRadius:this.options.barRadius,noScrollbar:this.options.noScrollbar}),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent();const e=this.options.url||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}initPlayerEvents(){this.subscriptions.push(this.player.on("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",{currentTime:t})})),this.player.on("play",(()=>{this.emit("play"),this.timer.start()})),this.player.on("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.player.on("canplay",(()=>{this.emit("canplay",{duration:this.getDuration()})})),this.player.on("seeking",(()=>{this.emit("seeking",{currentTime:this.getCurrentTime()})})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(({relativeX:t})=>{if(this.options.interactive){const e=this.getDuration()*t;this.seekTo(e),this.emit("seekClick",{currentTime:this.getCurrentTime()})}})))}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",{currentTime:t})})))}initReadyEvent(){let t=!1,e=!1;const i=()=>{t&&e&&this.emit("ready",{duration:this.getDuration()})};this.subscriptions.push(this.on("decode",(()=>{t=!0,i()})),this.on("canplay",(()=>{e=!0,i()})),this.player.on("waiting",(()=>{e=!1,t=!1})))}async load(t,e,i){if(this.player.loadUrl(t),null==e){const e=await this.fetcher.load(t),i=await this.decoder.decode(e);this.decodedData=i}else i||(i=await new Promise((t=>{this.player.on("canplay",(()=>t(this.getDuration())),{once:!0})}))||0),this.decodedData={duration:i,numberOfChannels:e.length,sampleRate:e[0].length/i,getChannelData:t=>e[t]};this.renderer.render(this.decodedData),this.emit("decode",{duration:this.decodedData.duration})}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(this.decodedData,t)}play(){this.player.play()}pause(){this.player.pause()}seekTo(t){this.player.seekTo(t)}isPlaying(){return this.player.isPlaying()}getDuration(){return this.player.getDuration()||this.decodedData?.duration||0}getCurrentTime(){return this.player.getCurrentTime()}getVolume(){return this.player.getVolume()}setVolume(t){this.player.setVolume(t)}getMuted(){return this.player.getMuted()}setMuted(t){this.player.setMuted(t)}getPlaybackRate(){return this.player.getPlaybackRate()}setPlaybackRate(t,e){this.player.setPlaybackRate(t,e)}registerPlugin(t,e){const i=new t({wavesurfer:this,container:this.renderer.getContainer(),wrapper:this.renderer.getWrapper()},e);return this.plugins.push(i),i}getDecodedData(){return this.decodedData}getMediaElement(){return this.player.getMediaElement()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.player.destroy(),this.decoder.destroy(),this.renderer.destroy()}}const h=a,l={height:50,overlayColor:"rgba(100, 100, 100, 0.1)"},d=class extends s{options;minimapWrapper;miniWavesurfer=null;overlay;constructor(t,e){super(t,e),this.options=Object.assign({},l,e),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay(),this.subscriptions.push(this.wavesurfer.on("decode",(()=>{this.initMinimap()})))}initMinimapWrapper(){const t=document.createElement("div");return t.style.position="relative",this.container.insertAdjacentElement("afterend",t),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(){const t=this.wavesurfer.getDecodedData(),e=this.wavesurfer.getMediaElement();if(!t||!e)return;this.miniWavesurfer=h.create({...this.options,container:this.minimapWrapper,minPxPerSec:1,fillParent:!0,media:e,url:e.src,peaks:[t.getChannelData(0)],duration:t.duration});const i=Math.round(this.minimapWrapper.clientWidth/this.wrapper.clientWidth*100);this.overlay.style.width=`${i}%`,this.wavesurfer.on("timeupdate",(({currentTime:t})=>{const e=Math.max(0,Math.min(t/this.wavesurfer.getDuration()*100-i/2,100-i)).toFixed(2);this.overlay.style.left=`${e}%`}))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}};return e.default})()));
|
|
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:()=>d});const i=class{eventTarget;constructor(){this.eventTarget=new EventTarget}emit(t,e){const i=new CustomEvent(String(t),{detail:e});this.eventTarget.dispatchEvent(i)}on(t,e,i){const s=t=>{t instanceof CustomEvent&&e(t.detail)},r=String(t);return this.eventTarget.addEventListener(r,s,{once:i}),()=>this.eventTarget.removeEventListener(r,s)}once(t,e){return this.on(t,e,!0)}},s=class extends i{wavesurfer;container;wrapper;subscriptions=[];options;constructor(t,e){super(),this.wavesurfer=t.wavesurfer,this.container=t.container,this.wrapper=t.wrapper,this.options=e}destroy(){this.subscriptions.forEach((t=>t()))}},r=class extends i{options;container;scrollContainer;wrapper;canvasWrapper;progressWrapper;timeout=null;isScrolling=!1;constructor(t){super(),this.options=t;let e=null;if("string"==typeof this.options.container?e=document.querySelector(this.options.container):this.options.container instanceof HTMLElement&&(e=this.options.container),!e)throw new Error("Container not found");const i=document.createElement("div"),s=i.attachShadow({mode:"open"});s.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 ${this.options.noScrollbar?"scrollbar-color: transparent;":""}\n }\n :host ::-webkit-scrollbar {\n display: ${this.options.noScrollbar?"none":"auto"};\n }\n :host .wrapper {\n position: relative;\n min-width: 100%;\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 height: ${this.options.height}px;\n image-rendering: pixelated;\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: ${this.options.height}px;\n overflow: hidden;\n box-sizing: border-box;\n border-right-style: solid;\n border-right-width: ${this.options.cursorWidth}px;\n border-right-color: ${this.options.cursorColor||this.options.progressColor};\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 `,this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),e.appendChild(i),this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",{relativeX:i})}))}getContainer(){return this.scrollContainer}getWrapper(){return this.wrapper}destroy(){this.container.remove()}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?this.options.barWidth*s:1,n=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?r/2:0,o=this.options.barRadius??0,a=t[0],h=a.length,l=Math.floor(e/(r+n))/h,d=i/2,p=1===t.length,c=p?a:t[1],u=p&&c.some((t=>t<0)),m=(t,i)=>{let p=0,m=0,g=0;const y=document.createElement("canvas");y.width=Math.round(e*(i-t)/h),y.height=this.options.height,y.style.width=`${Math.floor(y.width/s)}px`,y.style.left=`${Math.floor(t*e/s/h)}px`,this.canvasWrapper.appendChild(y);const v=y.getContext("2d",{desynchronized:!0});v.beginPath(),v.fillStyle=this.options.waveColor,v.roundRect||(v.roundRect=v.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*l);if(i>p){const t=Math.round(m*d),e=Math.round(g*d);v.roundRect(p*(r+n),d-t,r,t+e||1,o),p=i,m=0,g=0}const s=u?a[e]:Math.abs(a[e]),h=u?c[e]:Math.abs(c[e]);s>m&&(m=s),(u?h<-g:h>g)&&(g=h<0?-h:h)}v.fill(),v.closePath();const f=y.cloneNode();this.progressWrapper.appendChild(f);const b=f.getContext("2d",{desynchronized:!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:g,scrollWidth:y,clientWidth:v}=this.scrollContainer,f=h/y,b=Math.floor(g*f),w=Math.ceil(Math.min(y,g+v)*f);m(b,w);const C=w-b;for(let t=w;t<h;t+=C)await this.delay((()=>{m(t,Math.min(h,t+C))}));for(let t=b-1;t>=0;t-=C)await this.delay((()=>{m(Math.max(0,t-C),t)}))}render(t){const e=window.devicePixelRatio||1,i=this.options.fillParent?this.scrollContainer.clientWidth*e:0,s=t.duration*this.options.minPxPerSec;this.isScrolling=s>i;const r=Math.max(1,this.isScrolling?s:i),{height:n}=this.options,o=this.progressWrapper.clientWidth;this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.wrapper.style.width=`${Math.floor(r/e)}px`;const a=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=a-o;const h=[t.getChannelData(0)];t.numberOfChannels>1&&h.push(t.getChannelData(1)),this.renderPeaks(h,r,n,e)}zoom(t,e){this.options.minPxPerSec=e,this.render(t)}renderProgress(t,e=!1){if(this.progressWrapper.style.width=100*t+"%",this.isScrolling){const t=this.scrollContainer.clientWidth,i=t/2,s=this.progressWrapper.clientWidth,r=e?i:t;s>this.scrollContainer.scrollLeft+r&&(this.scrollContainer.scrollLeft=s-i)}}},n=class extends i{unsubscribe=()=>{};start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}},o={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interactive:!0};class a extends i{options;fetcher;decoder;renderer;player;timer;plugins=[];subscriptions=[];decodedData=null;static create(t){return new a(t)}constructor(t){super(),this.options=Object.assign({},o,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.decoder=new class{audioCtx=null;initAudioContext(t){this.audioCtx=new AudioContext({latencyHint:"playback",sampleRate:t})}constructor(){try{this.initAudioContext(3e3)}catch(t){this.initAudioContext(8e3)}}async decode(t){if(!this.audioCtx)throw new Error("AudioContext is not initialized");return this.audioCtx.decodeAudioData(t)}destroy(){this.audioCtx?.close(),this.audioCtx=null}},this.timer=new n,this.player=new class{media;isExternalMedia=!1;hasPlayedOnce=!1;subscriptions=[];constructor({media:t,autoplay:e}){t?(this.media=t,this.isExternalMedia=!0):this.media=document.createElement("audio"),this.subscriptions.push(this.on("play",(()=>{this.hasPlayedOnce=!0}),{once:!0})),e&&(this.media.autoplay=!0)}on(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}destroy(){this.subscriptions.forEach((t=>{t()})),this.media.pause(),this.isExternalMedia||this.media.remove()}loadUrl(t){this.media.src=t}getCurrentTime(){return this.media.currentTime}play(){this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}seekTo(t){!this.hasPlayedOnce&&navigator.userAgent.match(/(iPad|iPhone|iPod)/g)&&this.media.play()?.then?.((()=>{setTimeout((()=>this.media.pause()),10)})),this.media.currentTime=t}getDuration(){return this.media.duration}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}}({media:this.options.media,autoplay:this.options.autoplay}),this.renderer=new r({container:this.options.container,height:this.options.height,waveColor:this.options.waveColor,progressColor:this.options.progressColor,cursorColor:this.options.cursorColor,cursorWidth:this.options.cursorWidth,minPxPerSec:this.options.minPxPerSec,fillParent:this.options.fillParent,barWidth:this.options.barWidth,barGap:this.options.barGap,barRadius:this.options.barRadius,noScrollbar:this.options.noScrollbar}),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent();const e=this.options.url||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}initPlayerEvents(){this.subscriptions.push(this.player.on("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",{currentTime:t})})),this.player.on("play",(()=>{this.emit("play"),this.timer.start()})),this.player.on("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.player.on("canplay",(()=>{this.emit("canplay",{duration:this.getDuration()})})),this.player.on("seeking",(()=>{this.emit("seeking",{currentTime:this.getCurrentTime()})})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(({relativeX:t})=>{if(this.options.interactive){const e=this.getDuration()*t;this.seekTo(e),this.emit("seekClick",{currentTime:this.getCurrentTime()})}})))}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",{currentTime:t})})))}initReadyEvent(){let t=!1,e=!1;const i=()=>{t&&e&&this.emit("ready",{duration:this.getDuration()})};this.subscriptions.push(this.on("decode",(()=>{t=!0,i()})),this.on("canplay",(()=>{e=!0,i()})),this.player.on("waiting",(()=>{e=!1,t=!1})))}async load(t,e,i){if(this.player.loadUrl(t),null==e){const e=await this.fetcher.load(t),i=await this.decoder.decode(e);this.decodedData=i}else i||(i=await new Promise((t=>{this.player.on("canplay",(()=>t(this.getDuration())),{once:!0})}))||0),this.decodedData={duration:i,numberOfChannels:e.length,sampleRate:e[0].length/i,getChannelData:t=>e[t]};this.renderer.render(this.decodedData),this.emit("decode",{duration:this.decodedData.duration})}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(this.decodedData,t),this.emit("zoom",{minPxPerSec:t})}play(){this.player.play()}pause(){this.player.pause()}seekTo(t){this.player.seekTo(t)}isPlaying(){return this.player.isPlaying()}getDuration(){return this.player.getDuration()||this.decodedData?.duration||0}getCurrentTime(){return this.player.getCurrentTime()}getVolume(){return this.player.getVolume()}setVolume(t){this.player.setVolume(t)}getMuted(){return this.player.getMuted()}setMuted(t){this.player.setMuted(t)}getPlaybackRate(){return this.player.getPlaybackRate()}setPlaybackRate(t,e){this.player.setPlaybackRate(t,e)}registerPlugin(t,e){const i=new t({wavesurfer:this,container:this.renderer.getContainer(),wrapper:this.renderer.getWrapper()},e);return this.plugins.push(i),i.once("destroy",(()=>{this.plugins=this.plugins.filter((t=>t!==i))})),i}getDecodedData(){return this.decodedData}getMediaElement(){return this.player.getMediaElement()}toggleInteractive(t){this.options.interactive=t}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.plugins.forEach((t=>t.destroy())),this.timer.destroy(),this.player.destroy(),this.decoder.destroy(),this.renderer.destroy()}}const h=a,l={height:50,overlayColor:"rgba(100, 100, 100, 0.1)"},d=class extends s{options;minimapWrapper;miniWavesurfer=null;overlay;constructor(t,e){super(t,e),this.options=Object.assign({},l,e),this.minimapWrapper=this.initMinimapWrapper(),this.overlay=this.initOverlay(),this.subscriptions.push(this.wavesurfer.on("decode",(()=>{this.initMinimap()})))}initMinimapWrapper(){const t=document.createElement("div");return t.style.position="relative",this.container.insertAdjacentElement("afterend",t),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(){const t=this.wavesurfer.getDecodedData(),e=this.wavesurfer.getMediaElement();if(!t||!e)return;this.miniWavesurfer=h.create({...this.options,container:this.minimapWrapper,minPxPerSec:1,fillParent:!0,media:e,url:e.src,peaks:[t.getChannelData(0)],duration:t.duration});const i=Math.round(this.minimapWrapper.clientWidth/this.wrapper.clientWidth*100);this.overlay.style.width=`${i}%`,this.wavesurfer.on("timeupdate",(({currentTime:t})=>{const e=Math.max(0,Math.min(t/this.wavesurfer.getDuration()*100-i/2,100-i)).toFixed(2);this.overlay.style.left=`${e}%`}))}destroy(){this.miniWavesurfer?.destroy(),this.minimapWrapper.remove(),super.destroy()}};return e.default})()));
|