wavesurfer.js 4.6.0 → 5.2.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.
Files changed (66) hide show
  1. package/CHANGES.md +41 -0
  2. package/README.md +6 -3
  3. package/dist/plugin/wavesurfer.cursor.js +9 -5
  4. package/dist/plugin/wavesurfer.cursor.js.map +1 -1
  5. package/dist/plugin/wavesurfer.cursor.min.js +2 -2
  6. package/dist/plugin/wavesurfer.cursor.min.js.map +1 -1
  7. package/dist/plugin/wavesurfer.elan.js +5 -4
  8. package/dist/plugin/wavesurfer.elan.js.map +1 -1
  9. package/dist/plugin/wavesurfer.elan.min.js +2 -2
  10. package/dist/plugin/wavesurfer.elan.min.js.map +1 -1
  11. package/dist/plugin/wavesurfer.markers.js +31 -12
  12. package/dist/plugin/wavesurfer.markers.js.map +1 -1
  13. package/dist/plugin/wavesurfer.markers.min.js +2 -2
  14. package/dist/plugin/wavesurfer.markers.min.js.map +1 -1
  15. package/dist/plugin/wavesurfer.mediasession.js +5 -4
  16. package/dist/plugin/wavesurfer.mediasession.js.map +1 -1
  17. package/dist/plugin/wavesurfer.mediasession.min.js +2 -2
  18. package/dist/plugin/wavesurfer.mediasession.min.js.map +1 -1
  19. package/dist/plugin/wavesurfer.microphone.js +5 -4
  20. package/dist/plugin/wavesurfer.microphone.js.map +1 -1
  21. package/dist/plugin/wavesurfer.microphone.min.js +2 -2
  22. package/dist/plugin/wavesurfer.microphone.min.js.map +1 -1
  23. package/dist/plugin/wavesurfer.minimap.js +5 -4
  24. package/dist/plugin/wavesurfer.minimap.js.map +1 -1
  25. package/dist/plugin/wavesurfer.minimap.min.js +2 -2
  26. package/dist/plugin/wavesurfer.minimap.min.js.map +1 -1
  27. package/dist/plugin/wavesurfer.playhead.js +324 -0
  28. package/dist/plugin/wavesurfer.playhead.js.map +1 -0
  29. package/dist/plugin/wavesurfer.playhead.min.js +7 -0
  30. package/dist/plugin/wavesurfer.playhead.min.js.map +1 -0
  31. package/dist/plugin/wavesurfer.regions.js +141 -86
  32. package/dist/plugin/wavesurfer.regions.js.map +1 -1
  33. package/dist/plugin/wavesurfer.regions.min.js +2 -2
  34. package/dist/plugin/wavesurfer.regions.min.js.map +1 -1
  35. package/dist/plugin/wavesurfer.spectrogram.js +70 -63
  36. package/dist/plugin/wavesurfer.spectrogram.js.map +1 -1
  37. package/dist/plugin/wavesurfer.spectrogram.min.js +2 -2
  38. package/dist/plugin/wavesurfer.spectrogram.min.js.map +1 -1
  39. package/dist/plugin/wavesurfer.timeline.js +32 -16
  40. package/dist/plugin/wavesurfer.timeline.js.map +1 -1
  41. package/dist/plugin/wavesurfer.timeline.min.js +2 -2
  42. package/dist/plugin/wavesurfer.timeline.min.js.map +1 -1
  43. package/dist/wavesurfer-html-init.js +5 -4
  44. package/dist/wavesurfer-html-init.js.map +1 -1
  45. package/dist/wavesurfer-html-init.min.js +2 -2
  46. package/dist/wavesurfer-html-init.min.js.map +1 -1
  47. package/dist/wavesurfer.js +326 -60
  48. package/dist/wavesurfer.js.map +1 -1
  49. package/dist/wavesurfer.min.js +2 -2
  50. package/dist/wavesurfer.min.js.map +1 -1
  51. package/package.json +19 -16
  52. package/src/drawer.canvasentry.js +16 -0
  53. package/src/drawer.js +30 -21
  54. package/src/drawer.multicanvas.js +63 -39
  55. package/src/plugin/cursor/index.js +3 -1
  56. package/src/plugin/markers/index.js +22 -8
  57. package/src/plugin/playhead/index.js +226 -0
  58. package/src/plugin/regions/index.js +28 -11
  59. package/src/plugin/regions/region.js +116 -75
  60. package/src/plugin/spectrogram/index.js +65 -61
  61. package/src/plugin/timeline/index.js +30 -19
  62. package/src/util/index.js +2 -0
  63. package/src/util/orientation.js +98 -0
  64. package/src/util/silence-mode.js +35 -0
  65. package/src/wavesurfer.js +41 -22
  66. package/src/webaudio.js +11 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wavesurfer.js",
3
- "version": "4.6.0",
3
+ "version": "5.2.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.13.8",
58
- "@babel/preset-env": "^7.13.9",
59
- "@babel/register": "^7.13.8",
58
+ "@babel/core": "^7.15.0",
59
+ "@babel/preset-env": "^7.15.0",
60
+ "@babel/register": "^7.15.3",
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.18.0",
66
- "debounce": "^1.2.0",
68
+ "date-fns": "^2.23.0",
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.21.0",
75
- "htmlhint": "^0.14.2",
77
+ "eslint": "^7.32.0",
78
+ "htmlhint": "^0.15.1",
76
79
  "in-publish": "^2.0.1",
77
- "jasmine-core": "^3.6.0",
78
- "karma": "^5.2.3",
80
+ "jasmine-core": "^3.8.0",
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.0",
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": "^10.5.4",
89
+ "lint-staged": "^11.1.2",
87
90
  "load-script": "^1.0.0",
88
91
  "pre-commit": "^1.2.2",
89
- "webpack": "^5.24.3",
90
- "webpack-cli": "^4.5.0",
91
- "webpack-dev-server": "^4.0.0-beta.0",
92
- "webpack-merge": "^5.7.3"
92
+ "webpack": "^5.50.0",
93
+ "webpack-cli": "^4.8.0",
94
+ "webpack-dev-server": "^4.0.0-rc.0",
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 = this.container.appendChild(
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 = e.targetTouches
90
- ? e.targetTouches[0].clientX
91
- : e.clientX;
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.rtl ? bbox.right - clientX : clientX - bbox.left) *
101
- (this.params.pixelRatio / nominalWidth) || 0;
102
+ progress = progressPixels *
103
+ (this.params.pixelRatio / nominalWidth) || 0;
102
104
  } else {
103
- progress =
104
- ((this.params.rtl
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 scrollbarHeight =
117
- this.wrapper.offsetHeight - this.wrapper.clientHeight;
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 (e.clientY >= bbox.bottom - scrollbarHeight) {
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: ~~(this.width / this.params.pixelRatio) + 'px'
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 = this.wrapper.appendChild(
101
- this.style(document.createElement('wave'), {
102
- position: 'absolute',
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
- entry.initWave(
174
- this.wrapper.appendChild(
175
- this.style(document.createElement('canvas'), {
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
- entry.initProgress(
190
- this.progressWave.appendChild(
191
- this.style(document.createElement('canvas'), {
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) return 0;
329
+ if (!element) {
330
+ return 0;
331
+ }
330
332
 
331
333
  let width = element.offsetWidth;
332
334
  let style = getComputedStyle(element);
@@ -4,9 +4,10 @@
4
4
  * @desc The parameters used to describe a marker.
5
5
  * @example wavesurfer.addMarker(regionParams);
6
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
7
+ * @property {?label} string An optional marker label
8
+ * @property {?color} string Background color for marker
9
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
10
11
  */
11
12
 
12
13
 
@@ -39,6 +40,7 @@ export default class MarkersPlugin {
39
40
  /**
40
41
  * @typedef {Object} MarkersPluginParams
41
42
  * @property {?MarkerParams[]} markers Initial set of markers
43
+ * @fires MarkersPlugin#marker-click
42
44
  */
43
45
 
44
46
  /**
@@ -138,7 +140,12 @@ export default class MarkersPlugin {
138
140
  position: params.position || DEFAULT_POSITION
139
141
  };
140
142
 
141
- marker.el = this._createMarkerElement(marker);
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);
142
149
 
143
150
  this.wrapper.appendChild(marker.el);
144
151
  this.markers.push(marker);
@@ -154,7 +161,9 @@ export default class MarkersPlugin {
154
161
  */
155
162
  remove(index) {
156
163
  let marker = this.markers[index];
157
- if ( !marker ) return;
164
+ if (!marker) {
165
+ return;
166
+ }
158
167
 
159
168
  this.wrapper.removeChild(marker.el);
160
169
  this.markers.splice(index, 1);
@@ -181,13 +190,14 @@ export default class MarkersPlugin {
181
190
  this.style(el, {
182
191
  width: this.markerWidth + "px",
183
192
  height: this.markerHeight + "px",
193
+ "min-width": this.markerWidth + "px",
184
194
  "margin-right": "5px",
185
195
  "z-index": 4
186
196
  });
187
197
  return el;
188
198
  }
189
199
 
190
- _createMarkerElement(marker) {
200
+ _createMarkerElement(marker, markerElement) {
191
201
  let label = marker.label;
192
202
  let time = marker.time;
193
203
 
@@ -198,6 +208,7 @@ export default class MarkersPlugin {
198
208
  position: "absolute",
199
209
  height: "100%",
200
210
  display: "flex",
211
+ overflow: "hidden",
201
212
  "flex-direction": (marker.position == "top" ? "column-reverse" : "column")
202
213
  });
203
214
 
@@ -212,7 +223,7 @@ export default class MarkersPlugin {
212
223
  el.appendChild(line);
213
224
 
214
225
  const labelDiv = document.createElement('div');
215
- const point = this._createPointerSVG(marker.color, marker.position);
226
+ const point = markerElement || this._createPointerSVG(marker.color, marker.position);
216
227
  labelDiv.appendChild(point);
217
228
 
218
229
  if ( label ) {
@@ -236,6 +247,7 @@ export default class MarkersPlugin {
236
247
  labelDiv.addEventListener("click", e => {
237
248
  e.stopPropagation();
238
249
  this.wavesurfer.setCurrentTime(time);
250
+ this.wavesurfer.fireEvent("marker-click", marker, e);
239
251
  });
240
252
 
241
253
  return el;
@@ -250,9 +262,11 @@ export default class MarkersPlugin {
250
262
  this.wavesurfer.drawer.width /
251
263
  this.wavesurfer.params.pixelRatio;
252
264
 
253
- const positionPct = marker.time / duration;
265
+ const positionPct = Math.min(marker.time / duration, 1);
266
+ const leftPx = ((elementWidth * positionPct) - (this.markerWidth / 2));
254
267
  this.style(marker.el, {
255
- left: ((elementWidth * positionPct) - (this.markerWidth / 2)) + "px"
268
+ "left": leftPx + "px",
269
+ "max-width": (elementWidth - leftPx) + "px"
256
270
  });
257
271
  }
258
272
  }