wavesurfer.js 4.5.0 → 5.1.0
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/CHANGES.md +36 -1
- package/README.md +6 -3
- package/dist/plugin/wavesurfer.cursor.js +39 -34
- package/dist/plugin/wavesurfer.cursor.js.map +1 -1
- package/dist/plugin/wavesurfer.cursor.min.js +2 -2
- package/dist/plugin/wavesurfer.cursor.min.js.map +1 -1
- package/dist/plugin/wavesurfer.elan.js +29 -27
- package/dist/plugin/wavesurfer.elan.js.map +1 -1
- package/dist/plugin/wavesurfer.elan.min.js +2 -2
- package/dist/plugin/wavesurfer.elan.min.js.map +1 -1
- package/dist/plugin/wavesurfer.markers.js +381 -0
- package/dist/plugin/wavesurfer.markers.js.map +1 -0
- package/dist/plugin/wavesurfer.markers.min.js +7 -0
- package/dist/plugin/wavesurfer.markers.min.js.map +1 -0
- package/dist/plugin/wavesurfer.mediasession.js +29 -27
- package/dist/plugin/wavesurfer.mediasession.js.map +1 -1
- package/dist/plugin/wavesurfer.mediasession.min.js +2 -2
- package/dist/plugin/wavesurfer.mediasession.min.js.map +1 -1
- package/dist/plugin/wavesurfer.microphone.js +29 -27
- package/dist/plugin/wavesurfer.microphone.js.map +1 -1
- package/dist/plugin/wavesurfer.microphone.min.js +2 -2
- package/dist/plugin/wavesurfer.microphone.min.js.map +1 -1
- package/dist/plugin/wavesurfer.minimap.js +30 -28
- package/dist/plugin/wavesurfer.minimap.js.map +1 -1
- package/dist/plugin/wavesurfer.minimap.min.js +2 -2
- package/dist/plugin/wavesurfer.minimap.min.js.map +1 -1
- package/dist/plugin/wavesurfer.playhead.js +324 -0
- package/dist/plugin/wavesurfer.playhead.js.map +1 -0
- package/dist/plugin/wavesurfer.playhead.min.js +7 -0
- package/dist/plugin/wavesurfer.playhead.min.js.map +1 -0
- package/dist/plugin/wavesurfer.regions.js +152 -126
- package/dist/plugin/wavesurfer.regions.js.map +1 -1
- package/dist/plugin/wavesurfer.regions.min.js +2 -2
- package/dist/plugin/wavesurfer.regions.min.js.map +1 -1
- package/dist/plugin/wavesurfer.spectrogram.js +97 -89
- package/dist/plugin/wavesurfer.spectrogram.js.map +1 -1
- package/dist/plugin/wavesurfer.spectrogram.min.js +2 -2
- package/dist/plugin/wavesurfer.spectrogram.min.js.map +1 -1
- package/dist/plugin/wavesurfer.timeline.js +46 -32
- package/dist/plugin/wavesurfer.timeline.js.map +1 -1
- package/dist/plugin/wavesurfer.timeline.min.js +2 -2
- package/dist/plugin/wavesurfer.timeline.min.js.map +1 -1
- package/dist/wavesurfer-html-init.js +9 -5
- package/dist/wavesurfer-html-init.js.map +1 -1
- package/dist/wavesurfer-html-init.min.js +2 -2
- package/dist/wavesurfer-html-init.min.js.map +1 -1
- package/dist/wavesurfer.js +350 -166
- package/dist/wavesurfer.js.map +1 -1
- package/dist/wavesurfer.min.js +2 -2
- package/dist/wavesurfer.min.js.map +1 -1
- package/package.json +19 -16
- package/src/drawer.canvasentry.js +16 -0
- package/src/drawer.js +30 -21
- package/src/drawer.multicanvas.js +63 -39
- package/src/plugin/cursor/index.js +3 -1
- package/src/plugin/markers/index.js +282 -0
- package/src/plugin/playhead/index.js +226 -0
- package/src/plugin/regions/index.js +28 -11
- package/src/plugin/regions/region.js +83 -70
- package/src/plugin/spectrogram/index.js +65 -61
- package/src/plugin/timeline/index.js +20 -11
- package/src/util/index.js +1 -0
- package/src/util/orientation.js +98 -0
- package/src/wavesurfer.js +18 -9
- package/src/webaudio.js +10 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wavesurfer.js",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Interactive navigable audio visualization using Web Audio and Canvas",
|
|
5
5
|
"main": "dist/wavesurfer.js",
|
|
6
6
|
"directories": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"lint-js-html": "lint-staged",
|
|
30
30
|
"doc": "esdoc",
|
|
31
31
|
"test": "karma start karma.conf.js",
|
|
32
|
+
"test:chrome": "karma start karma.conf.js --browsers=Chrome --single-run=false",
|
|
32
33
|
"prepublishOnly": "not-in-install && npm run build || in-install",
|
|
33
34
|
"prepare": "npm run build"
|
|
34
35
|
},
|
|
@@ -54,16 +55,18 @@
|
|
|
54
55
|
"visualization"
|
|
55
56
|
],
|
|
56
57
|
"devDependencies": {
|
|
57
|
-
"@babel/core": "^7.
|
|
58
|
-
"@babel/preset-env": "^7.
|
|
59
|
-
"@babel/register": "^7.
|
|
58
|
+
"@babel/core": "^7.14.6",
|
|
59
|
+
"@babel/preset-env": "^7.14.5",
|
|
60
|
+
"@babel/register": "^7.14.5",
|
|
61
|
+
"@chiragrupani/karma-chromium-edge-launcher": "^2.1.1",
|
|
60
62
|
"babel-eslint": "^10.1.0",
|
|
61
63
|
"babel-loader": "^8.2.2",
|
|
62
64
|
"babel-plugin-add-module-exports": "^1.0.4",
|
|
63
65
|
"babel-plugin-istanbul": "^6.0.0",
|
|
66
|
+
"babel-plugin-proxy": "^1.1.0",
|
|
64
67
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
|
65
|
-
"date-fns": "^2.
|
|
66
|
-
"debounce": "^1.2.
|
|
68
|
+
"date-fns": "^2.22.1",
|
|
69
|
+
"debounce": "^1.2.1",
|
|
67
70
|
"esdoc": "^1.1.0",
|
|
68
71
|
"esdoc-accessor-plugin": "^1.0.0",
|
|
69
72
|
"esdoc-brand-plugin": "^1.0.1",
|
|
@@ -71,25 +74,25 @@
|
|
|
71
74
|
"esdoc-ecmascript-proposal-plugin": "^1.0.0",
|
|
72
75
|
"esdoc-integrate-test-plugin": "^1.0.0",
|
|
73
76
|
"esdoc-standard-plugin": "^1.0.0",
|
|
74
|
-
"eslint": "^7.
|
|
75
|
-
"htmlhint": "^0.
|
|
77
|
+
"eslint": "^7.29.0",
|
|
78
|
+
"htmlhint": "^0.15.1",
|
|
76
79
|
"in-publish": "^2.0.1",
|
|
77
|
-
"jasmine-core": "^3.
|
|
78
|
-
"karma": "^
|
|
80
|
+
"jasmine-core": "^3.7.1",
|
|
81
|
+
"karma": "^6.3.4",
|
|
79
82
|
"karma-chrome-launcher": "3.1.0",
|
|
80
83
|
"karma-coverage": "^2.0.3",
|
|
81
|
-
"karma-firefox-launcher": "^2.1.
|
|
84
|
+
"karma-firefox-launcher": "^2.1.1",
|
|
82
85
|
"karma-jasmine": "4.0.1",
|
|
83
86
|
"karma-jasmine-matchers": "4.0.2",
|
|
84
87
|
"karma-verbose-reporter": "0.0.6",
|
|
85
88
|
"karma-webpack": "^5.0.0",
|
|
86
|
-
"lint-staged": "^
|
|
89
|
+
"lint-staged": "^11.0.0",
|
|
87
90
|
"load-script": "^1.0.0",
|
|
88
91
|
"pre-commit": "^1.2.2",
|
|
89
|
-
"webpack": "^5.
|
|
90
|
-
"webpack-cli": "^4.
|
|
91
|
-
"webpack-dev-server": "^4.0.0-beta.
|
|
92
|
-
"webpack-merge": "^5.
|
|
92
|
+
"webpack": "^5.39.1",
|
|
93
|
+
"webpack-cli": "^4.7.2",
|
|
94
|
+
"webpack-dev-server": "^4.0.0-beta.3",
|
|
95
|
+
"webpack-merge": "^5.8.0"
|
|
93
96
|
},
|
|
94
97
|
"homepage": "https://wavesurfer-js.org",
|
|
95
98
|
"pre-commit": "lint-js-html",
|
|
@@ -157,6 +157,22 @@ export default class CanvasEntry {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Set the canvas transforms for wave and progress
|
|
162
|
+
*
|
|
163
|
+
* @param {boolean} vertical Whether to render vertically
|
|
164
|
+
*/
|
|
165
|
+
applyCanvasTransforms(vertical) {
|
|
166
|
+
if (vertical) {
|
|
167
|
+
// Reflect the waveform across the line y = -x
|
|
168
|
+
this.waveCtx.setTransform(0, 1, 1, 0, 0, 0);
|
|
169
|
+
|
|
170
|
+
if (this.hasProgressCanvas) {
|
|
171
|
+
this.progressCtx.setTransform(0, 1, 1, 0, 0, 0);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
160
176
|
/**
|
|
161
177
|
* Draw a rectangle for wave and progress
|
|
162
178
|
*
|
package/src/drawer.js
CHANGED
|
@@ -13,7 +13,7 @@ export default class Drawer extends util.Observer {
|
|
|
13
13
|
constructor(container, params) {
|
|
14
14
|
super();
|
|
15
15
|
|
|
16
|
-
this.container = container;
|
|
16
|
+
this.container = util.withOrientation(container, params.vertical);
|
|
17
17
|
/**
|
|
18
18
|
* @type {WavesurferParams}
|
|
19
19
|
*/
|
|
@@ -53,8 +53,9 @@ export default class Drawer extends util.Observer {
|
|
|
53
53
|
* interaction
|
|
54
54
|
*/
|
|
55
55
|
createWrapper() {
|
|
56
|
-
this.wrapper =
|
|
57
|
-
document.createElement('wave')
|
|
56
|
+
this.wrapper = util.withOrientation(
|
|
57
|
+
this.container.appendChild(document.createElement('wave')),
|
|
58
|
+
this.params.vertical
|
|
58
59
|
);
|
|
59
60
|
|
|
60
61
|
this.style(this.wrapper, {
|
|
@@ -86,39 +87,46 @@ export default class Drawer extends util.Observer {
|
|
|
86
87
|
handleEvent(e, noPrevent) {
|
|
87
88
|
!noPrevent && e.preventDefault();
|
|
88
89
|
|
|
89
|
-
const clientX =
|
|
90
|
-
? e.targetTouches[0]
|
|
91
|
-
|
|
90
|
+
const clientX = util.withOrientation(
|
|
91
|
+
e.targetTouches ? e.targetTouches[0] : e,
|
|
92
|
+
this.params.vertical
|
|
93
|
+
).clientX;
|
|
92
94
|
const bbox = this.wrapper.getBoundingClientRect();
|
|
93
95
|
|
|
94
96
|
const nominalWidth = this.width;
|
|
95
97
|
const parentWidth = this.getWidth();
|
|
98
|
+
const progressPixels = this.getProgressPixels(bbox, clientX);
|
|
96
99
|
|
|
97
100
|
let progress;
|
|
98
101
|
if (!this.params.fillParent && nominalWidth < parentWidth) {
|
|
99
|
-
progress =
|
|
100
|
-
(this.params.
|
|
101
|
-
(this.params.pixelRatio / nominalWidth) || 0;
|
|
102
|
+
progress = progressPixels *
|
|
103
|
+
(this.params.pixelRatio / nominalWidth) || 0;
|
|
102
104
|
} else {
|
|
103
|
-
progress =
|
|
104
|
-
|
|
105
|
-
? bbox.right - clientX
|
|
106
|
-
: clientX - bbox.left) +
|
|
107
|
-
this.wrapper.scrollLeft) /
|
|
108
|
-
this.wrapper.scrollWidth || 0;
|
|
105
|
+
progress = (progressPixels + this.wrapper.scrollLeft) /
|
|
106
|
+
this.wrapper.scrollWidth || 0;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
return util.clamp(progress, 0, 1);
|
|
112
110
|
}
|
|
113
111
|
|
|
112
|
+
getProgressPixels(wrapperBbox, clientX) {
|
|
113
|
+
if (this.params.rtl) {
|
|
114
|
+
return wrapperBbox.right - clientX;
|
|
115
|
+
} else {
|
|
116
|
+
return clientX - wrapperBbox.left;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
setupWrapperEvents() {
|
|
115
121
|
this.wrapper.addEventListener('click', e => {
|
|
116
|
-
const
|
|
117
|
-
|
|
122
|
+
const orientedEvent = util.withOrientation(e, this.params.vertical);
|
|
123
|
+
const scrollbarHeight = this.wrapper.offsetHeight -
|
|
124
|
+
this.wrapper.clientHeight;
|
|
125
|
+
|
|
118
126
|
if (scrollbarHeight !== 0) {
|
|
119
127
|
// scrollbar is visible. Check if click was on it
|
|
120
128
|
const bbox = this.wrapper.getBoundingClientRect();
|
|
121
|
-
if (
|
|
129
|
+
if (orientedEvent.clientY >= bbox.bottom - scrollbarHeight) {
|
|
122
130
|
// ignore mousedown as it was on the scrollbar
|
|
123
131
|
return;
|
|
124
132
|
}
|
|
@@ -274,8 +282,9 @@ export default class Drawer extends util.Observer {
|
|
|
274
282
|
width: ''
|
|
275
283
|
});
|
|
276
284
|
} else {
|
|
285
|
+
const newWidth = ~~(this.width / this.params.pixelRatio) + 'px';
|
|
277
286
|
this.style(this.wrapper, {
|
|
278
|
-
width:
|
|
287
|
+
width: newWidth
|
|
279
288
|
});
|
|
280
289
|
}
|
|
281
290
|
|
|
@@ -333,8 +342,8 @@ export default class Drawer extends util.Observer {
|
|
|
333
342
|
destroy() {
|
|
334
343
|
this.unAll();
|
|
335
344
|
if (this.wrapper) {
|
|
336
|
-
if (this.wrapper.parentNode == this.container) {
|
|
337
|
-
this.container.removeChild(this.wrapper);
|
|
345
|
+
if (this.wrapper.parentNode == this.container.domElement) {
|
|
346
|
+
this.container.removeChild(this.wrapper.domElement);
|
|
338
347
|
}
|
|
339
348
|
this.wrapper = null;
|
|
340
349
|
}
|
|
@@ -82,6 +82,13 @@ export default class MultiCanvas extends Drawer {
|
|
|
82
82
|
* @type {number}
|
|
83
83
|
*/
|
|
84
84
|
this.barRadius = params.barRadius || 0;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Whether to render the waveform vertically. Defaults to false.
|
|
88
|
+
*
|
|
89
|
+
* @type {boolean}
|
|
90
|
+
*/
|
|
91
|
+
this.vertical = params.vertical;
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
/**
|
|
@@ -97,21 +104,23 @@ export default class MultiCanvas extends Drawer {
|
|
|
97
104
|
*
|
|
98
105
|
*/
|
|
99
106
|
createElements() {
|
|
100
|
-
this.progressWave =
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
zIndex: 3,
|
|
104
|
-
left: 0,
|
|
105
|
-
top: 0,
|
|
106
|
-
bottom: 0,
|
|
107
|
-
overflow: 'hidden',
|
|
108
|
-
width: '0',
|
|
109
|
-
display: 'none',
|
|
110
|
-
boxSizing: 'border-box',
|
|
111
|
-
borderRightStyle: 'solid',
|
|
112
|
-
pointerEvents: 'none'
|
|
113
|
-
})
|
|
107
|
+
this.progressWave = util.withOrientation(
|
|
108
|
+
this.wrapper.appendChild(document.createElement('wave')),
|
|
109
|
+
this.params.vertical
|
|
114
110
|
);
|
|
111
|
+
this.style(this.progressWave, {
|
|
112
|
+
position: 'absolute',
|
|
113
|
+
zIndex: 3,
|
|
114
|
+
left: 0,
|
|
115
|
+
top: 0,
|
|
116
|
+
bottom: 0,
|
|
117
|
+
overflow: 'hidden',
|
|
118
|
+
width: '0',
|
|
119
|
+
display: 'none',
|
|
120
|
+
boxSizing: 'border-box',
|
|
121
|
+
borderRightStyle: 'solid',
|
|
122
|
+
pointerEvents: 'none'
|
|
123
|
+
});
|
|
115
124
|
|
|
116
125
|
this.addCanvas();
|
|
117
126
|
this.updateCursor();
|
|
@@ -170,33 +179,35 @@ export default class MultiCanvas extends Drawer {
|
|
|
170
179
|
const leftOffset = this.maxCanvasElementWidth * this.canvases.length;
|
|
171
180
|
|
|
172
181
|
// wave
|
|
173
|
-
|
|
174
|
-
this.wrapper.appendChild(
|
|
175
|
-
|
|
176
|
-
position: 'absolute',
|
|
177
|
-
zIndex: 2,
|
|
178
|
-
left: leftOffset + 'px',
|
|
179
|
-
top: 0,
|
|
180
|
-
bottom: 0,
|
|
181
|
-
height: '100%',
|
|
182
|
-
pointerEvents: 'none'
|
|
183
|
-
})
|
|
184
|
-
)
|
|
182
|
+
let wave = util.withOrientation(
|
|
183
|
+
this.wrapper.appendChild(document.createElement('canvas')),
|
|
184
|
+
this.params.vertical
|
|
185
185
|
);
|
|
186
|
+
this.style(wave, {
|
|
187
|
+
position: 'absolute',
|
|
188
|
+
zIndex: 2,
|
|
189
|
+
left: leftOffset + 'px',
|
|
190
|
+
top: 0,
|
|
191
|
+
bottom: 0,
|
|
192
|
+
height: '100%',
|
|
193
|
+
pointerEvents: 'none'
|
|
194
|
+
});
|
|
195
|
+
entry.initWave(wave);
|
|
186
196
|
|
|
187
197
|
// progress
|
|
188
198
|
if (this.hasProgressCanvas) {
|
|
189
|
-
|
|
190
|
-
this.progressWave.appendChild(
|
|
191
|
-
|
|
192
|
-
position: 'absolute',
|
|
193
|
-
left: leftOffset + 'px',
|
|
194
|
-
top: 0,
|
|
195
|
-
bottom: 0,
|
|
196
|
-
height: '100%'
|
|
197
|
-
})
|
|
198
|
-
)
|
|
199
|
+
let progress = util.withOrientation(
|
|
200
|
+
this.progressWave.appendChild(document.createElement('canvas')),
|
|
201
|
+
this.params.vertical
|
|
199
202
|
);
|
|
203
|
+
this.style(progress, {
|
|
204
|
+
position: 'absolute',
|
|
205
|
+
left: leftOffset + 'px',
|
|
206
|
+
top: 0,
|
|
207
|
+
bottom: 0,
|
|
208
|
+
height: '100%'
|
|
209
|
+
});
|
|
210
|
+
entry.initProgress(progress);
|
|
200
211
|
}
|
|
201
212
|
|
|
202
213
|
this.canvases.push(entry);
|
|
@@ -210,11 +221,11 @@ export default class MultiCanvas extends Drawer {
|
|
|
210
221
|
let lastEntry = this.canvases[this.canvases.length - 1];
|
|
211
222
|
|
|
212
223
|
// wave
|
|
213
|
-
lastEntry.wave.parentElement.removeChild(lastEntry.wave);
|
|
224
|
+
lastEntry.wave.parentElement.removeChild(lastEntry.wave.domElement);
|
|
214
225
|
|
|
215
226
|
// progress
|
|
216
227
|
if (this.hasProgressCanvas) {
|
|
217
|
-
lastEntry.progress.parentElement.removeChild(lastEntry.progress);
|
|
228
|
+
lastEntry.progress.parentElement.removeChild(lastEntry.progress.domElement);
|
|
218
229
|
}
|
|
219
230
|
|
|
220
231
|
// cleanup
|
|
@@ -303,8 +314,9 @@ export default class MultiCanvas extends Drawer {
|
|
|
303
314
|
|
|
304
315
|
/* in case of silences, allow the user to specify that we
|
|
305
316
|
* always draw *something* (normally a 1px high bar) */
|
|
306
|
-
if (h == 0 && this.params.barMinHeight)
|
|
317
|
+
if (h == 0 && this.params.barMinHeight) {
|
|
307
318
|
h = this.params.barMinHeight;
|
|
319
|
+
}
|
|
308
320
|
|
|
309
321
|
this.fillRect(
|
|
310
322
|
i + this.halfPixel,
|
|
@@ -386,6 +398,7 @@ export default class MultiCanvas extends Drawer {
|
|
|
386
398
|
const { waveColor, progressColor } = this.params.splitChannelsOptions.channelColors[channelIndex] || {};
|
|
387
399
|
this.canvases.forEach((entry, i) => {
|
|
388
400
|
this.setFillStyles(entry, waveColor, progressColor);
|
|
401
|
+
this.applyCanvasTransforms(entry, this.params.vertical);
|
|
389
402
|
entry.drawLines(peaks, absmax, halfH, offsetY, start, end);
|
|
390
403
|
});
|
|
391
404
|
}
|
|
@@ -424,6 +437,7 @@ export default class MultiCanvas extends Drawer {
|
|
|
424
437
|
if (intersection.x1 < intersection.x2) {
|
|
425
438
|
const { waveColor, progressColor } = this.params.splitChannelsOptions.channelColors[channelIndex] || {};
|
|
426
439
|
this.setFillStyles(entry, waveColor, progressColor);
|
|
440
|
+
this.applyCanvasTransforms(entry, this.params.vertical);
|
|
427
441
|
|
|
428
442
|
entry.fillRects(
|
|
429
443
|
intersection.x1 - leftOffset,
|
|
@@ -542,6 +556,16 @@ export default class MultiCanvas extends Drawer {
|
|
|
542
556
|
entry.setFillStyles(waveColor, progressColor);
|
|
543
557
|
}
|
|
544
558
|
|
|
559
|
+
/**
|
|
560
|
+
* Set the canvas transforms for a certain entry (wave and progress)
|
|
561
|
+
*
|
|
562
|
+
* @param {CanvasEntry} entry Target entry
|
|
563
|
+
* @param {boolean} vertical Whether to render the waveform vertically
|
|
564
|
+
*/
|
|
565
|
+
applyCanvasTransforms(entry, vertical = false) {
|
|
566
|
+
entry.applyCanvasTransforms(vertical);
|
|
567
|
+
}
|
|
568
|
+
|
|
545
569
|
/**
|
|
546
570
|
* Return image data of the multi-canvas
|
|
547
571
|
*
|
|
@@ -326,7 +326,9 @@ export default class CursorPlugin {
|
|
|
326
326
|
* @returns {number} outer width
|
|
327
327
|
*/
|
|
328
328
|
outerWidth(element) {
|
|
329
|
-
if (!element)
|
|
329
|
+
if (!element) {
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
330
332
|
|
|
331
333
|
let width = element.offsetWidth;
|
|
332
334
|
let style = getComputedStyle(element);
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {Object} MarkerParams
|
|
4
|
+
* @desc The parameters used to describe a marker.
|
|
5
|
+
* @example wavesurfer.addMarker(regionParams);
|
|
6
|
+
* @property {number} time The time to set the marker at
|
|
7
|
+
* @property {?label} string An optional marker label
|
|
8
|
+
* @property {?color} string Background color for marker
|
|
9
|
+
* @property {?position} string "top" or "bottom", defaults to "bottom"
|
|
10
|
+
* @property {?markerElement} element An HTML element to display instead of the default marker image
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Markers are points in time in the audio that can be jumped to.
|
|
16
|
+
*
|
|
17
|
+
* @implements {PluginClass}
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* import MarkersPlugin from 'wavesurfer.markers.js';
|
|
21
|
+
*
|
|
22
|
+
* // if you are using <script> tags
|
|
23
|
+
* var MarkerPlugin = window.WaveSurfer.markers;
|
|
24
|
+
*
|
|
25
|
+
* // ... initialising wavesurfer with the plugin
|
|
26
|
+
* var wavesurfer = WaveSurfer.create({
|
|
27
|
+
* // wavesurfer options ...
|
|
28
|
+
* plugins: [
|
|
29
|
+
* MarkersPlugin.create({
|
|
30
|
+
* // plugin options ...
|
|
31
|
+
* })
|
|
32
|
+
* ]
|
|
33
|
+
* });
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const DEFAULT_FILL_COLOR = "#D8D8D8";
|
|
37
|
+
const DEFAULT_POSITION = "bottom";
|
|
38
|
+
|
|
39
|
+
export default class MarkersPlugin {
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} MarkersPluginParams
|
|
42
|
+
* @property {?MarkerParams[]} markers Initial set of markers
|
|
43
|
+
* @fires MarkersPlugin#marker-click
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Markers plugin definition factory
|
|
48
|
+
*
|
|
49
|
+
* This function must be used to create a plugin definition which can be
|
|
50
|
+
* used by wavesurfer to correctly instantiate the plugin.
|
|
51
|
+
*
|
|
52
|
+
* @param {MarkersPluginParams} params parameters use to initialise the plugin
|
|
53
|
+
* @since 4.6.0
|
|
54
|
+
* @return {PluginDefinition} an object representing the plugin
|
|
55
|
+
*/
|
|
56
|
+
static create(params) {
|
|
57
|
+
return {
|
|
58
|
+
name: 'markers',
|
|
59
|
+
deferInit: params && params.deferInit ? params.deferInit : false,
|
|
60
|
+
params: params,
|
|
61
|
+
staticProps: {
|
|
62
|
+
addMarker(options) {
|
|
63
|
+
if (!this.initialisedPluginList.markers) {
|
|
64
|
+
this.initPlugin('markers');
|
|
65
|
+
}
|
|
66
|
+
return this.markers.add(options);
|
|
67
|
+
},
|
|
68
|
+
clearMarkers() {
|
|
69
|
+
this.markers && this.markers.clear();
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
instance: MarkersPlugin
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
constructor(params, ws) {
|
|
77
|
+
this.params = params;
|
|
78
|
+
this.wavesurfer = ws;
|
|
79
|
+
this.util = ws.util;
|
|
80
|
+
this.style = this.util.style;
|
|
81
|
+
this.markerWidth = 11;
|
|
82
|
+
this.markerHeight = 22;
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
this._onResize = () => {
|
|
86
|
+
this._updateMarkerPositions();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
this._onBackendCreated = () => {
|
|
90
|
+
this.wrapper = this.wavesurfer.drawer.wrapper;
|
|
91
|
+
if (this.params.markers) {
|
|
92
|
+
this.params.markers.forEach(marker => this.add(marker));
|
|
93
|
+
}
|
|
94
|
+
window.addEventListener('resize', this._onResize, true);
|
|
95
|
+
window.addEventListener('orientationchange', this._onResize, true);
|
|
96
|
+
this.wavesurfer.on('zoom', this._onResize);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
this.markers = [];
|
|
100
|
+
this._onReady = () => {
|
|
101
|
+
this.wrapper = this.wavesurfer.drawer.wrapper;
|
|
102
|
+
this._updateMarkerPositions();
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
init() {
|
|
107
|
+
// Check if ws is ready
|
|
108
|
+
if (this.wavesurfer.isReady) {
|
|
109
|
+
this._onBackendCreated();
|
|
110
|
+
this._onReady();
|
|
111
|
+
} else {
|
|
112
|
+
this.wavesurfer.once('ready', this._onReady);
|
|
113
|
+
this.wavesurfer.once('backend-created', this._onBackendCreated);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
destroy() {
|
|
118
|
+
this.wavesurfer.un('ready', this._onReady);
|
|
119
|
+
this.wavesurfer.un('backend-created', this._onBackendCreated);
|
|
120
|
+
|
|
121
|
+
this.wavesurfer.un('zoom', this._onResize);
|
|
122
|
+
|
|
123
|
+
window.removeEventListener('resize', this._onResize, true);
|
|
124
|
+
window.removeEventListener('orientationchange', this._onResize, true);
|
|
125
|
+
|
|
126
|
+
this.clear();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Add a marker
|
|
131
|
+
*
|
|
132
|
+
* @param {MarkerParams} params Marker definition
|
|
133
|
+
* @return {object} The created marker
|
|
134
|
+
*/
|
|
135
|
+
add(params) {
|
|
136
|
+
let marker = {
|
|
137
|
+
time: params.time,
|
|
138
|
+
label: params.label,
|
|
139
|
+
color: params.color || DEFAULT_FILL_COLOR,
|
|
140
|
+
position: params.position || DEFAULT_POSITION
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
if (params.markerElement) {
|
|
144
|
+
this.markerWidth = params.markerElement.width;
|
|
145
|
+
this.markerHeight = params.markerElement.height;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
marker.el = this._createMarkerElement(marker, params.markerElement);
|
|
149
|
+
|
|
150
|
+
this.wrapper.appendChild(marker.el);
|
|
151
|
+
this.markers.push(marker);
|
|
152
|
+
this._updateMarkerPositions();
|
|
153
|
+
|
|
154
|
+
return marker;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Remove a marker
|
|
159
|
+
*
|
|
160
|
+
* @param {number} index Index of the marker to remove
|
|
161
|
+
*/
|
|
162
|
+
remove(index) {
|
|
163
|
+
let marker = this.markers[index];
|
|
164
|
+
if (!marker) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.wrapper.removeChild(marker.el);
|
|
169
|
+
this.markers.splice(index, 1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_createPointerSVG(color, position) {
|
|
173
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
174
|
+
|
|
175
|
+
const el = document.createElementNS(svgNS, "svg");
|
|
176
|
+
const polygon = document.createElementNS(svgNS, "polygon");
|
|
177
|
+
|
|
178
|
+
el.setAttribute("viewBox", "0 0 40 80");
|
|
179
|
+
|
|
180
|
+
polygon.setAttribute("id", "polygon");
|
|
181
|
+
polygon.setAttribute("stroke", "#979797");
|
|
182
|
+
polygon.setAttribute("fill", color);
|
|
183
|
+
polygon.setAttribute("points", "20 0 40 30 40 80 0 80 0 30");
|
|
184
|
+
if ( position == "top" ) {
|
|
185
|
+
polygon.setAttribute("transform", "rotate(180, 20 40)");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
el.appendChild(polygon);
|
|
189
|
+
|
|
190
|
+
this.style(el, {
|
|
191
|
+
width: this.markerWidth + "px",
|
|
192
|
+
height: this.markerHeight + "px",
|
|
193
|
+
"min-width": this.markerWidth + "px",
|
|
194
|
+
"margin-right": "5px",
|
|
195
|
+
"z-index": 4
|
|
196
|
+
});
|
|
197
|
+
return el;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_createMarkerElement(marker, markerElement) {
|
|
201
|
+
let label = marker.label;
|
|
202
|
+
let time = marker.time;
|
|
203
|
+
|
|
204
|
+
const el = document.createElement('marker');
|
|
205
|
+
el.className = "wavesurfer-marker";
|
|
206
|
+
|
|
207
|
+
this.style(el, {
|
|
208
|
+
position: "absolute",
|
|
209
|
+
height: "100%",
|
|
210
|
+
display: "flex",
|
|
211
|
+
overflow: "hidden",
|
|
212
|
+
"flex-direction": (marker.position == "top" ? "column-reverse" : "column")
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const line = document.createElement('div');
|
|
216
|
+
this.style(line, {
|
|
217
|
+
"flex-grow": 1,
|
|
218
|
+
"margin-left": (this.markerWidth / 2 - 0.5) + "px",
|
|
219
|
+
background: "black",
|
|
220
|
+
width: "1px",
|
|
221
|
+
opacity: 0.1
|
|
222
|
+
});
|
|
223
|
+
el.appendChild(line);
|
|
224
|
+
|
|
225
|
+
const labelDiv = document.createElement('div');
|
|
226
|
+
const point = markerElement || this._createPointerSVG(marker.color, marker.position);
|
|
227
|
+
labelDiv.appendChild(point);
|
|
228
|
+
|
|
229
|
+
if ( label ) {
|
|
230
|
+
const labelEl = document.createElement('span');
|
|
231
|
+
labelEl.innerText = label;
|
|
232
|
+
this.style(labelEl, {
|
|
233
|
+
"font-family": "monospace",
|
|
234
|
+
"font-size": "90%"
|
|
235
|
+
});
|
|
236
|
+
labelDiv.appendChild(labelEl);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.style(labelDiv, {
|
|
240
|
+
display: "flex",
|
|
241
|
+
"align-items": "center",
|
|
242
|
+
cursor: "pointer"
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
el.appendChild(labelDiv);
|
|
246
|
+
|
|
247
|
+
labelDiv.addEventListener("click", e => {
|
|
248
|
+
e.stopPropagation();
|
|
249
|
+
this.wavesurfer.setCurrentTime(time);
|
|
250
|
+
this.wavesurfer.fireEvent("marker-click", marker, e);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return el;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
_updateMarkerPositions() {
|
|
257
|
+
const duration = this.wavesurfer.getDuration();
|
|
258
|
+
|
|
259
|
+
for ( let i = 0 ; i < this.markers.length; i++ ) {
|
|
260
|
+
let marker = this.markers[i];
|
|
261
|
+
const elementWidth =
|
|
262
|
+
this.wavesurfer.drawer.width /
|
|
263
|
+
this.wavesurfer.params.pixelRatio;
|
|
264
|
+
|
|
265
|
+
const positionPct = Math.min(marker.time / duration, 1);
|
|
266
|
+
const leftPx = ((elementWidth * positionPct) - (this.markerWidth / 2));
|
|
267
|
+
this.style(marker.el, {
|
|
268
|
+
"left": leftPx + "px",
|
|
269
|
+
"max-width": (elementWidth - leftPx) + "px"
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Remove all markers
|
|
276
|
+
*/
|
|
277
|
+
clear() {
|
|
278
|
+
while ( this.markers.length > 0 ) {
|
|
279
|
+
this.remove(0);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|