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/README.md +30 -9
- package/dist/decoder.d.ts +1 -1
- package/dist/decoder.js +1 -1
- package/dist/fetcher.d.ts +4 -3
- package/dist/fetcher.js +5 -4
- package/dist/plugins/envelope.d.ts +16 -10
- package/dist/plugins/envelope.js +184 -171
- package/dist/plugins/envelope.min.js +1 -1
- package/dist/plugins/minimap.js +1 -0
- package/dist/plugins/minimap.min.js +1 -1
- package/dist/plugins/multitrack.js +1 -0
- package/dist/plugins/multitrack.min.js +1 -1
- package/dist/plugins/regions.d.ts +2 -2
- package/dist/plugins/regions.js +7 -4
- package/dist/plugins/regions.min.js +1 -1
- package/dist/plugins/spectrogram.d.ts +3 -0
- package/dist/plugins/spectrogram.js +3 -0
- package/dist/plugins/timeline.d.ts +1 -1
- package/dist/plugins/timeline.js +4 -2
- package/dist/plugins/timeline.min.js +1 -1
- package/dist/renderer.d.ts +3 -0
- package/dist/renderer.js +40 -5
- package/dist/wavesurfer.d.ts +2 -2
- package/dist/wavesurfer.js +10 -3
- package/dist/wavesurfer.min.js +1 -1
- package/package.json +2 -2
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
|
-
|
|
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.
|
|
251
|
-
this.
|
|
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
|
}
|
package/dist/wavesurfer.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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;
|
package/dist/wavesurfer.js
CHANGED
|
@@ -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
|
|
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)
|
package/dist/wavesurfer.min.js
CHANGED
|
@@ -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.
|
|
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'
|
|
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": {
|