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.
Files changed (65) hide show
  1. package/CHANGES.md +36 -1
  2. package/README.md +6 -3
  3. package/dist/plugin/wavesurfer.cursor.js +39 -34
  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 +29 -27
  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 +381 -0
  12. package/dist/plugin/wavesurfer.markers.js.map +1 -0
  13. package/dist/plugin/wavesurfer.markers.min.js +7 -0
  14. package/dist/plugin/wavesurfer.markers.min.js.map +1 -0
  15. package/dist/plugin/wavesurfer.mediasession.js +29 -27
  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 +29 -27
  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 +30 -28
  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 +152 -126
  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 +97 -89
  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 +46 -32
  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 +9 -5
  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 +350 -166
  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 +282 -0
  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 +83 -70
  60. package/src/plugin/spectrogram/index.js +65 -61
  61. package/src/plugin/timeline/index.js +20 -11
  62. package/src/util/index.js +1 -0
  63. package/src/util/orientation.js +98 -0
  64. package/src/wavesurfer.js +18 -9
  65. package/src/webaudio.js +10 -9
@@ -0,0 +1,226 @@
1
+
2
+ /**
3
+ * The playhead plugin separates the notion of the currently playing position from
4
+ * a 'play-start' position. Having a playhead enables a listening pattern
5
+ * (commonly found in DAWs) that involves listening to a section of a track
6
+ * repeatedly, rather than listening to an entire track in a linear fashion.
7
+ *
8
+ * @implements {PluginClass}
9
+ *
10
+ * @example
11
+ * import PlayheadPlugin from 'wavesurfer.playhead.js';
12
+ *
13
+ * // if you are using <script> tags
14
+ * var PlayheadPlugin = window.WaveSurfer.playhead;
15
+ *
16
+ * // ... initialising wavesurfer with the plugin
17
+ * var wavesurfer = WaveSurfer.create({
18
+ * // wavesurfer options ...
19
+ * plugins: [
20
+ * PlayheadPlugin.create({
21
+ * movePlayheadOnSeek: true,
22
+ * drawPlayhead: false,
23
+ * movePlayheadOnPause: false
24
+ * })
25
+ * ]
26
+ * });
27
+ */
28
+
29
+ const DEFAULT_FILL_COLOR = '#CF2F00';
30
+
31
+ export default class PlayheadPlugin {
32
+ /**
33
+ * @typedef {Object} PlayheadPluginParams
34
+ * @property {?boolean} draw=true Draw the playhead as a triangle/line
35
+ * @property {?boolean} moveOnSeek=true Seeking (via clicking) while playing moves the playhead
36
+ * @property {?boolean} returnOnPause=true Pausing the track returns the seek position to the playhead
37
+ */
38
+
39
+ /**
40
+ * Playhead plugin definition factory
41
+ *
42
+ * This function must be used to create a plugin definition which can be
43
+ * used by wavesurfer to correctly instantiate the plugin.
44
+ *
45
+ * @param {PlayheadPluginParams} params parameters use to initialise the plugin
46
+ * @since 5.0.0
47
+ * @return {PluginDefinition} an object representing the plugin
48
+ */
49
+ static create(params) {
50
+ return {
51
+ name: 'playhead',
52
+ deferInit: params && params.deferInit ? params.deferInit : false,
53
+ params: params,
54
+ instance: PlayheadPlugin
55
+ };
56
+ }
57
+
58
+ constructor(params, ws) {
59
+ this.params = params;
60
+ this.options = {};
61
+
62
+ ['draw', 'moveOnSeek', 'returnOnPause'].forEach(opt => {
63
+ if (opt in params) {
64
+ this.options[opt] = params[opt];
65
+ } else {
66
+ this.options[opt] = true;
67
+ }
68
+ });
69
+
70
+
71
+ this.wavesurfer = ws;
72
+ this.util = ws.util;
73
+ this.style = this.util.style;
74
+ this.markerWidth = 21;
75
+ this.markerHeight = 16;
76
+ this.playheadTime = 0;
77
+ this.unFuns = [];
78
+
79
+ this._onResize = () => {
80
+ this._updatePlayheadPosition();
81
+ };
82
+
83
+ this._onReady = () => {
84
+ this.wrapper = this.wavesurfer.drawer.wrapper;
85
+ this._updatePlayheadPosition();
86
+ };
87
+ }
88
+
89
+ _onBackendCreated() {
90
+ this.wrapper = this.wavesurfer.drawer.wrapper;
91
+
92
+ if (this.options.draw) {
93
+ this._createPlayheadElement();
94
+ window.addEventListener('resize', this._onResize, true);
95
+ window.addEventListener('orientationchange', this._onResize, true);
96
+
97
+ this.wavesurferOn('zoom', this._onResize);
98
+ }
99
+
100
+ this.wavesurferOn('pause', () => {
101
+ if ( this.options.returnOnPause ) {
102
+ this.wavesurfer.setCurrentTime(this.playheadTime);
103
+ }
104
+ });
105
+
106
+ this.wavesurferOn('seek', () => {
107
+ if ( this.options.moveOnSeek ) {
108
+ this.playheadTime = this.wavesurfer.getCurrentTime();
109
+ this._updatePlayheadPosition();
110
+ }
111
+ });
112
+
113
+ this.playheadTime = this.wavesurfer.getCurrentTime();
114
+ }
115
+
116
+ wavesurferOn(ev, fn) {
117
+ let ret = this.wavesurfer.on(ev, fn);
118
+ this.unFuns.push(() => {
119
+ this.wavesurfer.un(ev, fn);
120
+ });
121
+ return ret;
122
+ }
123
+
124
+ init() {
125
+ if (this.wavesurfer.isReady) {
126
+ this._onBackendCreated();
127
+ this._onReady();
128
+ } else {
129
+ let r;
130
+
131
+ this.wavesurfer.once('ready', () => this._onReady());
132
+ this.wavesurfer.once('backend-created', () => this._onBackendCreated());
133
+ }
134
+ }
135
+
136
+ destroy() {
137
+ this.unFuns.forEach(f => f());
138
+ this.unFuns = [];
139
+
140
+ this.wrapper.removeChild(this.element);
141
+
142
+ window.removeEventListener('resize', this._onResize, true);
143
+ window.removeEventListener('orientationchange', this._onResize, true);
144
+ }
145
+
146
+ setPlayheadTime(time) {
147
+ this.playheadTime = time;
148
+
149
+ if (!this.wavesurfer.isPlaying()) {
150
+ this.wavesurfer.setCurrentTime(time);
151
+ }
152
+ this._updatePlayheadPosition();
153
+ }
154
+
155
+ _createPointerSVG() {
156
+ const svgNS = 'http://www.w3.org/2000/svg';
157
+
158
+ const el = document.createElementNS(svgNS, 'svg');
159
+ const path = document.createElementNS(svgNS, 'path');
160
+
161
+ el.setAttribute('viewBox', '0 0 33 30');
162
+ path.setAttribute('d', 'M16.75 31 31.705 5.566A3 3 0 0 0 29.146 1H4.354a3 3 0 0 0-2.56 4.566L16.75 31z');
163
+ path.setAttribute('stroke', '#979797');
164
+ path.setAttribute('fill', DEFAULT_FILL_COLOR);
165
+
166
+ el.appendChild(path);
167
+
168
+ this.style(el, {
169
+ width: this.markerWidth + 'px',
170
+ height: this.markerHeight + 'px',
171
+ cursor: 'pointer',
172
+ 'z-index': 5
173
+ });
174
+ return el;
175
+ }
176
+
177
+ _createPlayheadElement() {
178
+ const el = document.createElement('playhead');
179
+ el.className = 'wavesurfer-playhead';
180
+
181
+ this.style(el, {
182
+ position: 'absolute',
183
+ height: '100%',
184
+ display: 'flex',
185
+ 'flex-direction': 'column'
186
+ });
187
+
188
+ const pointer = this._createPointerSVG();
189
+ el.appendChild(pointer);
190
+
191
+ pointer.addEventListener('click', e => {
192
+ e.stopPropagation();
193
+ this.wavesurfer.setCurrentTime(this.playheadTime);
194
+ });
195
+
196
+ const line = document.createElement('div');
197
+ this.style(line, {
198
+ 'flex-grow': 1,
199
+ 'margin-left': (this.markerWidth / 2 - 0.5) + 'px',
200
+ background: 'black',
201
+ width: '1px',
202
+ opacity: 0.1
203
+ });
204
+
205
+ el.appendChild(line);
206
+
207
+ this.element = el;
208
+ this.wrapper.appendChild(el);
209
+ }
210
+
211
+ _updatePlayheadPosition() {
212
+ if (!this.element) {
213
+ return;
214
+ }
215
+
216
+ const duration = this.wavesurfer.getDuration();
217
+ const elementWidth =
218
+ this.wavesurfer.drawer.width /
219
+ this.wavesurfer.params.pixelRatio;
220
+
221
+ const positionPct = this.playheadTime / duration;
222
+ this.style(this.element, {
223
+ left: ((elementWidth * positionPct) - (this.markerWidth / 2)) + 'px'
224
+ });
225
+ }
226
+ }
@@ -30,6 +30,7 @@
30
30
  * @property {?number} channelIdx Select channel to draw the region on (if there are multiple channel waveforms).
31
31
  * @property {?object} handleStyle A set of CSS properties used to style the left and right handle.
32
32
  * @property {?boolean} preventContextMenu=false Determines whether the context menu is prevented from being opened.
33
+ * @property {boolean} showTooltip=true Enable/disable tooltip displaying start and end times when hovering over region.
33
34
  */
34
35
 
35
36
  import {Region} from "./region.js";
@@ -127,11 +128,16 @@ export default class RegionsPlugin {
127
128
  });
128
129
  this.wavesurfer.Region = Region;
129
130
 
131
+ // By default, scroll the container if the user drags a region
132
+ // within 5% of its edge
133
+ const scrollWidthProportion = 0.05;
130
134
  this._onBackendCreated = () => {
131
135
  this.wrapper = this.wavesurfer.drawer.wrapper;
136
+ this.orientation = this.wavesurfer.drawer.orientation;
132
137
  if (this.params.regions) {
133
138
  this.params.regions.forEach(region => {
134
- region.edgeScrollWidth = this.params.edgeScrollWidth || this.wrapper.clientWidth * 0.05;
139
+ region.edgeScrollWidth = this.params.edgeScrollWidth ||
140
+ this.wrapper.clientWidth * scrollWidthProportion;
135
141
  this.add(region);
136
142
  });
137
143
  }
@@ -141,6 +147,7 @@ export default class RegionsPlugin {
141
147
  this.list = {};
142
148
  this._onReady = () => {
143
149
  this.wrapper = this.wavesurfer.drawer.wrapper;
150
+ this.vertical = this.wavesurfer.drawer.params.vertical;
144
151
  if (this.params.dragSelection) {
145
152
  this.enableDragSelection(this.params);
146
153
  }
@@ -186,7 +193,9 @@ export default class RegionsPlugin {
186
193
  * @return {Region} The created region
187
194
  */
188
195
  add(params) {
189
- if (this.wouldExceedMaxRegions()) return null;
196
+ if (this.wouldExceedMaxRegions()) {
197
+ return null;
198
+ }
190
199
 
191
200
  if (!params.minLength && this.regionsMinLength) {
192
201
  params = {...params, minLength: this.regionsMinLength};
@@ -268,8 +277,12 @@ export default class RegionsPlugin {
268
277
  touchId = e.targetTouches ? e.targetTouches[0].identifier : null;
269
278
 
270
279
  // Store for scroll calculations
271
- maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;
272
- wrapperRect = this.wrapper.getBoundingClientRect();
280
+ maxScroll = this.wrapper.scrollWidth -
281
+ this.wrapper.clientWidth;
282
+ wrapperRect = this.util.withOrientation(
283
+ this.wrapper.getBoundingClientRect(),
284
+ this.vertical
285
+ );
273
286
 
274
287
  drag = true;
275
288
  start = this.wavesurfer.drawer.handleEvent(e, true);
@@ -314,7 +327,7 @@ export default class RegionsPlugin {
314
327
  this.wrapper.removeEventListener('mouseleave', eventUp);
315
328
  });
316
329
 
317
- const eventMove = e => {
330
+ const eventMove = event => {
318
331
  if (!drag) {
319
332
  return;
320
333
  }
@@ -322,20 +335,22 @@ export default class RegionsPlugin {
322
335
  return;
323
336
  }
324
337
 
325
- if (e.touches && e.touches.length > 1) {
338
+ if (event.touches && event.touches.length > 1) {
326
339
  return;
327
340
  }
328
- if (e.targetTouches && e.targetTouches[0].identifier != touchId) {
341
+ if (event.targetTouches && event.targetTouches[0].identifier != touchId) {
329
342
  return;
330
343
  }
331
344
 
332
345
  // auto-create a region during mouse drag, unless region-count would exceed "maxRegions"
333
346
  if (!region) {
334
347
  region = this.add(params || {});
335
- if (!region) return;
348
+ if (!region) {
349
+ return;
350
+ }
336
351
  }
337
352
 
338
- const end = this.wavesurfer.drawer.handleEvent(e);
353
+ const end = this.wavesurfer.drawer.handleEvent(event);
339
354
  const startUpdate = this.wavesurfer.regions.util.getRegionSnapToGridValue(
340
355
  start * duration
341
356
  );
@@ -347,10 +362,12 @@ export default class RegionsPlugin {
347
362
  end: Math.max(endUpdate, startUpdate)
348
363
  });
349
364
 
365
+ let orientedEvent = this.util.withOrientation(event, this.vertical);
366
+
350
367
  // If scrolling is enabled
351
368
  if (scroll && container.clientWidth < this.wrapper.scrollWidth) {
352
369
  // Check threshold based on mouse
353
- const x = e.clientX - wrapperRect.left;
370
+ const x = orientedEvent.clientX - wrapperRect.left;
354
371
  if (x <= scrollThreshold) {
355
372
  scrollDirection = -1;
356
373
  } else if (x >= wrapperRect.right - scrollThreshold) {
@@ -358,7 +375,7 @@ export default class RegionsPlugin {
358
375
  } else {
359
376
  scrollDirection = null;
360
377
  }
361
- scrollDirection && edgeScroll(e);
378
+ scrollDirection && edgeScroll(event);
362
379
  }
363
380
  };
364
381
  this.wrapper.addEventListener('mousemove', eventMove);