wavesurfer.js 7.0.0-beta.9 → 7.0.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 (76) hide show
  1. package/README.md +56 -24
  2. package/dist/base-plugin.d.ts +5 -2
  3. package/dist/base-plugin.js +1 -0
  4. package/dist/decoder.js +17 -6
  5. package/dist/draggable.js +10 -7
  6. package/dist/fetcher.d.ts +1 -3
  7. package/dist/fetcher.js +13 -6
  8. package/dist/player.d.ts +2 -2
  9. package/dist/player.js +10 -8
  10. package/dist/plugins/base-plugin.d.ts +16 -0
  11. package/dist/plugins/decoder.d.ts +9 -0
  12. package/dist/plugins/draggable.d.ts +1 -0
  13. package/dist/plugins/envelope.cjs +1 -0
  14. package/dist/plugins/envelope.d.ts +12 -4
  15. package/dist/plugins/envelope.esm.js +1 -0
  16. package/dist/plugins/envelope.js +29 -9
  17. package/dist/plugins/envelope.min.js +1 -0
  18. package/dist/plugins/event-emitter.d.ts +19 -0
  19. package/dist/plugins/fetcher.d.ts +5 -0
  20. package/dist/plugins/hover.cjs +1 -0
  21. package/dist/plugins/hover.d.ts +35 -0
  22. package/dist/plugins/hover.esm.js +1 -0
  23. package/dist/plugins/hover.js +101 -0
  24. package/dist/plugins/hover.min.js +1 -0
  25. package/dist/plugins/minimap.cjs +1 -0
  26. package/dist/plugins/minimap.d.ts +2 -2
  27. package/dist/plugins/minimap.esm.js +1 -0
  28. package/dist/plugins/minimap.js +8 -13
  29. package/dist/plugins/minimap.min.js +1 -0
  30. package/dist/plugins/player.d.ts +45 -0
  31. package/dist/plugins/plugins/envelope.d.ts +79 -0
  32. package/dist/plugins/plugins/hover.d.ts +35 -0
  33. package/dist/plugins/plugins/minimap.d.ts +39 -0
  34. package/dist/plugins/plugins/record.d.ts +31 -0
  35. package/dist/plugins/plugins/regions.d.ts +115 -0
  36. package/dist/plugins/plugins/spectrogram.d.ts +76 -0
  37. package/dist/plugins/plugins/timeline.d.ts +47 -0
  38. package/dist/plugins/record.cjs +1 -0
  39. package/dist/plugins/record.d.ts +7 -4
  40. package/dist/plugins/record.esm.js +1 -0
  41. package/dist/plugins/record.js +73 -66
  42. package/dist/plugins/record.min.js +1 -0
  43. package/dist/plugins/regions.cjs +1 -0
  44. package/dist/plugins/regions.d.ts +25 -4
  45. package/dist/plugins/regions.esm.js +1 -0
  46. package/dist/plugins/regions.js +60 -60
  47. package/dist/plugins/regions.min.js +1 -0
  48. package/dist/plugins/renderer.d.ts +44 -0
  49. package/dist/plugins/spectrogram.cjs +1 -0
  50. package/dist/plugins/spectrogram.d.ts +10 -3
  51. package/dist/plugins/spectrogram.esm.js +1 -0
  52. package/dist/plugins/spectrogram.js +166 -20
  53. package/dist/plugins/spectrogram.min.js +1 -0
  54. package/dist/plugins/timeline.cjs +1 -0
  55. package/dist/plugins/timeline.d.ts +5 -3
  56. package/dist/plugins/timeline.esm.js +1 -0
  57. package/dist/plugins/timeline.js +27 -21
  58. package/dist/plugins/timeline.min.js +1 -0
  59. package/dist/plugins/timer.d.ts +11 -0
  60. package/dist/plugins/wavesurfer.d.ts +156 -0
  61. package/dist/renderer.js +11 -7
  62. package/dist/wavesurfer.cjs +1 -0
  63. package/dist/wavesurfer.d.ts +5 -3
  64. package/dist/wavesurfer.esm.js +1 -0
  65. package/dist/wavesurfer.js +62 -41
  66. package/dist/wavesurfer.min.js +1 -0
  67. package/package.json +21 -22
  68. package/dist/plugins/envelope.min.cjs +0 -1
  69. package/dist/plugins/minimap.min.cjs +0 -1
  70. package/dist/plugins/record.min.cjs +0 -1
  71. package/dist/plugins/regions.min.cjs +0 -1
  72. package/dist/plugins/spectrogram-fft.d.ts +0 -9
  73. package/dist/plugins/spectrogram-fft.js +0 -150
  74. package/dist/plugins/spectrogram.min.cjs +0 -1
  75. package/dist/plugins/timeline.min.cjs +0 -1
  76. package/dist/wavesurfer.min.cjs +0 -1
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The Timeline plugin adds timestamps and notches under the waveform.
3
3
  */
4
- import BasePlugin from '../base-plugin.js';
4
+ import BasePlugin, { type BasePluginEvents } from '../base-plugin.js';
5
5
  export type TimelinePluginOptions = {
6
6
  /** The height of the timeline in pixels, defaults to 20 */
7
7
  height?: number;
@@ -19,11 +19,14 @@ export type TimelinePluginOptions = {
19
19
  secondaryLabelInterval?: number;
20
20
  /** Custom inline style to apply to the container */
21
21
  style?: Partial<CSSStyleDeclaration> | string;
22
+ /** Turn the time into a suitable label for the time. */
23
+ formatTimeCallback?: (seconds: number) => string;
22
24
  };
23
25
  declare const defaultOptions: {
24
26
  height: number;
27
+ formatTimeCallback: (seconds: number) => string;
25
28
  };
26
- export type TimelinePluginEvents = {
29
+ export type TimelinePluginEvents = BasePluginEvents & {
27
30
  ready: [];
28
31
  };
29
32
  declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOptions> {
@@ -36,7 +39,6 @@ declare class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePl
36
39
  /** Unmount */
37
40
  destroy(): void;
38
41
  private initTimelineWrapper;
39
- private formatTime;
40
42
  private defaultTimeInterval;
41
43
  private defaultPrimaryLabelInterval;
42
44
  private defaultSecondaryLabelInterval;
@@ -0,0 +1 @@
1
+ class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),n=this.on(t,(()=>{i(),n()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}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()))}}const i={height:20,formatTimeCallback:t=>{if(t/60>1){return`${Math.floor(t/60)}:${`${(t=Math.round(t%60))<10?"0":""}${t}`}`}return`${Math.round(1e3*t)/1e3}`}};class n extends e{constructor(t){super(t||{}),this.options=Object.assign({},i,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new n(t)}onInit(){var t;if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const e=null!==(t=this.options.container)&&void 0!==t?t:this.wavesurfer.getWrapper();this.options.insertPosition?(e.firstElementChild||e).insertAdjacentElement(this.options.insertPosition,this.timelineWrapper):e.appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("redraw",(()=>{var t;this.initTimeline((null===(t=this.wavesurfer)||void 0===t?void 0:t.getDuration())||0)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){const t=document.createElement("div");return t.setAttribute("part","timeline"),t}defaultTimeInterval(t){return t>=25?1:5*t>=25?5:15*t>=25?15:60*Math.ceil(.5/t)}defaultPrimaryLabelInterval(t){return t>=25?10:5*t>=25?6:4}defaultSecondaryLabelInterval(t){return t>=25?5:2}initTimeline(t){var e,i,n;const s=this.timelineWrapper.scrollWidth/t,r=null!==(e=this.options.timeInterval)&&void 0!==e?e:this.defaultTimeInterval(s),o=null!==(i=this.options.primaryLabelInterval)&&void 0!==i?i:this.defaultPrimaryLabelInterval(s),l=null!==(n=this.options.secondaryLabelInterval)&&void 0!==n?n:this.defaultSecondaryLabelInterval(s),a="beforebegin"===this.options.insertPosition,h=document.createElement("div");if(h.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n position: relative;\n `),a){const t="\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n ";h.setAttribute("style",h.getAttribute("style")+t)}"string"==typeof this.options.style?h.setAttribute("style",h.getAttribute("style")+this.options.style):"object"==typeof this.options.style&&Object.assign(h.style,this.options.style);const p=document.createElement("div");p.setAttribute("part","timeline-notch"),p.setAttribute("style",`\n width: 0;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: ${a?"flex-start":"flex-end"};\n ${a?"top: 0;":"bottom: 0;"}\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n position: absolute;\n z-index: 1;\n `);for(let e=0;e<t;e+=r){const t=p.cloneNode(),i=Math.round(100*e)/100%o==0,n=Math.round(100*e)/100%l==0;(i||n)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.options.formatTimeCallback(e),i&&(t.style.opacity="1")),t.style.left=e*s+"px",h.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(h),this.emit("ready")}}export{n as default};
@@ -4,6 +4,17 @@
4
4
  import BasePlugin from '../base-plugin.js';
5
5
  const defaultOptions = {
6
6
  height: 20,
7
+ formatTimeCallback: (seconds) => {
8
+ if (seconds / 60 > 1) {
9
+ // calculate minutes and seconds from seconds count
10
+ const minutes = Math.floor(seconds / 60);
11
+ seconds = Math.round(seconds % 60);
12
+ const paddedSeconds = `${seconds < 10 ? '0' : ''}${seconds}`;
13
+ return `${minutes}:${paddedSeconds}`;
14
+ }
15
+ const rounded = Math.round(seconds * 1000) / 1000;
16
+ return `${rounded}`;
17
+ },
7
18
  };
8
19
  class TimelinePlugin extends BasePlugin {
9
20
  constructor(options) {
@@ -16,10 +27,11 @@ class TimelinePlugin extends BasePlugin {
16
27
  }
17
28
  /** Called by wavesurfer, don't call manually */
18
29
  onInit() {
30
+ var _a;
19
31
  if (!this.wavesurfer) {
20
32
  throw Error('WaveSurfer is not initialized');
21
33
  }
22
- const container = this.options.container ?? this.wavesurfer.getWrapper();
34
+ const container = (_a = this.options.container) !== null && _a !== void 0 ? _a : this.wavesurfer.getWrapper();
23
35
  if (this.options.insertPosition) {
24
36
  ;
25
37
  (container.firstElementChild || container).insertAdjacentElement(this.options.insertPosition, this.timelineWrapper);
@@ -32,7 +44,8 @@ class TimelinePlugin extends BasePlugin {
32
44
  }
33
45
  else {
34
46
  this.subscriptions.push(this.wavesurfer.on('redraw', () => {
35
- this.initTimeline(this.wavesurfer?.getDuration() || 0);
47
+ var _a;
48
+ this.initTimeline(((_a = this.wavesurfer) === null || _a === void 0 ? void 0 : _a.getDuration()) || 0);
36
49
  }));
37
50
  }
38
51
  }
@@ -46,17 +59,6 @@ class TimelinePlugin extends BasePlugin {
46
59
  div.setAttribute('part', 'timeline');
47
60
  return div;
48
61
  }
49
- formatTime(seconds) {
50
- if (seconds / 60 > 1) {
51
- // calculate minutes and seconds from seconds count
52
- const minutes = Math.floor(seconds / 60);
53
- seconds = Math.round(seconds % 60);
54
- const paddedSeconds = `${seconds < 10 ? '0' : ''}${seconds}`;
55
- return `${minutes}:${paddedSeconds}`;
56
- }
57
- const rounded = Math.round(seconds * 1000) / 1000;
58
- return `${rounded}`;
59
- }
60
62
  // Return how many seconds should be between each notch
61
63
  defaultTimeInterval(pxPerSec) {
62
64
  if (pxPerSec >= 25) {
@@ -97,20 +99,19 @@ class TimelinePlugin extends BasePlugin {
97
99
  return 2;
98
100
  }
99
101
  initTimeline(duration) {
102
+ var _a, _b, _c;
100
103
  const pxPerSec = this.timelineWrapper.scrollWidth / duration;
101
- const timeInterval = this.options.timeInterval ?? this.defaultTimeInterval(pxPerSec);
102
- const primaryLabelInterval = this.options.primaryLabelInterval ?? this.defaultPrimaryLabelInterval(pxPerSec);
103
- const secondaryLabelInterval = this.options.secondaryLabelInterval ?? this.defaultSecondaryLabelInterval(pxPerSec);
104
+ const timeInterval = (_a = this.options.timeInterval) !== null && _a !== void 0 ? _a : this.defaultTimeInterval(pxPerSec);
105
+ const primaryLabelInterval = (_b = this.options.primaryLabelInterval) !== null && _b !== void 0 ? _b : this.defaultPrimaryLabelInterval(pxPerSec);
106
+ const secondaryLabelInterval = (_c = this.options.secondaryLabelInterval) !== null && _c !== void 0 ? _c : this.defaultSecondaryLabelInterval(pxPerSec);
104
107
  const isTop = this.options.insertPosition === 'beforebegin';
105
108
  const timeline = document.createElement('div');
106
109
  timeline.setAttribute('style', `
107
110
  height: ${this.options.height}px;
108
111
  overflow: hidden;
109
- display: flex;
110
- justify-content: space-between;
111
- align-items: ${isTop ? 'flex-start' : 'flex-end'};
112
112
  font-size: ${this.options.height / 2}px;
113
113
  white-space: nowrap;
114
+ position: relative;
114
115
  `);
115
116
  if (isTop) {
116
117
  const topStyle = `
@@ -129,15 +130,19 @@ class TimelinePlugin extends BasePlugin {
129
130
  Object.assign(timeline.style, this.options.style);
130
131
  }
131
132
  const notchEl = document.createElement('div');
133
+ notchEl.setAttribute('part', 'timeline-notch');
132
134
  notchEl.setAttribute('style', `
133
- width: 1px;
135
+ width: 0;
134
136
  height: 50%;
135
137
  display: flex;
136
138
  flex-direction: column;
137
139
  justify-content: ${isTop ? 'flex-start' : 'flex-end'};
140
+ ${isTop ? 'top: 0;' : 'bottom: 0;'}
138
141
  overflow: visible;
139
142
  border-left: 1px solid currentColor;
140
143
  opacity: 0.25;
144
+ position: absolute;
145
+ z-index: 1;
141
146
  `);
142
147
  for (let i = 0; i < duration; i += timeInterval) {
143
148
  const notch = notchEl.cloneNode();
@@ -146,10 +151,11 @@ class TimelinePlugin extends BasePlugin {
146
151
  if (isPrimary || isSecondary) {
147
152
  notch.style.height = '100%';
148
153
  notch.style.textIndent = '3px';
149
- notch.textContent = this.formatTime(i);
154
+ notch.textContent = this.options.formatTimeCallback(i);
150
155
  if (isPrimary)
151
156
  notch.style.opacity = '1';
152
157
  }
158
+ notch.style.left = `${i * pxPerSec}px`;
153
159
  timeline.appendChild(notch);
154
160
  }
155
161
  this.timelineWrapper.innerHTML = '';
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):((t="undefined"!=typeof globalThis?globalThis:t||self).WaveSurfer=t.WaveSurfer||{},t.WaveSurfer.Timeline=e())}(this,(function(){"use strict";class t{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),n=this.on(t,(()=>{i(),n()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}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()))}}const i={height:20,formatTimeCallback:t=>{if(t/60>1){return`${Math.floor(t/60)}:${`${(t=Math.round(t%60))<10?"0":""}${t}`}`}return`${Math.round(1e3*t)/1e3}`}};class n extends e{constructor(t){super(t||{}),this.options=Object.assign({},i,t),this.timelineWrapper=this.initTimelineWrapper()}static create(t){return new n(t)}onInit(){var t;if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const e=null!==(t=this.options.container)&&void 0!==t?t:this.wavesurfer.getWrapper();this.options.insertPosition?(e.firstElementChild||e).insertAdjacentElement(this.options.insertPosition,this.timelineWrapper):e.appendChild(this.timelineWrapper),this.options.duration?this.initTimeline(this.options.duration):this.subscriptions.push(this.wavesurfer.on("redraw",(()=>{var t;this.initTimeline((null===(t=this.wavesurfer)||void 0===t?void 0:t.getDuration())||0)})))}destroy(){this.timelineWrapper.remove(),super.destroy()}initTimelineWrapper(){const t=document.createElement("div");return t.setAttribute("part","timeline"),t}defaultTimeInterval(t){return t>=25?1:5*t>=25?5:15*t>=25?15:60*Math.ceil(.5/t)}defaultPrimaryLabelInterval(t){return t>=25?10:5*t>=25?6:4}defaultSecondaryLabelInterval(t){return t>=25?5:2}initTimeline(t){var e,i,n;const s=this.timelineWrapper.scrollWidth/t,r=null!==(e=this.options.timeInterval)&&void 0!==e?e:this.defaultTimeInterval(s),o=null!==(i=this.options.primaryLabelInterval)&&void 0!==i?i:this.defaultPrimaryLabelInterval(s),l=null!==(n=this.options.secondaryLabelInterval)&&void 0!==n?n:this.defaultSecondaryLabelInterval(s),a="beforebegin"===this.options.insertPosition,h=document.createElement("div");if(h.setAttribute("style",`\n height: ${this.options.height}px;\n overflow: hidden;\n font-size: ${this.options.height/2}px;\n white-space: nowrap;\n position: relative;\n `),a){const t="\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n ";h.setAttribute("style",h.getAttribute("style")+t)}"string"==typeof this.options.style?h.setAttribute("style",h.getAttribute("style")+this.options.style):"object"==typeof this.options.style&&Object.assign(h.style,this.options.style);const p=document.createElement("div");p.setAttribute("part","timeline-notch"),p.setAttribute("style",`\n width: 0;\n height: 50%;\n display: flex;\n flex-direction: column;\n justify-content: ${a?"flex-start":"flex-end"};\n ${a?"top: 0;":"bottom: 0;"}\n overflow: visible;\n border-left: 1px solid currentColor;\n opacity: 0.25;\n position: absolute;\n z-index: 1;\n `);for(let e=0;e<t;e+=r){const t=p.cloneNode(),i=Math.round(100*e)/100%o==0,n=Math.round(100*e)/100%l==0;(i||n)&&(t.style.height="100%",t.style.textIndent="3px",t.textContent=this.options.formatTimeCallback(e),i&&(t.style.opacity="1")),t.style.left=e*s+"px",h.appendChild(t)}this.timelineWrapper.innerHTML="",this.timelineWrapper.appendChild(h),this.emit("ready")}}return n}));
@@ -0,0 +1,11 @@
1
+ import EventEmitter from './event-emitter.js';
2
+ type TimerEvents = {
3
+ tick: [];
4
+ };
5
+ declare class Timer extends EventEmitter<TimerEvents> {
6
+ private unsubscribe;
7
+ start(): void;
8
+ stop(): void;
9
+ destroy(): void;
10
+ }
11
+ export default Timer;
@@ -0,0 +1,156 @@
1
+ import type { GenericPlugin } from './base-plugin.js';
2
+ import Player from './player.js';
3
+ export type WaveSurferOptions = {
4
+ /** HTML element or CSS selector */
5
+ container: HTMLElement | string;
6
+ /** The height of the waveform in pixels, or "auto" to fill the container height */
7
+ height?: number | 'auto';
8
+ /** The color of the waveform */
9
+ waveColor?: string | string[] | CanvasGradient;
10
+ /** The color of the progress mask */
11
+ progressColor?: string | string[] | CanvasGradient;
12
+ /** The color of the playpack cursor */
13
+ cursorColor?: string;
14
+ /** The cursor width */
15
+ cursorWidth?: number;
16
+ /** Render the waveform with bars like this: ▁ ▂ ▇ ▃ ▅ ▂ */
17
+ barWidth?: number;
18
+ /** Spacing between bars in pixels */
19
+ barGap?: number;
20
+ /** Rounded borders for bars */
21
+ barRadius?: number;
22
+ /** A vertical scaling factor for the waveform */
23
+ barHeight?: number;
24
+ /** Vertical bar alignment */
25
+ barAlign?: 'top' | 'bottom';
26
+ /** Minimum pixels per second of audio (i.e. zoom level) */
27
+ minPxPerSec?: number;
28
+ /** Stretch the waveform to fill the container, true by default */
29
+ fillParent?: boolean;
30
+ /** Audio URL */
31
+ url?: string;
32
+ /** Pre-computed audio data */
33
+ peaks?: Array<Float32Array | number[]>;
34
+ /** Pre-computed duration */
35
+ duration?: number;
36
+ /** Use an existing media element instead of creating one */
37
+ media?: HTMLMediaElement;
38
+ /** Play the audio on load */
39
+ autoplay?: boolean;
40
+ /** Pass false to disable clicks on the waveform */
41
+ interact?: boolean;
42
+ /** Hide the scrollbar */
43
+ hideScrollbar?: boolean;
44
+ /** Audio rate */
45
+ audioRate?: number;
46
+ /** Automatically scroll the container to keep the current position in viewport */
47
+ autoScroll?: boolean;
48
+ /** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
49
+ autoCenter?: boolean;
50
+ /** Decoding sample rate. Doesn't affect the playback. Defaults to 8000 */
51
+ sampleRate?: number;
52
+ /** Render each audio channel as a separate waveform */
53
+ splitChannels?: WaveSurferOptions[];
54
+ /** Stretch the waveform to the full height */
55
+ normalize?: boolean;
56
+ /** The list of plugins to initialize on start */
57
+ plugins?: GenericPlugin[];
58
+ /** Custom render function */
59
+ renderFunction?: (peaks: Array<Float32Array | number[]>, ctx: CanvasRenderingContext2D) => void;
60
+ /** Options to pass to the fetch method */
61
+ fetchParams?: RequestInit;
62
+ };
63
+ declare const defaultOptions: {
64
+ waveColor: string;
65
+ progressColor: string;
66
+ cursorWidth: number;
67
+ minPxPerSec: number;
68
+ fillParent: boolean;
69
+ interact: boolean;
70
+ autoScroll: boolean;
71
+ autoCenter: boolean;
72
+ sampleRate: number;
73
+ };
74
+ export type WaveSurferEvents = {
75
+ /** When audio starts loading */
76
+ load: [url: string];
77
+ /** When the audio has been decoded */
78
+ decode: [duration: number];
79
+ /** When the audio is both decoded and can play */
80
+ ready: [duration: number];
81
+ /** When a waveform is drawn */
82
+ redraw: [];
83
+ /** When the audio starts playing */
84
+ play: [];
85
+ /** When the audio pauses */
86
+ pause: [];
87
+ /** When the audio finishes playing */
88
+ finish: [];
89
+ /** On audio position change, fires continuously during playback */
90
+ timeupdate: [currentTime: number];
91
+ /** An alias of timeupdate but only when the audio is playing */
92
+ audioprocess: [currentTime: number];
93
+ /** When the user seeks to a new position */
94
+ seeking: [currentTime: number];
95
+ /** When the user interacts with the waveform (i.g. clicks or drags on it) */
96
+ interaction: [newTime: number];
97
+ /** When the user clicks on the waveform */
98
+ click: [relativeX: number];
99
+ /** When the user drags the cursor */
100
+ drag: [relativeX: number];
101
+ /** When the waveform is scrolled (panned) */
102
+ scroll: [visibleStartTime: number, visibleEndTime: number];
103
+ /** When the zoom level changes */
104
+ zoom: [minPxPerSec: number];
105
+ /** Just before the waveform is destroyed so you can clean up your events */
106
+ destroy: [];
107
+ };
108
+ declare class WaveSurfer extends Player<WaveSurferEvents> {
109
+ options: WaveSurferOptions & typeof defaultOptions;
110
+ private renderer;
111
+ private timer;
112
+ private plugins;
113
+ private decodedData;
114
+ private duration;
115
+ protected subscriptions: Array<() => void>;
116
+ /** Create a new WaveSurfer instance */
117
+ static create(options: WaveSurferOptions): WaveSurfer;
118
+ /** Create a new WaveSurfer instance */
119
+ constructor(options: WaveSurferOptions);
120
+ setOptions(options: Partial<WaveSurferOptions>): void;
121
+ private initTimerEvents;
122
+ private initPlayerEvents;
123
+ private initRendererEvents;
124
+ private initPlugins;
125
+ /** Register a wavesurfer.js plugin */
126
+ registerPlugin<T extends GenericPlugin>(plugin: T): T;
127
+ /** For plugins only: get the waveform wrapper div */
128
+ getWrapper(): HTMLElement;
129
+ /** Get the current scroll position in pixels */
130
+ getScroll(): number;
131
+ /** Get all registered plugins */
132
+ getActivePlugins(): GenericPlugin[];
133
+ /** Load an audio file by URL, with optional pre-decoded audio data */
134
+ load(url: string, channelData?: WaveSurferOptions['peaks'], duration?: number): Promise<void>;
135
+ /** Zoom the waveform by a given pixels-per-second factor */
136
+ zoom(minPxPerSec: number): void;
137
+ /** Get the decoded audio data */
138
+ getDecodedData(): AudioBuffer | null;
139
+ /** Get the duration of the audio in seconds */
140
+ getDuration(): number;
141
+ /** Toggle if the waveform should react to clicks */
142
+ toggleInteraction(isInteractive: boolean): void;
143
+ /** Seek to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
144
+ seekTo(progress: number): void;
145
+ /** Play or pause the audio */
146
+ playPause(): Promise<void>;
147
+ /** Stop the audio and go to the beginning */
148
+ stop(): void;
149
+ /** Skip N or -N seconds from the current position */
150
+ skip(seconds: number): void;
151
+ /** Empty the waveform by loading a tiny silent audio */
152
+ empty(): void;
153
+ /** Unmount wavesurfer */
154
+ destroy(): void;
155
+ }
156
+ export default WaveSurfer;
package/dist/renderer.js CHANGED
@@ -88,6 +88,7 @@ class Renderer extends EventEmitter {
88
88
  overflow-y: hidden;
89
89
  width: 100%;
90
90
  position: relative;
91
+ touch-action: none;
91
92
  }
92
93
  :host .noScrollbar {
93
94
  scrollbar-color: transparent;
@@ -160,8 +161,9 @@ class Renderer extends EventEmitter {
160
161
  return this.scrollContainer.scrollLeft;
161
162
  }
162
163
  destroy() {
164
+ var _a;
163
165
  this.container.remove();
164
- this.resizeObserver?.disconnect();
166
+ (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
165
167
  }
166
168
  createDelay(delayMs = 10) {
167
169
  const context = {};
@@ -215,6 +217,7 @@ class Renderer extends EventEmitter {
215
217
  }
216
218
  }
217
219
  const vScale = (halfHeight / max) * barHeight;
220
+ const rectFn = barRadius && 'roundRect' in ctx ? 'roundRect' : 'rect';
218
221
  ctx.beginPath();
219
222
  let prevX = 0;
220
223
  let maxTop = 0;
@@ -231,7 +234,7 @@ class Renderer extends EventEmitter {
231
234
  y = 0;
232
235
  else if (options.barAlign === 'bottom')
233
236
  y = height - barHeight;
234
- ctx.roundRect(prevX * (barWidth + barGap), y, barWidth, barHeight, barRadius);
237
+ ctx[rectFn](prevX * (barWidth + barGap), y, barWidth, barHeight, barRadius);
235
238
  prevX = x;
236
239
  maxTop = 0;
237
240
  maxBottom = 0;
@@ -330,6 +333,10 @@ class Renderer extends EventEmitter {
330
333
  // Clear previous timeouts
331
334
  this.timeouts.forEach((context) => context.timeout && clearTimeout(context.timeout));
332
335
  this.timeouts = [];
336
+ // Clear the canvases
337
+ this.canvasWrapper.innerHTML = '';
338
+ this.progressWrapper.innerHTML = '';
339
+ this.wrapper.style.width = '';
333
340
  // Determine the width of the waveform
334
341
  const pixelRatio = window.devicePixelRatio || 1;
335
342
  const parentWidth = this.scrollContainer.clientWidth;
@@ -337,7 +344,7 @@ class Renderer extends EventEmitter {
337
344
  // Whether the container should scroll
338
345
  this.isScrolling = scrollWidth > parentWidth;
339
346
  const useParentWidth = this.options.fillParent && !this.isScrolling;
340
- // Width and height of the waveform in pixels
347
+ // Width of the waveform in pixels
341
348
  const width = (useParentWidth ? parentWidth : scrollWidth) * pixelRatio;
342
349
  // Set the width of the wrapper
343
350
  this.wrapper.style.width = useParentWidth ? '100%' : `${scrollWidth}px`;
@@ -346,14 +353,11 @@ class Renderer extends EventEmitter {
346
353
  this.scrollContainer.classList.toggle('noScrollbar', !!this.options.hideScrollbar);
347
354
  this.cursor.style.backgroundColor = `${this.options.cursorColor || this.options.progressColor}`;
348
355
  this.cursor.style.width = `${this.options.cursorWidth}px`;
349
- // Clear the canvases
350
- this.canvasWrapper.innerHTML = '';
351
- this.progressWrapper.innerHTML = '';
352
356
  // Render the waveform
353
357
  if (this.options.splitChannels) {
354
358
  // Render a waveform for each channel
355
359
  for (let i = 0; i < audioData.numberOfChannels; i++) {
356
- const options = { ...this.options, ...this.options.splitChannels[i] };
360
+ const options = Object.assign(Object.assign({}, this.options), this.options.splitChannels[i]);
357
361
  this.renderWaveform([audioData.getChannelData(i)], options, width);
358
362
  }
359
363
  }
@@ -0,0 +1 @@
1
+ "use strict";function t(t,e,i,s){return new(i||(i=Promise))((function(n,r){function o(t){try{h(s.next(t))}catch(t){r(t)}}function a(t){try{h(s.throw(t))}catch(t){r(t)}}function h(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(o,a)}h((s=s.apply(t,e||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const e={decode:function(e,i){return t(this,void 0,void 0,(function*(){const t=new AudioContext({sampleRate:i}),s=t.decodeAudioData(e);return s.finally((()=>t.close())),s}))},createBuffer:function(t,e){return"number"==typeof t[0]&&(t=[t]),function(t){const e=t[0];if(e.some((t=>t>1||t<-1))){const i=e.length;let s=0;for(let t=0;t<i;t++){const i=Math.abs(e[t]);i>s&&(s=i)}for(const e of t)for(let t=0;t<i;t++)e[t]/=s}}(t),{duration:e,length:t[0].length,sampleRate:t[0].length/e,numberOfChannels:t.length,getChannelData:e=>null==t?void 0:t[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};const i={fetchBlob:function(e,i){return t(this,void 0,void 0,(function*(){return fetch(e,i).then((t=>t.blob()))}))}};class s{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),s=this.on(t,(()=>{i(),s()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class n extends s{constructor(t){super(),t.media?this.media=t.media:this.media=document.createElement("audio"),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}onceMediaEvent(t,e){return this.onMediaEvent(t,e,{once:!0})}getSrc(){return this.media.currentSrc||this.media.src||""}revokeSrc(){const t=this.getSrc();t.startsWith("blob:")&&URL.revokeObjectURL(t)}setSrc(t,e){if(this.getSrc()===t)return;this.revokeSrc();const i=e instanceof Blob?URL.createObjectURL(e):t;this.media.src=i,this.media.load()}destroy(){this.media.pause(),this.revokeSrc(),this.media.src="",this.media.load()}play(){return this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}setTime(t){this.media.currentTime=t}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}setSinkId(t){return this.media.setSinkId(t)}}class r extends s{constructor(t){let e;if(super(),this.timeouts=[],this.isScrolling=!1,this.audioData=null,this.resizeObserver=null,this.isDragging=!1,this.options=t,"string"==typeof t.container?e=document.querySelector(t.container):t.container instanceof HTMLElement&&(e=t.container),!e)throw new Error("Container not found");this.parent=e;const[i,s]=this.initHtml();e.appendChild(i),this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),this.cursor=s.querySelector(".cursor"),this.initEvents()}initEvents(){this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",i)})),this.initDrag(),this.scrollContainer.addEventListener("scroll",(()=>{const{scrollLeft:t,scrollWidth:e,clientWidth:i}=this.scrollContainer,s=t/e,n=(t+i)/e;this.emit("scroll",s,n)}));const t=this.createDelay(100);this.resizeObserver=new ResizeObserver((()=>{t((()=>this.reRender()))})),this.resizeObserver.observe(this.scrollContainer)}initDrag(){!function(t,e,i,s,n=5){let r=()=>{};if(!t)return r;const o=o=>{if(2===o.button)return;o.preventDefault(),o.stopPropagation();let a=o.clientX,h=o.clientY,l=!1;const c=s=>{s.preventDefault(),s.stopPropagation();const r=s.clientX,o=s.clientY;if(l||Math.abs(r-a)>=n||Math.abs(o-h)>=n){const{left:s,top:n}=t.getBoundingClientRect();l||(l=!0,null==i||i(a-s,h-n)),e(r-a,o-h,r-s,o-n),a=r,h=o}},d=t=>{l&&(t.preventDefault(),t.stopPropagation())},u=()=>{l&&(null==s||s()),r()};document.addEventListener("pointermove",c),document.addEventListener("pointerup",u),document.addEventListener("pointerleave",u),document.addEventListener("click",d,!0),r=()=>{document.removeEventListener("pointermove",c),document.removeEventListener("pointerup",u),document.removeEventListener("pointerleave",u),setTimeout((()=>{document.removeEventListener("click",d,!0)}),10)}};t.addEventListener("pointerdown",o)}(this.wrapper,((t,e,i)=>{this.emit("drag",Math.max(0,Math.min(1,i/this.wrapper.clientWidth)))}),(()=>this.isDragging=!0),(()=>this.isDragging=!1))}getHeight(){return null==this.options.height?128:isNaN(Number(this.options.height))?"auto"===this.options.height&&this.parent.clientHeight||128:Number(this.options.height)}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"});return e.innerHTML=`\n <style>\n :host {\n user-select: none;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n touch-action: none;\n }\n :host .noScrollbar {\n scrollbar-color: transparent;\n scrollbar-width: none;\n }\n :host .noScrollbar::-webkit-scrollbar {\n display: none;\n -webkit-appearance: none;\n }\n :host .wrapper {\n position: relative;\n overflow: visible;\n z-index: 2;\n }\n :host .canvases {\n min-height: ${this.getHeight()}px;\n }\n :host .canvases > div {\n position: relative;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n overflow: hidden;\n }\n :host .progress > div {\n position: relative;\n }\n :host .cursor {\n pointer-events: none;\n position: absolute;\n z-index: 5;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n }\n </style>\n\n <div class="scroll" part="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress" part="progress"></div>\n <div class="cursor" part="cursor"></div>\n </div>\n </div>\n `,[t,e]}setOptions(t){this.options=t,this.reRender()}getWrapper(){return this.wrapper}getScroll(){return this.scrollContainer.scrollLeft}destroy(){var t;this.container.remove(),null===(t=this.resizeObserver)||void 0===t||t.disconnect()}createDelay(t=10){const e={};return this.timeouts.push(e),i=>{e.timeout&&clearTimeout(e.timeout),e.timeout=setTimeout(i,t)}}convertColorValues(t){if(!Array.isArray(t))return t||"";if(t.length<2)return t[0]||"";const e=document.createElement("canvas"),i=e.getContext("2d").createLinearGradient(0,0,0,e.height),s=1/(t.length-1);return t.forEach(((t,e)=>{const n=e*s;i.addColorStop(n,t)})),i}renderBars(t,e,i){if(i.fillStyle=this.convertColorValues(e.waveColor),e.renderFunction)return void e.renderFunction(t,i);const s=t[0],n=t[1]||t[0],r=s.length,o=window.devicePixelRatio||1,{width:a,height:h}=i.canvas,l=h/2,c=e.barHeight||1,d=e.barWidth?e.barWidth*o:1,u=e.barGap?e.barGap*o:e.barWidth?d/2:0,p=e.barRadius||0,m=a/(d+u)/r;let g=1;if(e.normalize){g=0;for(let t=0;t<r;t++){const e=Math.abs(s[t]);e>g&&(g=e)}}const v=l/g*c,f=p&&"roundRect"in i?"roundRect":"rect";i.beginPath();let b=0,y=0,C=0;for(let t=0;t<=r;t++){const r=Math.round(t*m);if(r>b){const t=Math.round(y*v),s=t+Math.round(C*v)||1;let n=l-t;"top"===e.barAlign?n=0:"bottom"===e.barAlign&&(n=h-s),i[f](b*(d+u),n,d,s,p),b=r,y=0,C=0}const o=Math.abs(s[t]||0),a=Math.abs(n[t]||0);o>y&&(y=o),a>C&&(C=a)}i.fill(),i.closePath()}renderSingleCanvas(t,e,i,s,n,r,o,a){const h=window.devicePixelRatio||1,l=document.createElement("canvas"),c=t[0].length;l.width=Math.round(i*(r-n)/c),l.height=s*h,l.style.width=`${Math.floor(l.width/h)}px`,l.style.height=`${s}px`,l.style.left=`${Math.floor(n*i/h/c)}px`,o.appendChild(l);const d=l.getContext("2d");this.renderBars(t.map((t=>t.slice(n,r))),e,d);const u=l.cloneNode();a.appendChild(u);const p=u.getContext("2d");l.width>0&&l.height>0&&p.drawImage(l,0,0),p.globalCompositeOperation="source-in",p.fillStyle=this.convertColorValues(e.progressColor),p.fillRect(0,0,l.width,l.height)}renderWaveform(t,e,i){const s=document.createElement("div"),n=this.getHeight();s.style.height=`${n}px`,this.canvasWrapper.style.minHeight=`${n}px`,this.canvasWrapper.appendChild(s);const o=s.cloneNode();this.progressWrapper.appendChild(o);const{scrollLeft:a,scrollWidth:h,clientWidth:l}=this.scrollContainer,c=t[0].length,d=c/h;let u=Math.min(r.MAX_CANVAS_WIDTH,l);if(e.barWidth||e.barGap){const t=e.barWidth||.5,i=t+(e.barGap||t/2);u%i!=0&&(u=Math.floor(u/i)*i)}const p=Math.floor(Math.abs(a)*d),m=Math.floor(p+u*d),g=m-p,v=(r,a)=>{this.renderSingleCanvas(t,e,i,n,Math.max(0,r),Math.min(a,c),s,o)},f=this.createDelay(),b=this.createDelay(),y=(t,e)=>{v(t,e),t>0&&f((()=>{y(t-g,e-g)}))},C=(t,e)=>{v(t,e),e<c&&b((()=>{C(t+g,e+g)}))};y(p,m),m<c&&C(m,m+g)}render(t){this.timeouts.forEach((t=>t.timeout&&clearTimeout(t.timeout))),this.timeouts=[],this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="",this.wrapper.style.width="";const e=window.devicePixelRatio||1,i=this.scrollContainer.clientWidth,s=Math.ceil(t.duration*(this.options.minPxPerSec||0));this.isScrolling=s>i;const n=this.options.fillParent&&!this.isScrolling,r=(n?i:s)*e;if(this.wrapper.style.width=n?"100%":`${s}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.cursor.style.backgroundColor=`${this.options.cursorColor||this.options.progressColor}`,this.cursor.style.width=`${this.options.cursorWidth}px`,this.options.splitChannels)for(let e=0;e<t.numberOfChannels;e++){const i=Object.assign(Object.assign({},this.options),this.options.splitChannels[e]);this.renderWaveform([t.getChannelData(e)],i,r)}else{const e=[t.getChannelData(0)];t.numberOfChannels>1&&e.push(t.getChannelData(1)),this.renderWaveform(e,this.options,r)}this.audioData=t,this.emit("render")}reRender(){if(!this.audioData)return;const t=this.progressWrapper.clientWidth;this.render(this.audioData);const e=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=e-t}zoom(t){this.options.minPxPerSec=t,this.reRender()}scrollIntoView(t,e=!1){const{clientWidth:i,scrollLeft:s,scrollWidth:n}=this.scrollContainer,r=n*t,o=i/2;if(r>s+(e&&this.options.autoCenter&&!this.isDragging?o:i)||r<s)if(this.options.autoCenter&&!this.isDragging){const t=o/20;r-(s+o)>=t&&r<s+i?this.scrollContainer.scrollLeft+=t:this.scrollContainer.scrollLeft=r-o}else if(this.isDragging){const t=10;this.scrollContainer.scrollLeft=r<s?r-t:r-i+t}else this.scrollContainer.scrollLeft=r;{const{scrollLeft:t}=this.scrollContainer,e=t/n,s=(t+i)/n;this.emit("scroll",e,s)}}renderProgress(t,e){isNaN(t)||(this.progressWrapper.style.width=100*t+"%",this.cursor.style.left=100*t+"%",this.cursor.style.marginLeft=100===Math.round(100*t)?`-${this.options.cursorWidth}px`:"",this.isScrolling&&this.options.autoScroll&&this.scrollIntoView(t,e))}}r.MAX_CANVAS_WIDTH=4e3;class o extends s{constructor(){super(...arguments),this.unsubscribe=()=>{}}start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}}const a={waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class h extends n{static create(t){return new h(t)}constructor(t){var e,i;super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.duration=null,this.subscriptions=[],this.options=Object.assign({},a,t),this.timer=new o,this.renderer=new r(this.options),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initPlugins();const s=this.options.url||(null===(e=this.options.media)||void 0===e?void 0:e.currentSrc)||(null===(i=this.options.media)||void 0===i?void 0:i.src);s&&this.load(s,this.options.peaks,this.options.duration)}setOptions(t){this.options=Object.assign({},this.options,t),this.renderer.setOptions(this.options),t.audioRate&&this.setPlaybackRate(t.audioRate)}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",t),this.emit("audioprocess",t)})))}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",t)})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.onMediaEvent("ended",(()=>{this.emit("finish")})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(t=>{this.options.interact&&(this.seekTo(t),this.emit("interaction",this.getCurrentTime()),this.emit("click",t))})),this.renderer.on("scroll",((t,e)=>{const i=this.getDuration();this.emit("scroll",t*i,e*i)})),this.renderer.on("render",(()=>{this.emit("redraw")})));{let t;this.subscriptions.push(this.renderer.on("drag",(e=>{this.options.interact&&(this.renderer.renderProgress(e),clearTimeout(t),t=setTimeout((()=>{this.seekTo(e)}),this.isPlaying()?0:200),this.emit("interaction",e*this.getDuration()),this.emit("drag",e))})))}}initPlugins(){var t;(null===(t=this.options.plugins)||void 0===t?void 0:t.length)&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init(this),this.plugins.push(t),this.subscriptions.push(t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t))}))),t}getWrapper(){return this.renderer.getWrapper()}getScroll(){return this.renderer.getScroll()}getActivePlugins(){return this.plugins}load(s,n,r){return t(this,void 0,void 0,(function*(){this.isPlaying()&&this.pause(),this.decodedData=null,this.duration=null,this.emit("load",s);const t=n?void 0:yield i.fetchBlob(s,this.options.fetchParams);if(this.setSrc(s,t),this.duration=r||this.getDuration()||(yield new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getDuration())))})))||0,n)this.decodedData=e.createBuffer(n,this.duration);else if(t){const i=yield t.arrayBuffer();this.decodedData=yield e.decode(i,this.options.sampleRate),0!==this.duration&&this.duration!==1/0||(this.duration=this.decodedData.duration)}this.emit("decode",this.duration),this.decodedData&&this.renderer.render(this.decodedData),this.emit("ready",this.duration)}))}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}getDuration(){return null!==this.duration?this.duration:super.getDuration()}toggleInteraction(t){this.options.interact=t}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}playPause(){return t(this,void 0,void 0,(function*(){return this.isPlaying()?this.pause():this.play()}))}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}destroy(){this.emit("destroy"),this.plugins.forEach((t=>t.destroy())),this.subscriptions.forEach((t=>t())),this.timer.destroy(),this.renderer.destroy(),super.destroy()}}module.exports=h;
@@ -57,6 +57,8 @@ export type WaveSurferOptions = {
57
57
  plugins?: GenericPlugin[];
58
58
  /** Custom render function */
59
59
  renderFunction?: (peaks: Array<Float32Array | number[]>, ctx: CanvasRenderingContext2D) => void;
60
+ /** Options to pass to the fetch method */
61
+ fetchParams?: RequestInit;
60
62
  };
61
63
  declare const defaultOptions: {
62
64
  waveColor: string;
@@ -130,7 +132,7 @@ declare class WaveSurfer extends Player<WaveSurferEvents> {
130
132
  getActivePlugins(): GenericPlugin[];
131
133
  /** Load an audio file by URL, with optional pre-decoded audio data */
132
134
  load(url: string, channelData?: WaveSurferOptions['peaks'], duration?: number): Promise<void>;
133
- /** Zoom in or out */
135
+ /** Zoom the waveform by a given pixels-per-second factor */
134
136
  zoom(minPxPerSec: number): void;
135
137
  /** Get the decoded audio data */
136
138
  getDecodedData(): AudioBuffer | null;
@@ -138,13 +140,13 @@ declare class WaveSurfer extends Player<WaveSurferEvents> {
138
140
  getDuration(): number;
139
141
  /** Toggle if the waveform should react to clicks */
140
142
  toggleInteraction(isInteractive: boolean): void;
141
- /** Seeks to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
143
+ /** Seek to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
142
144
  seekTo(progress: number): void;
143
145
  /** Play or pause the audio */
144
146
  playPause(): Promise<void>;
145
147
  /** Stop the audio and go to the beginning */
146
148
  stop(): void;
147
- /** Skip N or -N seconds from the current positions */
149
+ /** Skip N or -N seconds from the current position */
148
150
  skip(seconds: number): void;
149
151
  /** Empty the waveform by loading a tiny silent audio */
150
152
  empty(): void;
@@ -0,0 +1 @@
1
+ function t(t,e,i,s){return new(i||(i=Promise))((function(n,r){function o(t){try{h(s.next(t))}catch(t){r(t)}}function a(t){try{h(s.throw(t))}catch(t){r(t)}}function h(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(o,a)}h((s=s.apply(t,e||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const e={decode:function(e,i){return t(this,void 0,void 0,(function*(){const t=new AudioContext({sampleRate:i}),s=t.decodeAudioData(e);return s.finally((()=>t.close())),s}))},createBuffer:function(t,e){return"number"==typeof t[0]&&(t=[t]),function(t){const e=t[0];if(e.some((t=>t>1||t<-1))){const i=e.length;let s=0;for(let t=0;t<i;t++){const i=Math.abs(e[t]);i>s&&(s=i)}for(const e of t)for(let t=0;t<i;t++)e[t]/=s}}(t),{duration:e,length:t[0].length,sampleRate:t[0].length/e,numberOfChannels:t.length,getChannelData:e=>null==t?void 0:t[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};const i={fetchBlob:function(e,i){return t(this,void 0,void 0,(function*(){return fetch(e,i).then((t=>t.blob()))}))}};class s{constructor(){this.listeners={}}on(t,e){return this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),()=>this.un(t,e)}once(t,e){const i=this.on(t,e),s=this.on(t,(()=>{i(),s()}));return i}un(t,e){this.listeners[t]&&(e?this.listeners[t].delete(e):delete this.listeners[t])}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}class n extends s{constructor(t){super(),t.media?this.media=t.media:this.media=document.createElement("audio"),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e)}onceMediaEvent(t,e){return this.onMediaEvent(t,e,{once:!0})}getSrc(){return this.media.currentSrc||this.media.src||""}revokeSrc(){const t=this.getSrc();t.startsWith("blob:")&&URL.revokeObjectURL(t)}setSrc(t,e){if(this.getSrc()===t)return;this.revokeSrc();const i=e instanceof Blob?URL.createObjectURL(e):t;this.media.src=i,this.media.load()}destroy(){this.media.pause(),this.revokeSrc(),this.media.src="",this.media.load()}play(){return this.media.play()}pause(){this.media.pause()}isPlaying(){return this.media.currentTime>0&&!this.media.paused&&!this.media.ended}setTime(t){this.media.currentTime=t}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}setSinkId(t){return this.media.setSinkId(t)}}class r extends s{constructor(t){let e;if(super(),this.timeouts=[],this.isScrolling=!1,this.audioData=null,this.resizeObserver=null,this.isDragging=!1,this.options=t,"string"==typeof t.container?e=document.querySelector(t.container):t.container instanceof HTMLElement&&(e=t.container),!e)throw new Error("Container not found");this.parent=e;const[i,s]=this.initHtml();e.appendChild(i),this.container=i,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),this.cursor=s.querySelector(".cursor"),this.initEvents()}initEvents(){this.wrapper.addEventListener("click",(t=>{const e=this.wrapper.getBoundingClientRect(),i=(t.clientX-e.left)/e.width;this.emit("click",i)})),this.initDrag(),this.scrollContainer.addEventListener("scroll",(()=>{const{scrollLeft:t,scrollWidth:e,clientWidth:i}=this.scrollContainer,s=t/e,n=(t+i)/e;this.emit("scroll",s,n)}));const t=this.createDelay(100);this.resizeObserver=new ResizeObserver((()=>{t((()=>this.reRender()))})),this.resizeObserver.observe(this.scrollContainer)}initDrag(){!function(t,e,i,s,n=5){let r=()=>{};if(!t)return r;const o=o=>{if(2===o.button)return;o.preventDefault(),o.stopPropagation();let a=o.clientX,h=o.clientY,l=!1;const c=s=>{s.preventDefault(),s.stopPropagation();const r=s.clientX,o=s.clientY;if(l||Math.abs(r-a)>=n||Math.abs(o-h)>=n){const{left:s,top:n}=t.getBoundingClientRect();l||(l=!0,null==i||i(a-s,h-n)),e(r-a,o-h,r-s,o-n),a=r,h=o}},d=t=>{l&&(t.preventDefault(),t.stopPropagation())},u=()=>{l&&(null==s||s()),r()};document.addEventListener("pointermove",c),document.addEventListener("pointerup",u),document.addEventListener("pointerleave",u),document.addEventListener("click",d,!0),r=()=>{document.removeEventListener("pointermove",c),document.removeEventListener("pointerup",u),document.removeEventListener("pointerleave",u),setTimeout((()=>{document.removeEventListener("click",d,!0)}),10)}};t.addEventListener("pointerdown",o)}(this.wrapper,((t,e,i)=>{this.emit("drag",Math.max(0,Math.min(1,i/this.wrapper.clientWidth)))}),(()=>this.isDragging=!0),(()=>this.isDragging=!1))}getHeight(){return null==this.options.height?128:isNaN(Number(this.options.height))?"auto"===this.options.height&&this.parent.clientHeight||128:Number(this.options.height)}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"});return e.innerHTML=`\n <style>\n :host {\n user-select: none;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n touch-action: none;\n }\n :host .noScrollbar {\n scrollbar-color: transparent;\n scrollbar-width: none;\n }\n :host .noScrollbar::-webkit-scrollbar {\n display: none;\n -webkit-appearance: none;\n }\n :host .wrapper {\n position: relative;\n overflow: visible;\n z-index: 2;\n }\n :host .canvases {\n min-height: ${this.getHeight()}px;\n }\n :host .canvases > div {\n position: relative;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n overflow: hidden;\n }\n :host .progress > div {\n position: relative;\n }\n :host .cursor {\n pointer-events: none;\n position: absolute;\n z-index: 5;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n }\n </style>\n\n <div class="scroll" part="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress" part="progress"></div>\n <div class="cursor" part="cursor"></div>\n </div>\n </div>\n `,[t,e]}setOptions(t){this.options=t,this.reRender()}getWrapper(){return this.wrapper}getScroll(){return this.scrollContainer.scrollLeft}destroy(){var t;this.container.remove(),null===(t=this.resizeObserver)||void 0===t||t.disconnect()}createDelay(t=10){const e={};return this.timeouts.push(e),i=>{e.timeout&&clearTimeout(e.timeout),e.timeout=setTimeout(i,t)}}convertColorValues(t){if(!Array.isArray(t))return t||"";if(t.length<2)return t[0]||"";const e=document.createElement("canvas"),i=e.getContext("2d").createLinearGradient(0,0,0,e.height),s=1/(t.length-1);return t.forEach(((t,e)=>{const n=e*s;i.addColorStop(n,t)})),i}renderBars(t,e,i){if(i.fillStyle=this.convertColorValues(e.waveColor),e.renderFunction)return void e.renderFunction(t,i);const s=t[0],n=t[1]||t[0],r=s.length,o=window.devicePixelRatio||1,{width:a,height:h}=i.canvas,l=h/2,c=e.barHeight||1,d=e.barWidth?e.barWidth*o:1,u=e.barGap?e.barGap*o:e.barWidth?d/2:0,p=e.barRadius||0,m=a/(d+u)/r;let g=1;if(e.normalize){g=0;for(let t=0;t<r;t++){const e=Math.abs(s[t]);e>g&&(g=e)}}const v=l/g*c,f=p&&"roundRect"in i?"roundRect":"rect";i.beginPath();let b=0,y=0,C=0;for(let t=0;t<=r;t++){const r=Math.round(t*m);if(r>b){const t=Math.round(y*v),s=t+Math.round(C*v)||1;let n=l-t;"top"===e.barAlign?n=0:"bottom"===e.barAlign&&(n=h-s),i[f](b*(d+u),n,d,s,p),b=r,y=0,C=0}const o=Math.abs(s[t]||0),a=Math.abs(n[t]||0);o>y&&(y=o),a>C&&(C=a)}i.fill(),i.closePath()}renderSingleCanvas(t,e,i,s,n,r,o,a){const h=window.devicePixelRatio||1,l=document.createElement("canvas"),c=t[0].length;l.width=Math.round(i*(r-n)/c),l.height=s*h,l.style.width=`${Math.floor(l.width/h)}px`,l.style.height=`${s}px`,l.style.left=`${Math.floor(n*i/h/c)}px`,o.appendChild(l);const d=l.getContext("2d");this.renderBars(t.map((t=>t.slice(n,r))),e,d);const u=l.cloneNode();a.appendChild(u);const p=u.getContext("2d");l.width>0&&l.height>0&&p.drawImage(l,0,0),p.globalCompositeOperation="source-in",p.fillStyle=this.convertColorValues(e.progressColor),p.fillRect(0,0,l.width,l.height)}renderWaveform(t,e,i){const s=document.createElement("div"),n=this.getHeight();s.style.height=`${n}px`,this.canvasWrapper.style.minHeight=`${n}px`,this.canvasWrapper.appendChild(s);const o=s.cloneNode();this.progressWrapper.appendChild(o);const{scrollLeft:a,scrollWidth:h,clientWidth:l}=this.scrollContainer,c=t[0].length,d=c/h;let u=Math.min(r.MAX_CANVAS_WIDTH,l);if(e.barWidth||e.barGap){const t=e.barWidth||.5,i=t+(e.barGap||t/2);u%i!=0&&(u=Math.floor(u/i)*i)}const p=Math.floor(Math.abs(a)*d),m=Math.floor(p+u*d),g=m-p,v=(r,a)=>{this.renderSingleCanvas(t,e,i,n,Math.max(0,r),Math.min(a,c),s,o)},f=this.createDelay(),b=this.createDelay(),y=(t,e)=>{v(t,e),t>0&&f((()=>{y(t-g,e-g)}))},C=(t,e)=>{v(t,e),e<c&&b((()=>{C(t+g,e+g)}))};y(p,m),m<c&&C(m,m+g)}render(t){this.timeouts.forEach((t=>t.timeout&&clearTimeout(t.timeout))),this.timeouts=[],this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="",this.wrapper.style.width="";const e=window.devicePixelRatio||1,i=this.scrollContainer.clientWidth,s=Math.ceil(t.duration*(this.options.minPxPerSec||0));this.isScrolling=s>i;const n=this.options.fillParent&&!this.isScrolling,r=(n?i:s)*e;if(this.wrapper.style.width=n?"100%":`${s}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.cursor.style.backgroundColor=`${this.options.cursorColor||this.options.progressColor}`,this.cursor.style.width=`${this.options.cursorWidth}px`,this.options.splitChannels)for(let e=0;e<t.numberOfChannels;e++){const i=Object.assign(Object.assign({},this.options),this.options.splitChannels[e]);this.renderWaveform([t.getChannelData(e)],i,r)}else{const e=[t.getChannelData(0)];t.numberOfChannels>1&&e.push(t.getChannelData(1)),this.renderWaveform(e,this.options,r)}this.audioData=t,this.emit("render")}reRender(){if(!this.audioData)return;const t=this.progressWrapper.clientWidth;this.render(this.audioData);const e=this.progressWrapper.clientWidth;this.scrollContainer.scrollLeft+=e-t}zoom(t){this.options.minPxPerSec=t,this.reRender()}scrollIntoView(t,e=!1){const{clientWidth:i,scrollLeft:s,scrollWidth:n}=this.scrollContainer,r=n*t,o=i/2;if(r>s+(e&&this.options.autoCenter&&!this.isDragging?o:i)||r<s)if(this.options.autoCenter&&!this.isDragging){const t=o/20;r-(s+o)>=t&&r<s+i?this.scrollContainer.scrollLeft+=t:this.scrollContainer.scrollLeft=r-o}else if(this.isDragging){const t=10;this.scrollContainer.scrollLeft=r<s?r-t:r-i+t}else this.scrollContainer.scrollLeft=r;{const{scrollLeft:t}=this.scrollContainer,e=t/n,s=(t+i)/n;this.emit("scroll",e,s)}}renderProgress(t,e){isNaN(t)||(this.progressWrapper.style.width=100*t+"%",this.cursor.style.left=100*t+"%",this.cursor.style.marginLeft=100===Math.round(100*t)?`-${this.options.cursorWidth}px`:"",this.isScrolling&&this.options.autoScroll&&this.scrollIntoView(t,e))}}r.MAX_CANVAS_WIDTH=4e3;class o extends s{constructor(){super(...arguments),this.unsubscribe=()=>{}}start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}}const a={waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class h extends n{static create(t){return new h(t)}constructor(t){var e,i;super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.duration=null,this.subscriptions=[],this.options=Object.assign({},a,t),this.timer=new o,this.renderer=new r(this.options),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initPlugins();const s=this.options.url||(null===(e=this.options.media)||void 0===e?void 0:e.currentSrc)||(null===(i=this.options.media)||void 0===i?void 0:i.src);s&&this.load(s,this.options.peaks,this.options.duration)}setOptions(t){this.options=Object.assign({},this.options,t),this.renderer.setOptions(this.options),t.audioRate&&this.setPlaybackRate(t.audioRate)}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),!0),this.emit("timeupdate",t),this.emit("audioprocess",t)})))}initPlayerEvents(){this.subscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.getCurrentTime();this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),this.emit("timeupdate",t)})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop()})),this.onMediaEvent("ended",(()=>{this.emit("finish")})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(t=>{this.options.interact&&(this.seekTo(t),this.emit("interaction",this.getCurrentTime()),this.emit("click",t))})),this.renderer.on("scroll",((t,e)=>{const i=this.getDuration();this.emit("scroll",t*i,e*i)})),this.renderer.on("render",(()=>{this.emit("redraw")})));{let t;this.subscriptions.push(this.renderer.on("drag",(e=>{this.options.interact&&(this.renderer.renderProgress(e),clearTimeout(t),t=setTimeout((()=>{this.seekTo(e)}),this.isPlaying()?0:200),this.emit("interaction",e*this.getDuration()),this.emit("drag",e))})))}}initPlugins(){var t;(null===(t=this.options.plugins)||void 0===t?void 0:t.length)&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init(this),this.plugins.push(t),this.subscriptions.push(t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t))}))),t}getWrapper(){return this.renderer.getWrapper()}getScroll(){return this.renderer.getScroll()}getActivePlugins(){return this.plugins}load(s,n,r){return t(this,void 0,void 0,(function*(){this.isPlaying()&&this.pause(),this.decodedData=null,this.duration=null,this.emit("load",s);const t=n?void 0:yield i.fetchBlob(s,this.options.fetchParams);if(this.setSrc(s,t),this.duration=r||this.getDuration()||(yield new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getDuration())))})))||0,n)this.decodedData=e.createBuffer(n,this.duration);else if(t){const i=yield t.arrayBuffer();this.decodedData=yield e.decode(i,this.options.sampleRate),0!==this.duration&&this.duration!==1/0||(this.duration=this.decodedData.duration)}this.emit("decode",this.duration),this.decodedData&&this.renderer.render(this.decodedData),this.emit("ready",this.duration)}))}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}getDuration(){return null!==this.duration?this.duration:super.getDuration()}toggleInteraction(t){this.options.interact=t}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}playPause(){return t(this,void 0,void 0,(function*(){return this.isPlaying()?this.pause():this.play()}))}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}destroy(){this.emit("destroy"),this.plugins.forEach((t=>t.destroy())),this.subscriptions.forEach((t=>t())),this.timer.destroy(),this.renderer.destroy(),super.destroy()}}export{h as default};