wavesurfer.js 7.8.4-beta.1 → 7.8.4
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/base-plugin.js +1 -3
- package/dist/decoder.js +16 -5
- package/dist/dom.js +1 -1
- package/dist/draggable.js +2 -2
- package/dist/event-emitter.js +6 -3
- package/dist/fetcher.js +50 -37
- package/dist/player.js +14 -4
- package/dist/plugins/envelope.js +1 -355
- package/dist/plugins/hover.js +1 -106
- package/dist/plugins/record.cjs +1 -1
- package/dist/plugins/record.esm.js +1 -1
- package/dist/plugins/record.js +1 -1
- package/dist/plugins/record.min.js +1 -1
- package/dist/renderer.js +108 -88
- package/dist/timer.js +4 -1
- package/dist/wavesurfer.js +95 -76
- package/dist/webaudio.js +50 -33
- package/package.json +1 -1
package/dist/base-plugin.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import EventEmitter from './event-emitter.js';
|
|
2
2
|
/** Base class for wavesurfer plugins */
|
|
3
3
|
export class BasePlugin extends EventEmitter {
|
|
4
|
-
wavesurfer;
|
|
5
|
-
subscriptions = [];
|
|
6
|
-
options;
|
|
7
4
|
/** Create a plugin instance */
|
|
8
5
|
constructor(options) {
|
|
9
6
|
super();
|
|
7
|
+
this.subscriptions = [];
|
|
10
8
|
this.options = options;
|
|
11
9
|
}
|
|
12
10
|
/** Called after this.wavesurfer is available */
|
package/dist/decoder.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
/** Decode an array buffer into an audio buffer */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
11
|
+
function decode(audioData, sampleRate) {
|
|
12
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
const audioCtx = new AudioContext({ sampleRate });
|
|
14
|
+
const decode = audioCtx.decodeAudioData(audioData);
|
|
15
|
+
return decode.finally(() => audioCtx.close());
|
|
16
|
+
});
|
|
6
17
|
}
|
|
7
18
|
/** Normalize peaks to -1..1 */
|
|
8
19
|
function normalize(channelData) {
|
|
@@ -35,7 +46,7 @@ function createBuffer(channelData, duration) {
|
|
|
35
46
|
length: channelData[0].length,
|
|
36
47
|
sampleRate: channelData[0].length / duration,
|
|
37
48
|
numberOfChannels: channelData.length,
|
|
38
|
-
getChannelData: (i) => channelData
|
|
49
|
+
getChannelData: (i) => channelData === null || channelData === void 0 ? void 0 : channelData[i],
|
|
39
50
|
copyFromChannel: AudioBuffer.prototype.copyFromChannel,
|
|
40
51
|
copyToChannel: AudioBuffer.prototype.copyToChannel,
|
|
41
52
|
};
|
package/dist/dom.js
CHANGED
|
@@ -27,7 +27,7 @@ function renderNode(tagName, content) {
|
|
|
27
27
|
}
|
|
28
28
|
export function createElement(tagName, content, container) {
|
|
29
29
|
const el = renderNode(tagName, content || {});
|
|
30
|
-
container
|
|
30
|
+
container === null || container === void 0 ? void 0 : container.appendChild(el);
|
|
31
31
|
return el;
|
|
32
32
|
}
|
|
33
33
|
export default createElement;
|
package/dist/draggable.js
CHANGED
|
@@ -25,7 +25,7 @@ export function makeDraggable(element, onDrag, onStart, onEnd, threshold = 3, mo
|
|
|
25
25
|
const rect = element.getBoundingClientRect();
|
|
26
26
|
const { left, top } = rect;
|
|
27
27
|
if (!isDragging) {
|
|
28
|
-
onStart
|
|
28
|
+
onStart === null || onStart === void 0 ? void 0 : onStart(startX - left, startY - top);
|
|
29
29
|
isDragging = true;
|
|
30
30
|
}
|
|
31
31
|
onDrag(dx, dy, x - left, y - top);
|
|
@@ -39,7 +39,7 @@ export function makeDraggable(element, onDrag, onStart, onEnd, threshold = 3, mo
|
|
|
39
39
|
const y = event.clientY;
|
|
40
40
|
const rect = element.getBoundingClientRect();
|
|
41
41
|
const { left, top } = rect;
|
|
42
|
-
onEnd
|
|
42
|
+
onEnd === null || onEnd === void 0 ? void 0 : onEnd(x - left, y - top);
|
|
43
43
|
}
|
|
44
44
|
unsubscribeDocument();
|
|
45
45
|
};
|
package/dist/event-emitter.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/** A simple event emitter that can be used to listen to and emit events. */
|
|
2
2
|
class EventEmitter {
|
|
3
|
-
|
|
3
|
+
constructor() {
|
|
4
|
+
this.listeners = {};
|
|
5
|
+
}
|
|
4
6
|
/** Subscribe to an event. Returns an unsubscribe function. */
|
|
5
7
|
on(event, listener, options) {
|
|
6
8
|
if (!this.listeners[event]) {
|
|
7
9
|
this.listeners[event] = new Set();
|
|
8
10
|
}
|
|
9
11
|
this.listeners[event].add(listener);
|
|
10
|
-
if (options
|
|
12
|
+
if (options === null || options === void 0 ? void 0 : options.once) {
|
|
11
13
|
const unsubscribeOnce = () => {
|
|
12
14
|
this.un(event, unsubscribeOnce);
|
|
13
15
|
this.un(event, listener);
|
|
@@ -19,7 +21,8 @@ class EventEmitter {
|
|
|
19
21
|
}
|
|
20
22
|
/** Unsubscribe from an event */
|
|
21
23
|
un(event, listener) {
|
|
22
|
-
|
|
24
|
+
var _a;
|
|
25
|
+
(_a = this.listeners[event]) === null || _a === void 0 ? void 0 : _a.delete(listener);
|
|
23
26
|
}
|
|
24
27
|
/** Subscribe to an event only once */
|
|
25
28
|
once(event, listener) {
|
package/dist/fetcher.js
CHANGED
|
@@ -1,42 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
const read = async () => {
|
|
15
|
-
let data;
|
|
16
|
-
try {
|
|
17
|
-
data = await reader.read();
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
// Ignore errors because we can only handle the main response
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
function watchProgress(response, progressCallback) {
|
|
11
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
if (!response.body || !response.headers)
|
|
21
13
|
return;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
const reader = response.body.getReader();
|
|
15
|
+
const contentLength = Number(response.headers.get('Content-Length')) || 0;
|
|
16
|
+
let receivedLength = 0;
|
|
17
|
+
// Process the data
|
|
18
|
+
const processChunk = (value) => __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
// Add to the received length
|
|
20
|
+
receivedLength += (value === null || value === void 0 ? void 0 : value.length) || 0;
|
|
21
|
+
const percentage = Math.round((receivedLength / contentLength) * 100);
|
|
22
|
+
progressCallback(percentage);
|
|
23
|
+
});
|
|
24
|
+
const read = () => __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
let data;
|
|
26
|
+
try {
|
|
27
|
+
data = yield reader.read();
|
|
28
|
+
}
|
|
29
|
+
catch (_a) {
|
|
30
|
+
// Ignore errors because we can only handle the main response
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Continue reading data until done
|
|
34
|
+
if (!data.done) {
|
|
35
|
+
processChunk(data.value);
|
|
36
|
+
yield read();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
read();
|
|
40
|
+
});
|
|
30
41
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
function fetchBlob(url, progressCallback, requestInit) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
// Fetch the resource
|
|
45
|
+
const response = yield fetch(url, requestInit);
|
|
46
|
+
if (response.status >= 400) {
|
|
47
|
+
throw new Error(`Failed to fetch ${url}: ${response.status} (${response.statusText})`);
|
|
48
|
+
}
|
|
49
|
+
// Read the data to track progress
|
|
50
|
+
watchProgress(response.clone(), progressCallback);
|
|
51
|
+
return response.blob();
|
|
52
|
+
});
|
|
40
53
|
}
|
|
41
54
|
const Fetcher = {
|
|
42
55
|
fetchBlob,
|
package/dist/player.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import EventEmitter from './event-emitter.js';
|
|
2
11
|
class Player extends EventEmitter {
|
|
3
|
-
media;
|
|
4
|
-
isExternalMedia = false;
|
|
5
12
|
constructor(options) {
|
|
6
13
|
super();
|
|
14
|
+
this.isExternalMedia = false;
|
|
7
15
|
if (options.media) {
|
|
8
16
|
this.media = options.media;
|
|
9
17
|
this.isExternalMedia = true;
|
|
@@ -71,8 +79,10 @@ class Player extends EventEmitter {
|
|
|
71
79
|
this.media = element;
|
|
72
80
|
}
|
|
73
81
|
/** Start playing the audio */
|
|
74
|
-
|
|
75
|
-
return this
|
|
82
|
+
play() {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
return this.media.play();
|
|
85
|
+
});
|
|
76
86
|
}
|
|
77
87
|
/** Pause the audio */
|
|
78
88
|
pause() {
|
package/dist/plugins/envelope.js
CHANGED
|
@@ -1,355 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Envelope is a visual UI for controlling the audio volume and add fade-in and fade-out effects.
|
|
3
|
-
*/
|
|
4
|
-
import BasePlugin from '../base-plugin.js';
|
|
5
|
-
import { makeDraggable } from '../draggable.js';
|
|
6
|
-
import EventEmitter from '../event-emitter.js';
|
|
7
|
-
import createElement from '../dom.js';
|
|
8
|
-
const defaultOptions = {
|
|
9
|
-
points: [],
|
|
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 Polyline extends EventEmitter {
|
|
17
|
-
svg;
|
|
18
|
-
options;
|
|
19
|
-
polyPoints;
|
|
20
|
-
subscriptions = [];
|
|
21
|
-
constructor(options, wrapper) {
|
|
22
|
-
super();
|
|
23
|
-
this.subscriptions = [];
|
|
24
|
-
this.options = options;
|
|
25
|
-
this.polyPoints = new Map();
|
|
26
|
-
const width = wrapper.clientWidth;
|
|
27
|
-
const height = wrapper.clientHeight;
|
|
28
|
-
// SVG element
|
|
29
|
-
const svg = createElement('svg', {
|
|
30
|
-
xmlns: 'http://www.w3.org/2000/svg',
|
|
31
|
-
width: '100%',
|
|
32
|
-
height: '100%',
|
|
33
|
-
viewBox: `0 0 ${width} ${height}`,
|
|
34
|
-
preserveAspectRatio: 'none',
|
|
35
|
-
style: {
|
|
36
|
-
position: 'absolute',
|
|
37
|
-
left: '0',
|
|
38
|
-
top: '0',
|
|
39
|
-
zIndex: '4',
|
|
40
|
-
},
|
|
41
|
-
part: 'envelope',
|
|
42
|
-
}, wrapper);
|
|
43
|
-
this.svg = svg;
|
|
44
|
-
// A polyline representing the envelope
|
|
45
|
-
const polyline = createElement('polyline', {
|
|
46
|
-
xmlns: 'http://www.w3.org/2000/svg',
|
|
47
|
-
points: `0,${height} ${width},${height}`,
|
|
48
|
-
stroke: options.lineColor,
|
|
49
|
-
'stroke-width': options.lineWidth,
|
|
50
|
-
fill: 'none',
|
|
51
|
-
part: 'polyline',
|
|
52
|
-
style: options.dragLine
|
|
53
|
-
? {
|
|
54
|
-
cursor: 'row-resize',
|
|
55
|
-
pointerEvents: 'stroke',
|
|
56
|
-
}
|
|
57
|
-
: {},
|
|
58
|
-
}, svg);
|
|
59
|
-
// Make the polyline draggable along the Y axis
|
|
60
|
-
if (options.dragLine) {
|
|
61
|
-
this.subscriptions.push(makeDraggable(polyline, (_, dy) => {
|
|
62
|
-
const { height } = svg.viewBox.baseVal;
|
|
63
|
-
const { points } = polyline;
|
|
64
|
-
for (let i = 1; i < points.numberOfItems - 1; i++) {
|
|
65
|
-
const point = points.getItem(i);
|
|
66
|
-
point.y = Math.min(height, Math.max(0, point.y + dy));
|
|
67
|
-
}
|
|
68
|
-
const circles = svg.querySelectorAll('ellipse');
|
|
69
|
-
Array.from(circles).forEach((circle) => {
|
|
70
|
-
const newY = Math.min(height, Math.max(0, Number(circle.getAttribute('cy')) + dy));
|
|
71
|
-
circle.setAttribute('cy', newY.toString());
|
|
72
|
-
});
|
|
73
|
-
this.emit('line-move', dy / height);
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
// Listen to double click to add a new point
|
|
77
|
-
svg.addEventListener('dblclick', (e) => {
|
|
78
|
-
const rect = svg.getBoundingClientRect();
|
|
79
|
-
const x = e.clientX - rect.left;
|
|
80
|
-
const y = e.clientY - rect.top;
|
|
81
|
-
this.emit('point-create', x / rect.width, y / rect.height);
|
|
82
|
-
});
|
|
83
|
-
// Long press on touch devices
|
|
84
|
-
{
|
|
85
|
-
let pressTimer;
|
|
86
|
-
const clearTimer = () => clearTimeout(pressTimer);
|
|
87
|
-
svg.addEventListener('touchstart', (e) => {
|
|
88
|
-
if (e.touches.length === 1) {
|
|
89
|
-
pressTimer = window.setTimeout(() => {
|
|
90
|
-
e.preventDefault();
|
|
91
|
-
const rect = svg.getBoundingClientRect();
|
|
92
|
-
const x = e.touches[0].clientX - rect.left;
|
|
93
|
-
const y = e.touches[0].clientY - rect.top;
|
|
94
|
-
this.emit('point-create', x / rect.width, y / rect.height);
|
|
95
|
-
}, 500);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
clearTimer();
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
svg.addEventListener('touchmove', clearTimer);
|
|
102
|
-
svg.addEventListener('touchend', clearTimer);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
makeDraggable(draggable, onDrag) {
|
|
106
|
-
this.subscriptions.push(makeDraggable(draggable, onDrag, () => (draggable.style.cursor = 'grabbing'), () => (draggable.style.cursor = 'grab'), 1));
|
|
107
|
-
}
|
|
108
|
-
createCircle(x, y) {
|
|
109
|
-
const size = this.options.dragPointSize;
|
|
110
|
-
const radius = size / 2;
|
|
111
|
-
return createElement('ellipse', {
|
|
112
|
-
xmlns: 'http://www.w3.org/2000/svg',
|
|
113
|
-
cx: x,
|
|
114
|
-
cy: y,
|
|
115
|
-
rx: radius,
|
|
116
|
-
ry: radius,
|
|
117
|
-
fill: this.options.dragPointFill,
|
|
118
|
-
stroke: this.options.dragPointStroke,
|
|
119
|
-
'stroke-width': '2',
|
|
120
|
-
style: {
|
|
121
|
-
cursor: 'grab',
|
|
122
|
-
pointerEvents: 'all',
|
|
123
|
-
},
|
|
124
|
-
part: 'envelope-circle',
|
|
125
|
-
}, this.svg);
|
|
126
|
-
}
|
|
127
|
-
removePolyPoint(point) {
|
|
128
|
-
const item = this.polyPoints.get(point);
|
|
129
|
-
if (!item)
|
|
130
|
-
return;
|
|
131
|
-
const { polyPoint, circle } = item;
|
|
132
|
-
const { points } = this.svg.querySelector('polyline');
|
|
133
|
-
const index = Array.from(points).findIndex((p) => p.x === polyPoint.x && p.y === polyPoint.y);
|
|
134
|
-
points.removeItem(index);
|
|
135
|
-
circle.remove();
|
|
136
|
-
this.polyPoints.delete(point);
|
|
137
|
-
}
|
|
138
|
-
addPolyPoint(relX, relY, refPoint) {
|
|
139
|
-
const { svg } = this;
|
|
140
|
-
const { width, height } = svg.viewBox.baseVal;
|
|
141
|
-
const x = relX * width;
|
|
142
|
-
const y = height - relY * height;
|
|
143
|
-
const threshold = this.options.dragPointSize / 2;
|
|
144
|
-
const newPoint = svg.createSVGPoint();
|
|
145
|
-
newPoint.x = relX * width;
|
|
146
|
-
newPoint.y = height - relY * height;
|
|
147
|
-
const circle = this.createCircle(x, y);
|
|
148
|
-
const { points } = svg.querySelector('polyline');
|
|
149
|
-
const newIndex = Array.from(points).findIndex((point) => point.x >= x);
|
|
150
|
-
points.insertItemBefore(newPoint, Math.max(newIndex, 1));
|
|
151
|
-
this.polyPoints.set(refPoint, { polyPoint: newPoint, circle });
|
|
152
|
-
this.makeDraggable(circle, (dx, dy) => {
|
|
153
|
-
const newX = newPoint.x + dx;
|
|
154
|
-
const newY = newPoint.y + dy;
|
|
155
|
-
// Remove the point if it's dragged out of the SVG
|
|
156
|
-
if (newX < -threshold || newY < -threshold || newX > width + threshold || newY > height + threshold) {
|
|
157
|
-
this.emit('point-dragout', refPoint);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
// Don't allow to drag past the next or previous point
|
|
161
|
-
const next = Array.from(points).find((point) => point.x > newPoint.x);
|
|
162
|
-
const prev = Array.from(points).findLast((point) => point.x < newPoint.x);
|
|
163
|
-
if ((next && newX >= next.x) || (prev && newX <= prev.x)) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
// Update the point and the circle position
|
|
167
|
-
newPoint.x = newX;
|
|
168
|
-
newPoint.y = newY;
|
|
169
|
-
circle.setAttribute('cx', newX.toString());
|
|
170
|
-
circle.setAttribute('cy', newY.toString());
|
|
171
|
-
// Emit the event passing the point and new relative coordinates
|
|
172
|
-
this.emit('point-move', refPoint, newX / width, newY / height);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
update() {
|
|
176
|
-
const { svg } = this;
|
|
177
|
-
const aspectRatioX = svg.viewBox.baseVal.width / svg.clientWidth;
|
|
178
|
-
const aspectRatioY = svg.viewBox.baseVal.height / svg.clientHeight;
|
|
179
|
-
const circles = svg.querySelectorAll('ellipse');
|
|
180
|
-
circles.forEach((circle) => {
|
|
181
|
-
const radius = this.options.dragPointSize / 2;
|
|
182
|
-
const rx = radius * aspectRatioX;
|
|
183
|
-
const ry = radius * aspectRatioY;
|
|
184
|
-
circle.setAttribute('rx', rx.toString());
|
|
185
|
-
circle.setAttribute('ry', ry.toString());
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
destroy() {
|
|
189
|
-
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
190
|
-
this.polyPoints.clear();
|
|
191
|
-
this.svg.remove();
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
const randomId = () => Math.random().toString(36).slice(2);
|
|
195
|
-
class EnvelopePlugin extends BasePlugin {
|
|
196
|
-
options;
|
|
197
|
-
polyline = null;
|
|
198
|
-
points;
|
|
199
|
-
throttleTimeout = null;
|
|
200
|
-
volume = 1;
|
|
201
|
-
/**
|
|
202
|
-
* Create a new Envelope plugin.
|
|
203
|
-
*/
|
|
204
|
-
constructor(options) {
|
|
205
|
-
super(options);
|
|
206
|
-
this.points = options.points || [];
|
|
207
|
-
this.options = Object.assign({}, defaultOptions, options);
|
|
208
|
-
this.options.lineColor = this.options.lineColor || defaultOptions.lineColor;
|
|
209
|
-
this.options.dragPointFill = this.options.dragPointFill || defaultOptions.dragPointFill;
|
|
210
|
-
this.options.dragPointStroke = this.options.dragPointStroke || defaultOptions.dragPointStroke;
|
|
211
|
-
this.options.dragPointSize = this.options.dragPointSize || defaultOptions.dragPointSize;
|
|
212
|
-
}
|
|
213
|
-
static create(options) {
|
|
214
|
-
return new EnvelopePlugin(options);
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Add an envelope point with a given time and volume.
|
|
218
|
-
*/
|
|
219
|
-
addPoint(point) {
|
|
220
|
-
if (!point.id)
|
|
221
|
-
point.id = randomId();
|
|
222
|
-
// Insert the point in the correct position to keep the array sorted
|
|
223
|
-
const index = this.points.findLastIndex((p) => p.time < point.time);
|
|
224
|
-
this.points.splice(index + 1, 0, point);
|
|
225
|
-
this.emitPoints();
|
|
226
|
-
// Add the point to the polyline if the duration is available
|
|
227
|
-
const duration = this.wavesurfer?.getDuration();
|
|
228
|
-
if (duration) {
|
|
229
|
-
this.addPolyPoint(point, duration);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Remove an envelope point.
|
|
234
|
-
*/
|
|
235
|
-
removePoint(point) {
|
|
236
|
-
const index = this.points.indexOf(point);
|
|
237
|
-
if (index > -1) {
|
|
238
|
-
this.points.splice(index, 1);
|
|
239
|
-
this.polyline?.removePolyPoint(point);
|
|
240
|
-
this.emitPoints();
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Get all envelope points. Should not be modified directly.
|
|
245
|
-
*/
|
|
246
|
-
getPoints() {
|
|
247
|
-
return this.points;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Set new envelope points.
|
|
251
|
-
*/
|
|
252
|
-
setPoints(newPoints) {
|
|
253
|
-
this.points.slice().forEach((point) => this.removePoint(point));
|
|
254
|
-
newPoints.forEach((point) => this.addPoint(point));
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Destroy the plugin instance.
|
|
258
|
-
*/
|
|
259
|
-
destroy() {
|
|
260
|
-
this.polyline?.destroy();
|
|
261
|
-
super.destroy();
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Get the envelope volume.
|
|
265
|
-
*/
|
|
266
|
-
getCurrentVolume() {
|
|
267
|
-
return this.volume;
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Set the envelope volume. 0..1 (more than 1 will boost the volume).
|
|
271
|
-
*/
|
|
272
|
-
setVolume(floatValue) {
|
|
273
|
-
this.volume = floatValue;
|
|
274
|
-
this.wavesurfer?.setVolume(floatValue);
|
|
275
|
-
}
|
|
276
|
-
/** Called by wavesurfer, don't call manually */
|
|
277
|
-
onInit() {
|
|
278
|
-
if (!this.wavesurfer) {
|
|
279
|
-
throw Error('WaveSurfer is not initialized');
|
|
280
|
-
}
|
|
281
|
-
const { options } = this;
|
|
282
|
-
options.volume = options.volume ?? this.wavesurfer.getVolume();
|
|
283
|
-
this.setVolume(options.volume);
|
|
284
|
-
this.subscriptions.push(this.wavesurfer.on('decode', (duration) => {
|
|
285
|
-
this.initPolyline();
|
|
286
|
-
this.points.forEach((point) => {
|
|
287
|
-
this.addPolyPoint(point, duration);
|
|
288
|
-
});
|
|
289
|
-
}), this.wavesurfer.on('redraw', () => {
|
|
290
|
-
this.polyline?.update();
|
|
291
|
-
}), this.wavesurfer.on('timeupdate', (time) => {
|
|
292
|
-
this.onTimeUpdate(time);
|
|
293
|
-
}));
|
|
294
|
-
}
|
|
295
|
-
emitPoints() {
|
|
296
|
-
if (this.throttleTimeout) {
|
|
297
|
-
clearTimeout(this.throttleTimeout);
|
|
298
|
-
}
|
|
299
|
-
this.throttleTimeout = setTimeout(() => {
|
|
300
|
-
this.emit('points-change', this.points);
|
|
301
|
-
}, 200);
|
|
302
|
-
}
|
|
303
|
-
initPolyline() {
|
|
304
|
-
if (this.polyline)
|
|
305
|
-
this.polyline.destroy();
|
|
306
|
-
if (!this.wavesurfer)
|
|
307
|
-
return;
|
|
308
|
-
const wrapper = this.wavesurfer.getWrapper();
|
|
309
|
-
this.polyline = new Polyline(this.options, wrapper);
|
|
310
|
-
this.subscriptions.push(this.polyline.on('point-move', (point, relativeX, relativeY) => {
|
|
311
|
-
const duration = this.wavesurfer?.getDuration() || 0;
|
|
312
|
-
point.time = relativeX * duration;
|
|
313
|
-
point.volume = 1 - relativeY;
|
|
314
|
-
this.emitPoints();
|
|
315
|
-
}), this.polyline.on('point-dragout', (point) => {
|
|
316
|
-
this.removePoint(point);
|
|
317
|
-
}), this.polyline.on('point-create', (relativeX, relativeY) => {
|
|
318
|
-
this.addPoint({
|
|
319
|
-
time: relativeX * (this.wavesurfer?.getDuration() || 0),
|
|
320
|
-
volume: 1 - relativeY,
|
|
321
|
-
});
|
|
322
|
-
}), this.polyline.on('line-move', (relativeY) => {
|
|
323
|
-
this.points.forEach((point) => {
|
|
324
|
-
point.volume = Math.min(1, Math.max(0, point.volume - relativeY));
|
|
325
|
-
});
|
|
326
|
-
this.emitPoints();
|
|
327
|
-
this.onTimeUpdate(this.wavesurfer?.getCurrentTime() || 0);
|
|
328
|
-
}));
|
|
329
|
-
}
|
|
330
|
-
addPolyPoint(point, duration) {
|
|
331
|
-
this.polyline?.addPolyPoint(point.time / duration, point.volume, point);
|
|
332
|
-
}
|
|
333
|
-
onTimeUpdate(time) {
|
|
334
|
-
if (!this.wavesurfer)
|
|
335
|
-
return;
|
|
336
|
-
let nextPoint = this.points.find((point) => point.time > time);
|
|
337
|
-
if (!nextPoint) {
|
|
338
|
-
nextPoint = { time: this.wavesurfer.getDuration() || 0, volume: 0 };
|
|
339
|
-
}
|
|
340
|
-
let prevPoint = this.points.findLast((point) => point.time <= time);
|
|
341
|
-
if (!prevPoint) {
|
|
342
|
-
prevPoint = { time: 0, volume: 0 };
|
|
343
|
-
}
|
|
344
|
-
const timeDiff = nextPoint.time - prevPoint.time;
|
|
345
|
-
const volumeDiff = nextPoint.volume - prevPoint.volume;
|
|
346
|
-
const newVolume = prevPoint.volume + (time - prevPoint.time) * (volumeDiff / timeDiff);
|
|
347
|
-
const clampedVolume = Math.min(1, Math.max(0, newVolume));
|
|
348
|
-
const roundedVolume = Math.round(clampedVolume * 100) / 100;
|
|
349
|
-
if (roundedVolume !== this.getCurrentVolume()) {
|
|
350
|
-
this.setVolume(roundedVolume);
|
|
351
|
-
this.emit('volume-change', roundedVolume);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
export default EnvelopePlugin;
|
|
1
|
+
class t{constructor(){this.listeners={}}on(t,e,i){if(this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),null==i?void 0:i.once){const i=()=>{this.un(t,i),this.un(t,e)};return this.on(t,i),i}return()=>this.un(t,e)}un(t,e){var i;null===(i=this.listeners[t])||void 0===i||i.delete(e)}once(t,e){return this.on(t,e,{once:!0})}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class e extends t{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}_init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t()))}}function i(t,e,i,o,n=3,s=0,r=100){if(!t)return()=>{};const l=matchMedia("(pointer: coarse)").matches;let a=()=>{};const h=h=>{if(h.button!==s)return;h.preventDefault(),h.stopPropagation();let u=h.clientX,c=h.clientY,d=!1;const p=Date.now(),m=o=>{if(o.preventDefault(),o.stopPropagation(),l&&Date.now()-p<r)return;const s=o.clientX,a=o.clientY,h=s-u,m=a-c;if(d||Math.abs(h)>n||Math.abs(m)>n){const o=t.getBoundingClientRect(),{left:n,top:r}=o;d||(null==i||i(u-n,c-r),d=!0),e(h,m,s-n,a-r),u=s,c=a}},v=e=>{if(d){const i=e.clientX,n=e.clientY,s=t.getBoundingClientRect(),{left:r,top:l}=s;null==o||o(i-r,n-l)}a()},g=t=>{t.relatedTarget&&t.relatedTarget!==document.documentElement||v(t)},f=t=>{d&&(t.stopPropagation(),t.preventDefault())},y=t=>{d&&t.preventDefault()};document.addEventListener("pointermove",m),document.addEventListener("pointerup",v),document.addEventListener("pointerout",g),document.addEventListener("pointercancel",g),document.addEventListener("touchmove",y,{passive:!1}),document.addEventListener("click",f,{capture:!0}),a=()=>{document.removeEventListener("pointermove",m),document.removeEventListener("pointerup",v),document.removeEventListener("pointerout",g),document.removeEventListener("pointercancel",g),document.removeEventListener("touchmove",y),setTimeout((()=>{document.removeEventListener("click",f,{capture:!0})}),10)}};return t.addEventListener("pointerdown",h),()=>{a(),t.removeEventListener("pointerdown",h)}}function o(t,e){const i=e.xmlns?document.createElementNS(e.xmlns,t):document.createElement(t);for(const[t,n]of Object.entries(e))if("children"===t)for(const[t,n]of Object.entries(e))"string"==typeof n?i.appendChild(document.createTextNode(n)):i.appendChild(o(t,n));else"style"===t?Object.assign(i.style,n):"textContent"===t?i.textContent=n:i.setAttribute(t,n.toString());return i}function n(t,e,i){const n=o(t,e||{});return null==i||i.appendChild(n),n}const s={points:[],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 r extends t{constructor(t,e){super(),this.subscriptions=[],this.subscriptions=[],this.options=t,this.polyPoints=new Map;const o=e.clientWidth,s=e.clientHeight,r=n("svg",{xmlns:"http://www.w3.org/2000/svg",width:"100%",height:"100%",viewBox:`0 0 ${o} ${s}`,preserveAspectRatio:"none",style:{position:"absolute",left:"0",top:"0",zIndex:"4"},part:"envelope"},e);this.svg=r;const l=n("polyline",{xmlns:"http://www.w3.org/2000/svg",points:`0,${s} ${o},${s}`,stroke:t.lineColor,"stroke-width":t.lineWidth,fill:"none",part:"polyline",style:t.dragLine?{cursor:"row-resize",pointerEvents:"stroke"}:{}},r);t.dragLine&&this.subscriptions.push(i(l,((t,e)=>{const{height:i}=r.viewBox.baseVal,{points:o}=l;for(let t=1;t<o.numberOfItems-1;t++){const n=o.getItem(t);n.y=Math.min(i,Math.max(0,n.y+e))}const n=r.querySelectorAll("ellipse");Array.from(n).forEach((t=>{const o=Math.min(i,Math.max(0,Number(t.getAttribute("cy"))+e));t.setAttribute("cy",o.toString())})),this.emit("line-move",e/i)}))),r.addEventListener("dblclick",(t=>{const e=r.getBoundingClientRect(),i=t.clientX-e.left,o=t.clientY-e.top;this.emit("point-create",i/e.width,o/e.height)}));{let t;const e=()=>clearTimeout(t);r.addEventListener("touchstart",(i=>{1===i.touches.length?t=window.setTimeout((()=>{i.preventDefault();const t=r.getBoundingClientRect(),e=i.touches[0].clientX-t.left,o=i.touches[0].clientY-t.top;this.emit("point-create",e/t.width,o/t.height)}),500):e()})),r.addEventListener("touchmove",e),r.addEventListener("touchend",e)}}makeDraggable(t,e){this.subscriptions.push(i(t,e,(()=>t.style.cursor="grabbing"),(()=>t.style.cursor="grab"),1))}createCircle(t,e){const i=this.options.dragPointSize/2;return n("ellipse",{xmlns:"http://www.w3.org/2000/svg",cx:t,cy:e,rx:i,ry:i,fill:this.options.dragPointFill,stroke:this.options.dragPointStroke,"stroke-width":"2",style:{cursor:"grab",pointerEvents:"all"},part:"envelope-circle"},this.svg)}removePolyPoint(t){const e=this.polyPoints.get(t);if(!e)return;const{polyPoint:i,circle:o}=e,{points:n}=this.svg.querySelector("polyline"),s=Array.from(n).findIndex((t=>t.x===i.x&&t.y===i.y));n.removeItem(s),o.remove(),this.polyPoints.delete(t)}addPolyPoint(t,e,i){const{svg:o}=this,{width:n,height:s}=o.viewBox.baseVal,r=t*n,l=s-e*s,a=this.options.dragPointSize/2,h=o.createSVGPoint();h.x=t*n,h.y=s-e*s;const u=this.createCircle(r,l),{points:c}=o.querySelector("polyline"),d=Array.from(c).findIndex((t=>t.x>=r));c.insertItemBefore(h,Math.max(d,1)),this.polyPoints.set(i,{polyPoint:h,circle:u}),this.makeDraggable(u,((t,e)=>{const o=h.x+t,r=h.y+e;if(o<-a||r<-a||o>n+a||r>s+a)return void this.emit("point-dragout",i);const l=Array.from(c).find((t=>t.x>h.x)),d=Array.from(c).findLast((t=>t.x<h.x));l&&o>=l.x||d&&o<=d.x||(h.x=o,h.y=r,u.setAttribute("cx",o.toString()),u.setAttribute("cy",r.toString()),this.emit("point-move",i,o/n,r/s))}))}update(){const{svg:t}=this,e=t.viewBox.baseVal.width/t.clientWidth,i=t.viewBox.baseVal.height/t.clientHeight;t.querySelectorAll("ellipse").forEach((t=>{const o=this.options.dragPointSize/2,n=o*e,s=o*i;t.setAttribute("rx",n.toString()),t.setAttribute("ry",s.toString())}))}destroy(){this.subscriptions.forEach((t=>t())),this.polyPoints.clear(),this.svg.remove()}}class l extends e{constructor(t){super(t),this.polyline=null,this.throttleTimeout=null,this.volume=1,this.points=t.points||[],this.options=Object.assign({},s,t),this.options.lineColor=this.options.lineColor||s.lineColor,this.options.dragPointFill=this.options.dragPointFill||s.dragPointFill,this.options.dragPointStroke=this.options.dragPointStroke||s.dragPointStroke,this.options.dragPointSize=this.options.dragPointSize||s.dragPointSize}static create(t){return new l(t)}addPoint(t){var e;t.id||(t.id=Math.random().toString(36).slice(2));const i=this.points.findLastIndex((e=>e.time<t.time));this.points.splice(i+1,0,t),this.emitPoints();const o=null===(e=this.wavesurfer)||void 0===e?void 0:e.getDuration();o&&this.addPolyPoint(t,o)}removePoint(t){var e;const i=this.points.indexOf(t);i>-1&&(this.points.splice(i,1),null===(e=this.polyline)||void 0===e||e.removePolyPoint(t),this.emitPoints())}getPoints(){return this.points}setPoints(t){this.points.slice().forEach((t=>this.removePoint(t))),t.forEach((t=>this.addPoint(t)))}destroy(){var t;null===(t=this.polyline)||void 0===t||t.destroy(),super.destroy()}getCurrentVolume(){return this.volume}setVolume(t){var e;this.volume=t,null===(e=this.wavesurfer)||void 0===e||e.setVolume(t)}onInit(){var t;if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const{options:e}=this;e.volume=null!==(t=e.volume)&&void 0!==t?t:this.wavesurfer.getVolume(),this.setVolume(e.volume),this.subscriptions.push(this.wavesurfer.on("decode",(t=>{this.initPolyline(),this.points.forEach((e=>{this.addPolyPoint(e,t)}))})),this.wavesurfer.on("redraw",(()=>{var t;null===(t=this.polyline)||void 0===t||t.update()})),this.wavesurfer.on("timeupdate",(t=>{this.onTimeUpdate(t)})))}emitPoints(){this.throttleTimeout&&clearTimeout(this.throttleTimeout),this.throttleTimeout=setTimeout((()=>{this.emit("points-change",this.points)}),200)}initPolyline(){if(this.polyline&&this.polyline.destroy(),!this.wavesurfer)return;const t=this.wavesurfer.getWrapper();this.polyline=new r(this.options,t),this.subscriptions.push(this.polyline.on("point-move",((t,e,i)=>{var o;const n=(null===(o=this.wavesurfer)||void 0===o?void 0:o.getDuration())||0;t.time=e*n,t.volume=1-i,this.emitPoints()})),this.polyline.on("point-dragout",(t=>{this.removePoint(t)})),this.polyline.on("point-create",((t,e)=>{var i;this.addPoint({time:t*((null===(i=this.wavesurfer)||void 0===i?void 0:i.getDuration())||0),volume:1-e})})),this.polyline.on("line-move",(t=>{var e;this.points.forEach((e=>{e.volume=Math.min(1,Math.max(0,e.volume-t))})),this.emitPoints(),this.onTimeUpdate((null===(e=this.wavesurfer)||void 0===e?void 0:e.getCurrentTime())||0)})))}addPolyPoint(t,e){var i;null===(i=this.polyline)||void 0===i||i.addPolyPoint(t.time/e,t.volume,t)}onTimeUpdate(t){if(!this.wavesurfer)return;let e=this.points.find((e=>e.time>t));e||(e={time:this.wavesurfer.getDuration()||0,volume:0});let i=this.points.findLast((e=>e.time<=t));i||(i={time:0,volume:0});const o=e.time-i.time,n=e.volume-i.volume,s=i.volume+(t-i.time)*(n/o),r=Math.min(1,Math.max(0,s)),l=Math.round(100*r)/100;l!==this.getCurrentVolume()&&(this.setVolume(l),this.emit("volume-change",l))}}export{l as default};
|