wavesurfer.js 7.0.0-alpha.47 → 7.0.0-alpha.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/renderer.js CHANGED
@@ -28,6 +28,7 @@ class Renderer extends EventEmitter {
28
28
  this.wrapper = shadow.querySelector('.wrapper');
29
29
  this.canvasWrapper = shadow.querySelector('.canvases');
30
30
  this.progressWrapper = shadow.querySelector('.progress');
31
+ this.cursor = shadow.querySelector('.cursor');
31
32
  this.initEvents();
32
33
  }
33
34
  initEvents() {
@@ -38,6 +39,29 @@ class Renderer extends EventEmitter {
38
39
  const relativeX = x / rect.width;
39
40
  this.emit('click', relativeX);
40
41
  });
42
+ // Drag
43
+ this.wrapper.addEventListener('mousedown', (e) => {
44
+ let x = e.clientX;
45
+ let firstMove = true;
46
+ const move = (e) => {
47
+ const dx = e.clientX - x;
48
+ x = e.clientX;
49
+ if (dx !== 0) {
50
+ const rect = this.wrapper.getBoundingClientRect();
51
+ if (firstMove) {
52
+ firstMove = false;
53
+ this.emit('click', (x - rect.left) / rect.width);
54
+ }
55
+ this.emit('drag', dx / rect.width);
56
+ }
57
+ };
58
+ const up = () => {
59
+ document.removeEventListener('mousemove', move);
60
+ document.removeEventListener('mouseup', up);
61
+ };
62
+ document.addEventListener('mousemove', move);
63
+ document.addEventListener('mouseup', up);
64
+ });
41
65
  // Add a scroll listener
42
66
  this.scrollContainer.addEventListener('scroll', () => {
43
67
  const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
@@ -98,14 +122,24 @@ class Renderer extends EventEmitter {
98
122
  width: 0;
99
123
  height: 100%;
100
124
  overflow: hidden;
101
- box-sizing: border-box;
125
+ }
126
+ :host .cursor {
127
+ pointer-events: none;
128
+ position: absolute;
129
+ z-index: 2;
130
+ top: 0;
131
+ left: 0;
132
+ height: 100%;
133
+ width: ${this.options.cursorWidth}px;
134
+ background-color: ${this.options.cursorColor || this.options.progressColor};
102
135
  }
103
136
  </style>
104
137
 
105
- <div class="scroll">
138
+ <div class="scroll" part="scroll">
106
139
  <div class="wrapper">
107
140
  <div class="canvases"></div>
108
141
  <div class="progress"></div>
142
+ <div class="cursor" part="cursor"></div>
109
143
  </div>
110
144
  </div>
111
145
  `;
@@ -247,14 +281,14 @@ class Renderer extends EventEmitter {
247
281
  // Set additional styles
248
282
  this.scrollContainer.style.overflowX = this.isScrolling ? 'auto' : 'hidden';
249
283
  this.scrollContainer.classList.toggle('noScrollbar', !!this.options.hideScrollbar);
250
- this.progressWrapper.style.borderRightStyle = 'solid';
251
- this.progressWrapper.style.borderRightColor = `${this.options.cursorColor || this.options.progressColor}`;
252
- this.progressWrapper.style.borderRightWidth = `${this.options.cursorWidth}px`;
284
+ this.cursor.style.backgroundColor = `${this.options.cursorColor || this.options.progressColor}`;
285
+ this.cursor.style.width = `${this.options.cursorWidth}px`;
253
286
  this.canvasWrapper.style.height = `${this.options.height}px`;
254
287
  // Render the waveform
255
288
  this.renderPeaks(channelData, width, height, pixelRatio);
256
289
  this.channelData = channelData;
257
290
  this.duration = duration;
291
+ this.emit('render');
258
292
  }
259
293
  reRender() {
260
294
  // Return if the waveform has not been rendered yet
@@ -307,6 +341,7 @@ class Renderer extends EventEmitter {
307
341
  if (isNaN(progress))
308
342
  return;
309
343
  this.progressWrapper.style.width = `${progress * 100}%`;
344
+ this.cursor.style.left = `${progress * 100}%`;
310
345
  if (this.isScrolling && this.options.autoScroll) {
311
346
  this.scrollIntoView(progress, isPlaying);
312
347
  }
@@ -46,7 +46,7 @@ export type WaveSurferOptions = {
46
46
  autoScroll?: boolean;
47
47
  /** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
48
48
  autoCenter?: boolean;
49
- /** The sample rate used to decode audio, defaults to 8000 */
49
+ /** Decoding sample rate. Doesn't affect the playback. Defaults to 8000 */
50
50
  sampleRate?: number;
51
51
  /** The list of plugins to initialize on start */
52
52
  plugins?: GenericPlugin[];
@@ -61,6 +61,7 @@ declare const defaultOptions: {
61
61
  interact: boolean;
62
62
  autoScroll: boolean;
63
63
  autoCenter: boolean;
64
+ sampleRate: number;
64
65
  };
65
66
  export type WaveSurferEvents = {
66
67
  /** When audio starts loading */
@@ -96,7 +97,6 @@ export type WaveSurferEvents = {
96
97
  };
97
98
  declare class WaveSurfer extends Player<WaveSurferEvents> {
98
99
  options: WaveSurferOptions & typeof defaultOptions;
99
- private fetcher;
100
100
  private renderer;
101
101
  private timer;
102
102
  private plugins;
@@ -13,6 +13,7 @@ const defaultOptions = {
13
13
  interact: true,
14
14
  autoScroll: true,
15
15
  autoCenter: true,
16
+ sampleRate: 8000,
16
17
  };
17
18
  class WaveSurfer extends Player {
18
19
  /** Create a new WaveSurfer instance */
@@ -30,7 +31,6 @@ class WaveSurfer extends Player {
30
31
  this.decodedData = null;
31
32
  this.canPlay = false;
32
33
  this.options = Object.assign({}, defaultOptions, options);
33
- this.fetcher = new Fetcher();
34
34
  this.timer = new Timer();
35
35
  this.renderer = new Renderer({
36
36
  container: this.options.container,
@@ -77,9 +77,17 @@ class WaveSurfer extends Player {
77
77
  this.canPlay && this.seekTo(relativeX);
78
78
  this.emit('interaction');
79
79
  }
80
+ }), this.renderer.on('drag', (relativeX) => {
81
+ if (this.options.interact) {
82
+ const newTime = this.getCurrentTime() + this.getDuration() * relativeX;
83
+ this.canPlay && this.setTime(newTime);
84
+ this.emit('interaction');
85
+ }
80
86
  }), this.renderer.on('scroll', (startX, endX) => {
81
87
  const duration = this.getDuration();
82
88
  this.emit('scroll', startX * duration, endX * duration);
89
+ }), this.renderer.on('render', () => {
90
+ this.emit('redraw');
83
91
  }));
84
92
  }
85
93
  initTimerEvents() {
@@ -143,12 +151,11 @@ class WaveSurfer extends Player {
143
151
  }
144
152
  else {
145
153
  // Fetch and decode the audio of no pre-computed audio data is provided
146
- const audio = await this.fetcher.load(url);
154
+ const audio = await Fetcher.load(url);
147
155
  this.decodedData = await Decoder.decode(audio, this.options.sampleRate);
148
156
  }
149
157
  this.renderAudio();
150
158
  this.emit('decode', this.getDuration());
151
- this.emit('redraw');
152
159
  }
153
160
  renderAudio() {
154
161
  if (!this.decodedData)
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.WaveSurfer=e():t.WaveSurfer=e()}(this,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var s in i)t.o(i,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:i[s]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>d});const i={decode:async function(t,e=8e3){const i=new AudioContext({sampleRate:e}),s=i.decodeAudioData(t);return s.finally((()=>i.close())),s},createBuffer:function(t,e){if("number"==typeof t[0]&&(t=[t]),t[0].some((t=>t>1||t<-1))){const e=Math.max(...t[0]);t=t.map((t=>t.map((t=>t/e))))}return{length:t[0].length,duration:e,numberOfChannels:t.length,sampleRate:t[0].length/e,getChannelData:e=>t?.[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}},s=class{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,e){super(),this.options={height:0},this.timeout=null,this.isScrolling=!1,this.channelData=null,this.duration=null,this.resizeObserver=null,this.options={...e};let i=null;if("string"==typeof t.container?i=document.querySelector(t.container):t.container instanceof HTMLElement&&(i=t.container),!i)throw new Error("Container not found");const[s,n]=this.initHtml();i.appendChild(s),this.container=s,this.scrollContainer=n.querySelector(".scroll"),this.wrapper=n.querySelector(".wrapper"),this.canvasWrapper=n.querySelector(".canvases"),this.progressWrapper=n.querySelector(".progress"),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.scrollContainer.addEventListener("scroll",(()=>{const{scrollLeft:t,scrollWidth:e,clientWidth:i}=this.scrollContainer,s=t/e,n=(t+i)/e;this.emit("scroll",s,n)})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.reRender()),100)})),this.resizeObserver.observe(this.scrollContainer)}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 }\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 position: relative;\n height: ${this.options.height}px;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n height: ${this.options.height}px;\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 box-sizing: border-box;\n }\n </style>\n\n <div class="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="progress"></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(){this.container.remove(),this.resizeObserver?.disconnect()}delay(t,e=10){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}async renderPeaks(t,e,i,s){const r=null!=this.options.barWidth?this.options.barWidth*s:1,o=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?r/2:0,a=this.options.barRadius??0,h=this.options.barHeight??1,l=t[0],d=l.length,c=Math.floor(e/(r+o))/d,p=i/2,u=1===t.length,m=u?l:t[1],g=u&&m.some((t=>t<0)),y=(t,i)=>{let n=0,u=0,y=0;const f=document.createElement("canvas");f.width=Math.round(e*(i-t)/d),f.height=this.options.height,f.style.width=`${Math.floor(f.width/s)}px`,f.style.height=`${this.options.height}px`,f.style.left=`${Math.floor(t*e/s/d)}px`,this.canvasWrapper.appendChild(f);const v=f.getContext("2d",{desynchronized:!0});v.beginPath(),v.fillStyle=this.options.waveColor??"",v.roundRect||(v.roundRect=v.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*c);if(i>n){const t=Math.round(u*p*h),e=Math.round(y*p*h);v.roundRect(n*(r+o),p-t,r,t+(e||1),a),n=i,u=0,y=0}const s=g?l[e]:Math.abs(l[e]),d=g?m[e]:Math.abs(m[e]);s>u&&(u=s),(g?d<-y:d>y)&&(y=d<0?-d:d)}v.fill(),v.closePath();const b=f.cloneNode();this.progressWrapper.appendChild(b);const w=b.getContext("2d",{desynchronized:!0});f.width>0&&f.height>0&&w.drawImage(f,0,0),w.globalCompositeOperation="source-in",w.fillStyle=this.options.progressColor??"",w.fillRect(0,0,f.width,f.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:f,scrollWidth:v,clientWidth:b}=this.scrollContainer,w=d/v;let C=Math.min(n.MAX_CANVAS_WIDTH,b);C-=C%((r+o)/s);const P=Math.floor(Math.abs(f)*w),M=Math.ceil(P+C*w);y(P,M);const E=M-P;for(let t=M;t<d;t+=E)await this.delay((()=>{y(t,Math.min(d,t+E))}));for(let t=P-1;t>=0;t-=E)await this.delay((()=>{y(Math.max(0,t-E),t)}))}render(t,e){const i=window.devicePixelRatio||1,s=this.scrollContainer.clientWidth,n=Math.ceil(e*(this.options.minPxPerSec||0));this.isScrolling=n>s;const r=this.options.fillParent&&!this.isScrolling,o=(r?s:n)*i,{height:a}=this.options;this.wrapper.style.width=r?"100%":`${n}px`,this.scrollContainer.style.overflowX=this.isScrolling?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.progressWrapper.style.borderRightStyle="solid",this.progressWrapper.style.borderRightColor=`${this.options.cursorColor||this.options.progressColor}`,this.progressWrapper.style.borderRightWidth=`${this.options.cursorWidth}px`,this.canvasWrapper.style.height=`${this.options.height}px`,this.renderPeaks(t,o,a,i),this.channelData=t,this.duration=e}reRender(){if(!this.channelData||!this.duration)return;const t=this.progressWrapper.clientWidth;this.render(this.channelData,this.duration);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?o:i)||r<s)if(this.options.autoCenter){const t=o/20;r-(s+o)>=t&&r<s+i?this.scrollContainer.scrollLeft+=t:this.scrollContainer.scrollLeft=r-o}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.isScrolling&&this.options.autoScroll&&this.scrollIntoView(t,e))}}n.MAX_CANVAS_WIDTH=4e3;const r=n,o=class extends s{constructor(t){super(),this.subscriptions=[],this.isExternalMedia=!1,t.media?(this.media=t.media,this.isExternalMedia=!0):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})}loadUrl(t){this.media.src=t}destroy(){this.media.pause(),this.subscriptions.forEach((t=>t())),this.isExternalMedia||this.media.remove()}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)}},a=class 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()}},h={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoScroll:!0,autoCenter:!0};class l extends o{static create(t){return new l(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.canPlay=!1,this.options=Object.assign({},h,t),this.fetcher=new class{async load(t){return fetch(t).then((t=>t.arrayBuffer()))}},this.timer=new a,this.renderer=new r({container:this.options.container},this.options),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent(),this.initPlugins();const e=this.options.url||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}setOptions(t){this.options={...this.options,...t},this.renderer.setOptions(this.options)}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.getCurrentTime()>=this.getDuration()&&this.emit("finish")})),this.onMediaEvent("canplay",(()=>{this.canPlay=!0,this.emit("canplay",this.getDuration())})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(t=>{this.options.interact&&(this.canPlay&&this.seekTo(t),this.emit("interaction"))})),this.renderer.on("scroll",((t,e)=>{const i=this.getDuration();this.emit("scroll",t*i,e*i)})))}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)})))}initReadyEvent(){const t=()=>{this.decodedData&&this.canPlay&&this.emit("ready",this.getDuration())};this.subscriptions.push(this.on("decode",t),this.on("canplay",t))}initPlugins(){this.options.plugins?.length&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init(this),this.plugins.push(t),t}getWrapper(){return this.renderer.getWrapper()}getScroll(){return this.renderer.getScroll()}getActivePlugins(){return this.plugins}async load(t,e,s){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),this.emit("load",t),e)s||(s=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getMediaElement().duration)))}))||0),this.decodedData=i.createBuffer(e,s);else{const e=await this.fetcher.load(t);this.decodedData=await i.decode(e,this.options.sampleRate)}this.renderAudio(),this.emit("decode",this.getDuration()),this.emit("redraw")}renderAudio(){if(!this.decodedData)return;const t=[this.decodedData.getChannelData(0)];this.decodedData.numberOfChannels>1&&t.push(this.decodedData.getChannelData(1)),this.renderer.render(t,this.decodedData.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(){const t=super.getDuration();return t>0&&t<1/0?t:this.decodedData?.duration||0}toggleInteraction(t){this.options.interact=t}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}playPause(){return this.isPlaying()?(this.pause(),Promise.resolve()):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.timer.destroy(),this.renderer.destroy(),super.destroy()}}const d=l;return e.default})()));
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.WaveSurfer=e():t.WaveSurfer=e()}(this,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var s in i)t.o(i,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:i[s]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>d});const i=async function(t){return fetch(t).then((t=>t.arrayBuffer()))},s={decode:async function(t,e){const i=new AudioContext({sampleRate:e}),s=i.decodeAudioData(t);return s.finally((()=>i.close())),s},createBuffer:function(t,e){if("number"==typeof t[0]&&(t=[t]),t[0].some((t=>t>1||t<-1))){const e=Math.max(...t[0]);t=t.map((t=>t.map((t=>t/e))))}return{length:t[0].length,duration:e,numberOfChannels:t.length,sampleRate:t[0].length/e,getChannelData:e=>t?.[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}},n=class{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 r extends n{constructor(t,e){super(),this.options={height:0},this.timeout=null,this.isScrolling=!1,this.channelData=null,this.duration=null,this.resizeObserver=null,this.options={...e};let i=null;if("string"==typeof t.container?i=document.querySelector(t.container):t.container instanceof HTMLElement&&(i=t.container),!i)throw new Error("Container not found");const[s,n]=this.initHtml();i.appendChild(s),this.container=s,this.scrollContainer=n.querySelector(".scroll"),this.wrapper=n.querySelector(".wrapper"),this.canvasWrapper=n.querySelector(".canvases"),this.progressWrapper=n.querySelector(".progress"),this.cursor=n.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.wrapper.addEventListener("mousedown",(t=>{let e=t.clientX,i=!0;const s=t=>{const s=t.clientX-e;if(e=t.clientX,0!==s){const t=this.wrapper.getBoundingClientRect();i&&(i=!1,this.emit("click",(e-t.left)/t.width)),this.emit("drag",s/t.width)}},n=()=>{document.removeEventListener("mousemove",s),document.removeEventListener("mouseup",n)};document.addEventListener("mousemove",s),document.addEventListener("mouseup",n)})),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)})),this.resizeObserver=new ResizeObserver((()=>{this.delay((()=>this.reRender()),100)})),this.resizeObserver.observe(this.scrollContainer)}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 }\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 position: relative;\n height: ${this.options.height}px;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n height: ${this.options.height}px;\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 .cursor {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n height: 100%;\n width: ${this.options.cursorWidth}px;\n background-color: ${this.options.cursorColor||this.options.progressColor};\n }\n </style>\n\n <div class="scroll" part="scroll">\n <div class="wrapper">\n <div class="canvases"></div>\n <div class="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(){this.container.remove(),this.resizeObserver?.disconnect()}delay(t,e=10){return this.timeout&&clearTimeout(this.timeout),new Promise((i=>{this.timeout=setTimeout((()=>{i(t())}),e)}))}async renderPeaks(t,e,i,s){const n=null!=this.options.barWidth?this.options.barWidth*s:1,o=null!=this.options.barGap?this.options.barGap*s:this.options.barWidth?n/2:0,a=this.options.barRadius??0,h=this.options.barHeight??1,l=t[0],c=l.length,d=Math.floor(e/(n+o))/c,u=i/2,p=1===t.length,m=p?l:t[1],g=p&&m.some((t=>t<0)),y=(t,i)=>{let r=0,p=0,y=0;const f=document.createElement("canvas");f.width=Math.round(e*(i-t)/c),f.height=this.options.height,f.style.width=`${Math.floor(f.width/s)}px`,f.style.height=`${this.options.height}px`,f.style.left=`${Math.floor(t*e/s/c)}px`,this.canvasWrapper.appendChild(f);const v=f.getContext("2d",{desynchronized:!0});v.beginPath(),v.fillStyle=this.options.waveColor??"",v.roundRect||(v.roundRect=v.fillRect);for(let e=t;e<i;e++){const i=Math.round((e-t)*d);if(i>r){const t=Math.round(p*u*h),e=Math.round(y*u*h);v.roundRect(r*(n+o),u-t,n,t+(e||1),a),r=i,p=0,y=0}const s=g?l[e]:Math.abs(l[e]),c=g?m[e]:Math.abs(m[e]);s>p&&(p=s),(g?c<-y:c>y)&&(y=c<0?-c:c)}v.fill(),v.closePath();const b=f.cloneNode();this.progressWrapper.appendChild(b);const w=b.getContext("2d",{desynchronized:!0});f.width>0&&f.height>0&&w.drawImage(f,0,0),w.globalCompositeOperation="source-in",w.fillStyle=this.options.progressColor??"",w.fillRect(0,0,f.width,f.height)};this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="";const{scrollLeft:f,scrollWidth:v,clientWidth:b}=this.scrollContainer,w=c/v;let C=Math.min(r.MAX_CANVAS_WIDTH,b);C-=C%((n+o)/s);const E=Math.floor(Math.abs(f)*w),P=Math.ceil(E+C*w);y(E,P);const M=P-E;for(let t=P;t<c;t+=M)await this.delay((()=>{y(t,Math.min(c,t+M))}));for(let t=E-1;t>=0;t-=M)await this.delay((()=>{y(Math.max(0,t-M),t)}))}render(t,e){const i=window.devicePixelRatio||1,s=this.scrollContainer.clientWidth,n=Math.ceil(e*(this.options.minPxPerSec||0));this.isScrolling=n>s;const r=this.options.fillParent&&!this.isScrolling,o=(r?s:n)*i,{height:a}=this.options;this.wrapper.style.width=r?"100%":`${n}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.canvasWrapper.style.height=`${this.options.height}px`,this.renderPeaks(t,o,a,i),this.channelData=t,this.duration=e,this.emit("render")}reRender(){if(!this.channelData||!this.duration)return;const t=this.progressWrapper.clientWidth;this.render(this.channelData,this.duration);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?o:i)||r<s)if(this.options.autoCenter){const t=o/20;r-(s+o)>=t&&r<s+i?this.scrollContainer.scrollLeft+=t:this.scrollContainer.scrollLeft=r-o}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.isScrolling&&this.options.autoScroll&&this.scrollIntoView(t,e))}}r.MAX_CANVAS_WIDTH=4e3;const o=r,a=class extends n{constructor(t){super(),this.subscriptions=[],this.isExternalMedia=!1,t.media?(this.media=t.media,this.isExternalMedia=!0):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})}loadUrl(t){this.media.src=t}destroy(){this.media.pause(),this.subscriptions.forEach((t=>t())),this.isExternalMedia||this.media.remove()}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)}},h=class extends n{constructor(){super(...arguments),this.unsubscribe=()=>{}}start(){this.unsubscribe=this.on("tick",(()=>{requestAnimationFrame((()=>{this.emit("tick")}))})),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}},l={height:128,waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class c extends a{static create(t){return new c(t)}constructor(t){super({media:t.media,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.canPlay=!1,this.options=Object.assign({},l,t),this.timer=new h,this.renderer=new o({container:this.options.container},this.options),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReadyEvent(),this.initPlugins();const e=this.options.url||this.options.media?.src;e&&this.load(e,this.options.peaks,this.options.duration)}setOptions(t){this.options={...this.options,...t},this.renderer.setOptions(this.options)}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.getCurrentTime()>=this.getDuration()&&this.emit("finish")})),this.onMediaEvent("canplay",(()=>{this.canPlay=!0,this.emit("canplay",this.getDuration())})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(t=>{this.options.interact&&(this.canPlay&&this.seekTo(t),this.emit("interaction"))})),this.renderer.on("drag",(t=>{if(this.options.interact){const e=this.getCurrentTime()+this.getDuration()*t;this.canPlay&&this.setTime(e),this.emit("interaction")}})),this.renderer.on("scroll",((t,e)=>{const i=this.getDuration();this.emit("scroll",t*i,e*i)})),this.renderer.on("render",(()=>{this.emit("redraw")})))}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)})))}initReadyEvent(){const t=()=>{this.decodedData&&this.canPlay&&this.emit("ready",this.getDuration())};this.subscriptions.push(this.on("decode",t),this.on("canplay",t))}initPlugins(){this.options.plugins?.length&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}registerPlugin(t){return t.init(this),this.plugins.push(t),t}getWrapper(){return this.renderer.getWrapper()}getScroll(){return this.renderer.getScroll()}getActivePlugins(){return this.plugins}async load(t,e,n){if(this.decodedData=null,this.canPlay=!1,this.loadUrl(t),this.emit("load",t),e)n||(n=await new Promise((t=>{this.onceMediaEvent("loadedmetadata",(()=>t(this.getMediaElement().duration)))}))||0),this.decodedData=s.createBuffer(e,n);else{const e=await i(t);this.decodedData=await s.decode(e,this.options.sampleRate)}this.renderAudio(),this.emit("decode",this.getDuration())}renderAudio(){if(!this.decodedData)return;const t=[this.decodedData.getChannelData(0)];this.decodedData.numberOfChannels>1&&t.push(this.decodedData.getChannelData(1)),this.renderer.render(t,this.decodedData.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(){const t=super.getDuration();return t>0&&t<1/0?t:this.decodedData?.duration||0}toggleInteraction(t){this.options.interact=t}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}playPause(){return this.isPlaying()?(this.pause(),Promise.resolve()):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.timer.destroy(),this.renderer.destroy(),super.destroy()}}const d=c;return e.default})()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wavesurfer.js",
3
- "version": "7.0.0-alpha.47",
3
+ "version": "7.0.0-alpha.49",
4
4
  "license": "BSD-3-Clause",
5
5
  "author": "katspaugh",
6
6
  "homepage": "https://wavesurfer-js.org",
@@ -43,7 +43,7 @@
43
43
  "prettier": "prettier -w '**/*.{js,ts,css}' --ignore-path .gitignore",
44
44
  "cypress": "cypress open",
45
45
  "test": "cypress run",
46
- "serve": "browser-sync start --server --port 9090 --watch '*' --no-ghost-mode --no-ui -b 'Google Chrome' --startPath '/examples'",
46
+ "serve": "browser-sync start --server --port 9090 --watch '*' --no-ghost-mode --no-ui -b 'Google Chrome'",
47
47
  "start": "npm run build:dev & npm run serve"
48
48
  },
49
49
  "devDependencies": {