wavesurfer.js 5.1.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 (56) hide show
  1. package/CHANGES.md +11 -0
  2. package/dist/plugin/wavesurfer.cursor.js +2 -2
  3. package/dist/plugin/wavesurfer.cursor.js.map +1 -1
  4. package/dist/plugin/wavesurfer.cursor.min.js +2 -2
  5. package/dist/plugin/wavesurfer.cursor.min.js.map +1 -1
  6. package/dist/plugin/wavesurfer.elan.js +2 -2
  7. package/dist/plugin/wavesurfer.elan.js.map +1 -1
  8. package/dist/plugin/wavesurfer.elan.min.js +2 -2
  9. package/dist/plugin/wavesurfer.elan.min.js.map +1 -1
  10. package/dist/plugin/wavesurfer.markers.js +2 -2
  11. package/dist/plugin/wavesurfer.markers.js.map +1 -1
  12. package/dist/plugin/wavesurfer.markers.min.js +2 -2
  13. package/dist/plugin/wavesurfer.markers.min.js.map +1 -1
  14. package/dist/plugin/wavesurfer.mediasession.js +2 -2
  15. package/dist/plugin/wavesurfer.mediasession.js.map +1 -1
  16. package/dist/plugin/wavesurfer.mediasession.min.js +2 -2
  17. package/dist/plugin/wavesurfer.mediasession.min.js.map +1 -1
  18. package/dist/plugin/wavesurfer.microphone.js +2 -2
  19. package/dist/plugin/wavesurfer.microphone.js.map +1 -1
  20. package/dist/plugin/wavesurfer.microphone.min.js +2 -2
  21. package/dist/plugin/wavesurfer.microphone.min.js.map +1 -1
  22. package/dist/plugin/wavesurfer.minimap.js +2 -2
  23. package/dist/plugin/wavesurfer.minimap.js.map +1 -1
  24. package/dist/plugin/wavesurfer.minimap.min.js +2 -2
  25. package/dist/plugin/wavesurfer.minimap.min.js.map +1 -1
  26. package/dist/plugin/wavesurfer.playhead.js +2 -2
  27. package/dist/plugin/wavesurfer.playhead.js.map +1 -1
  28. package/dist/plugin/wavesurfer.playhead.min.js +2 -2
  29. package/dist/plugin/wavesurfer.playhead.min.js.map +1 -1
  30. package/dist/plugin/wavesurfer.regions.js +37 -7
  31. package/dist/plugin/wavesurfer.regions.js.map +1 -1
  32. package/dist/plugin/wavesurfer.regions.min.js +2 -2
  33. package/dist/plugin/wavesurfer.regions.min.js.map +1 -1
  34. package/dist/plugin/wavesurfer.spectrogram.js +2 -2
  35. package/dist/plugin/wavesurfer.spectrogram.js.map +1 -1
  36. package/dist/plugin/wavesurfer.spectrogram.min.js +2 -2
  37. package/dist/plugin/wavesurfer.spectrogram.min.js.map +1 -1
  38. package/dist/plugin/wavesurfer.timeline.js +14 -11
  39. package/dist/plugin/wavesurfer.timeline.js.map +1 -1
  40. package/dist/plugin/wavesurfer.timeline.min.js +2 -2
  41. package/dist/plugin/wavesurfer.timeline.min.js.map +1 -1
  42. package/dist/wavesurfer-html-init.js +2 -2
  43. package/dist/wavesurfer-html-init.js.map +1 -1
  44. package/dist/wavesurfer-html-init.min.js +2 -2
  45. package/dist/wavesurfer-html-init.min.js.map +1 -1
  46. package/dist/wavesurfer.js +99 -21
  47. package/dist/wavesurfer.js.map +1 -1
  48. package/dist/wavesurfer.min.js +2 -2
  49. package/dist/wavesurfer.min.js.map +1 -1
  50. package/package.json +11 -11
  51. package/src/plugin/regions/region.js +33 -5
  52. package/src/plugin/timeline/index.js +11 -9
  53. package/src/util/index.js +1 -0
  54. package/src/util/silence-mode.js +35 -0
  55. package/src/wavesurfer.js +23 -13
  56. package/src/webaudio.js +10 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wavesurfer.js",
3
- "version": "5.1.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": {
@@ -55,9 +55,9 @@
55
55
  "visualization"
56
56
  ],
57
57
  "devDependencies": {
58
- "@babel/core": "^7.14.6",
59
- "@babel/preset-env": "^7.14.5",
60
- "@babel/register": "^7.14.5",
58
+ "@babel/core": "^7.15.0",
59
+ "@babel/preset-env": "^7.15.0",
60
+ "@babel/register": "^7.15.3",
61
61
  "@chiragrupani/karma-chromium-edge-launcher": "^2.1.1",
62
62
  "babel-eslint": "^10.1.0",
63
63
  "babel-loader": "^8.2.2",
@@ -65,7 +65,7 @@
65
65
  "babel-plugin-istanbul": "^6.0.0",
66
66
  "babel-plugin-proxy": "^1.1.0",
67
67
  "babel-plugin-transform-class-properties": "^6.24.1",
68
- "date-fns": "^2.22.1",
68
+ "date-fns": "^2.23.0",
69
69
  "debounce": "^1.2.1",
70
70
  "esdoc": "^1.1.0",
71
71
  "esdoc-accessor-plugin": "^1.0.0",
@@ -74,10 +74,10 @@
74
74
  "esdoc-ecmascript-proposal-plugin": "^1.0.0",
75
75
  "esdoc-integrate-test-plugin": "^1.0.0",
76
76
  "esdoc-standard-plugin": "^1.0.0",
77
- "eslint": "^7.29.0",
77
+ "eslint": "^7.32.0",
78
78
  "htmlhint": "^0.15.1",
79
79
  "in-publish": "^2.0.1",
80
- "jasmine-core": "^3.7.1",
80
+ "jasmine-core": "^3.8.0",
81
81
  "karma": "^6.3.4",
82
82
  "karma-chrome-launcher": "3.1.0",
83
83
  "karma-coverage": "^2.0.3",
@@ -86,12 +86,12 @@
86
86
  "karma-jasmine-matchers": "4.0.2",
87
87
  "karma-verbose-reporter": "0.0.6",
88
88
  "karma-webpack": "^5.0.0",
89
- "lint-staged": "^11.0.0",
89
+ "lint-staged": "^11.1.2",
90
90
  "load-script": "^1.0.0",
91
91
  "pre-commit": "^1.2.2",
92
- "webpack": "^5.39.1",
93
- "webpack-cli": "^4.7.2",
94
- "webpack-dev-server": "^4.0.0-beta.3",
92
+ "webpack": "^5.50.0",
93
+ "webpack-cli": "^4.8.0",
94
+ "webpack-dev-server": "^4.0.0-rc.0",
95
95
  "webpack-merge": "^5.8.0"
96
96
  },
97
97
  "homepage": "https://wavesurfer-js.org",
@@ -87,7 +87,7 @@ export class Region {
87
87
  }
88
88
 
89
89
  /* Update region params. */
90
- update(params) {
90
+ update(params, eventParams) {
91
91
  if (params.start != null) {
92
92
  this.start = Number(params.start);
93
93
  }
@@ -125,7 +125,7 @@ export class Region {
125
125
 
126
126
  this.updateRender();
127
127
  this.fireEvent('update');
128
- this.wavesurfer.fireEvent('region-updated', this);
128
+ this.wavesurfer.fireEvent('region-updated', this, eventParams);
129
129
  }
130
130
 
131
131
  /* Remove a single region. */
@@ -708,10 +708,33 @@ export class Region {
708
708
  delta = this.start * -1;
709
709
  }
710
710
 
711
+ const eventParams = {
712
+ direction: this._getDragDirection(delta),
713
+ action: 'drag'
714
+ };
715
+
711
716
  this.update({
712
717
  start: this.start + delta,
713
718
  end: this.end + delta
714
- });
719
+ }, eventParams);
720
+ }
721
+
722
+ /**
723
+ * Returns the direction of dragging region based on delta
724
+ * Negative delta means region is moving to the left
725
+ * Positive - to the right
726
+ * For zero delta the direction is not defined
727
+ * @param {number} delta Drag offset
728
+ * @returns {string|null} Direction 'left', 'right' or null
729
+ */
730
+ _getDragDirection(delta) {
731
+ if (delta < 0) {
732
+ return 'left';
733
+ }
734
+ if (delta > 0) {
735
+ return 'right';
736
+ }
737
+ return null;
715
738
  }
716
739
 
717
740
  /**
@@ -724,6 +747,11 @@ export class Region {
724
747
  */
725
748
  onResize(delta, direction) {
726
749
  const duration = this.wavesurfer.getDuration();
750
+ const eventParams = {
751
+ action: 'resize',
752
+ direction: direction === 'start' ? 'right' : 'left'
753
+ };
754
+
727
755
  if (direction === 'start') {
728
756
  // Check if changing the start by the given delta would result in the region being smaller than minLength
729
757
  // Ignore cases where we are making the region wider rather than shrinking it
@@ -738,7 +766,7 @@ export class Region {
738
766
  this.update({
739
767
  start: Math.min(this.start + delta, this.end),
740
768
  end: Math.max(this.start + delta, this.end)
741
- });
769
+ }, eventParams);
742
770
  } else {
743
771
  // Check if changing the end by the given delta would result in the region being smaller than minLength
744
772
  // Ignore cases where we are making the region wider rather than shrinking it
@@ -753,7 +781,7 @@ export class Region {
753
781
  this.update({
754
782
  start: Math.min(this.end + delta, this.start),
755
783
  end: Math.max(this.end + delta, this.start)
756
- });
784
+ }, eventParams);
757
785
  }
758
786
  }
759
787
 
@@ -505,18 +505,20 @@ export default class TimelinePlugin {
505
505
 
506
506
  this.canvases.forEach(canvas => {
507
507
  const context = canvas.getContext('2d');
508
- const canvasWidth = context.canvas.width;
508
+ if (context) {
509
+ const canvasWidth = context.canvas.width;
509
510
 
510
- if (xOffset > x + textWidth) {
511
- return;
512
- }
511
+ if (xOffset > x + textWidth) {
512
+ return;
513
+ }
513
514
 
514
- if (xOffset + canvasWidth > x && context) {
515
- textWidth = context.measureText(text).width;
516
- context.fillText(text, x - xOffset, y);
517
- }
515
+ if (xOffset + canvasWidth > x && context) {
516
+ textWidth = context.measureText(text).width;
517
+ context.fillText(text, x - xOffset, y);
518
+ }
518
519
 
519
- xOffset += canvasWidth;
520
+ xOffset += canvasWidth;
521
+ }
520
522
  });
521
523
  }
522
524
 
package/src/util/index.js CHANGED
@@ -11,3 +11,4 @@ export { default as preventClick } from './prevent-click';
11
11
  export { default as fetchFile } from './fetch';
12
12
  export { default as clamp } from './clamp';
13
13
  export { default as withOrientation } from './orientation';
14
+ export { default as ignoreSilenceMode } from './silence-mode';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Ignores device silence mode when using the `WebAudio` backend.
3
+ *
4
+ * Many mobile devices contain a hardware button to mute the ringtone for incoming
5
+ * calls and messages. Unfortunately, on some platforms like iOS, this also mutes
6
+ * wavesurfer's audio when using the `WebAudio` backend. This function creates a
7
+ * temporary `<audio>` element that makes sure the WebAudio backend keeps playing
8
+ * when muting the device ringer.
9
+ *
10
+ * @since 5.2.0
11
+ */
12
+ export default function ignoreSilenceMode() {
13
+ // Set the src to a short bit of url encoded as a silent mp3
14
+ // NOTE The silence MP3 must be high quality, when web audio sounds are played
15
+ // in parallel the web audio sound is mixed to match the bitrate of the html sound
16
+ // 0.01 seconds of silence VBR220-260 Joint Stereo 859B
17
+ const audioData = "data:audio/mpeg;base64,//uQxAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAACcQCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA//////////////////////////////////////////////////////////////////8AAABhTEFNRTMuMTAwA8MAAAAAAAAAABQgJAUHQQAB9AAAAnGMHkkIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sQxAADgnABGiAAQBCqgCRMAAgEAH///////////////7+n/9FTuQsQH//////2NG0jWUGlio5gLQTOtIoeR2WX////X4s9Atb/JRVCbBUpeRUq//////////////////9RUi0f2jn/+xDECgPCjAEQAABN4AAANIAAAAQVTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==";
18
+
19
+ // disable iOS Airplay (setting the attribute in js doesn't work)
20
+ let tmp = document.createElement("div");
21
+ tmp.innerHTML = '<audio x-webkit-airplay="deny"></audio>';
22
+
23
+ let audioSilentMode = tmp.children.item(0);
24
+ audioSilentMode.src = audioData;
25
+ audioSilentMode.preload = "auto";
26
+ audioSilentMode.type = "audio/mpeg";
27
+ audioSilentMode.disableRemotePlayback = true;
28
+
29
+ // play
30
+ audioSilentMode.play();
31
+
32
+ // cleanup
33
+ audioSilentMode.remove();
34
+ tmp.remove();
35
+ }
package/src/wavesurfer.js CHANGED
@@ -69,6 +69,8 @@ import MediaElementWebAudio from './mediaelement-webaudio';
69
69
  * pixels.
70
70
  * @property {boolean} hideScrollbar=false Whether to hide the horizontal
71
71
  * scrollbar when one would normally be shown.
72
+ * @property {boolean} ignoreSilenceMode=false If true, ignores device silence mode
73
+ * when using the `WebAudio` backend.
72
74
  * @property {boolean} interact=true Whether the mouse interaction will be
73
75
  * enabled at initialization. You can switch this parameter at any time later
74
76
  * on.
@@ -268,6 +270,7 @@ export default class WaveSurfer extends util.Observer {
268
270
  forceDecode: false,
269
271
  height: 128,
270
272
  hideScrollbar: false,
273
+ ignoreSilenceMode: false,
271
274
  interact: true,
272
275
  loopSelection: true,
273
276
  maxCanvasWidth: 4000,
@@ -813,6 +816,11 @@ export default class WaveSurfer extends util.Observer {
813
816
  * wavesurfer.play(1, 5);
814
817
  */
815
818
  play(start, end) {
819
+ if (this.params.ignoreSilenceMode) {
820
+ // ignores device hardware silence mode
821
+ util.ignoreSilenceMode();
822
+ }
823
+
816
824
  this.fireEvent('interaction', () => this.play(start, end));
817
825
  return this.backend.play(start, end);
818
826
  }
@@ -1538,19 +1546,21 @@ export default class WaveSurfer extends util.Observer {
1538
1546
  * @param {function} callback The function to call on complete
1539
1547
  */
1540
1548
  decodeArrayBuffer(arraybuffer, callback) {
1541
- this.arraybuffer = arraybuffer;
1542
- this.backend.decodeArrayBuffer(
1543
- arraybuffer,
1544
- data => {
1545
- // Only use the decoded data if we haven't been destroyed or
1546
- // another decode started in the meantime
1547
- if (!this.isDestroyed && this.arraybuffer == arraybuffer) {
1548
- callback(data);
1549
- this.arraybuffer = null;
1550
- }
1551
- },
1552
- () => this.fireEvent('error', 'Error decoding audiobuffer')
1553
- );
1549
+ if (!this.isDestroyed) {
1550
+ this.arraybuffer = arraybuffer;
1551
+ this.backend.decodeArrayBuffer(
1552
+ arraybuffer,
1553
+ data => {
1554
+ // Only use the decoded data if we haven't been destroyed or
1555
+ // another decode started in the meantime
1556
+ if (!this.isDestroyed && this.arraybuffer == arraybuffer) {
1557
+ callback(data);
1558
+ this.arraybuffer = null;
1559
+ }
1560
+ },
1561
+ () => this.fireEvent('error', 'Error decoding audiobuffer')
1562
+ );
1563
+ }
1554
1564
  }
1555
1565
 
1556
1566
  /**
package/src/webaudio.js CHANGED
@@ -702,7 +702,16 @@ export default class WebAudio extends util.Observer {
702
702
  this.scheduledPause = null;
703
703
 
704
704
  this.startPosition += this.getPlayedTime();
705
- this.source && this.source.stop(0);
705
+ try {
706
+ this.source && this.source.stop(0);
707
+ } catch (err) {
708
+ // Calling stop can throw the following 2 errors:
709
+ // - RangeError (The value specified for when is negative.)
710
+ // - InvalidStateNode (The node has not been started by calling start().)
711
+ // We can safely ignore both errors, because:
712
+ // - The range is surely correct
713
+ // - The node might not have been started yet, in which case we just want to carry on without causing any trouble.
714
+ }
706
715
 
707
716
  this.setState(PAUSED);
708
717