UNPKG

wavesurfer

Version:

Interactive navigable audio visualization using Web Audio and Canvas

3 lines 7.98 kB
/*! wavesurfer.js 1.3.4 (Sat, 25 Feb 2017 22:02:05 GMT) * https://github.com/katspaugh/wavesurfer.js * @license CC-BY-3.0 */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Spectrogram={init:function(a){this.params=a;var b=this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw Error("No WaveSurfer instance provided");this.frequenciesDataUrl=a.frequenciesDataUrl;var c=this.drawer=this.wavesurfer.drawer;if(this.container="string"==typeof a.container?document.querySelector(a.container):a.container,!this.container)throw Error("No container for WaveSurfer spectrogram");this.width=c.width,this.pixelRatio=this.params.pixelRatio||b.params.pixelRatio,this.fftSamples=this.params.fftSamples||b.params.fftSamples||512,this.height=this.fftSamples/2,this.noverlap=a.noverlap,this.windowFunc=a.windowFunc,this.alpha=a.alpha,this.createWrapper(),this.createCanvas(),this.render(),c.wrapper.addEventListener("scroll",function(a){this.updateScroll(a)}.bind(this)),b.on("redraw",this.render.bind(this)),b.on("destroy",this.destroy.bind(this))},destroy:function(){this.unAll(),this.wrapper&&(this.wrapper.parentNode.removeChild(this.wrapper),this.wrapper=null)},createWrapper:function(){var a=this.container.querySelector("spectrogram");if(a&&this.container.removeChild(a),this.params.labels){var b=document.createElement("div");b.setAttribute("id","specLabels"),this.drawer.style(b,{left:0,position:"relative",zIndex:9}),b.innerHTML="<canvas></canvas>",this.wrapper=this.container.appendChild(b),this.loadLabels("rgba(68,68,68,0.5)","12px","10px","","#fff","#f7f7f7","center","#specLabels")}var c=this.wavesurfer.params,d=document.createElement("spectrogram");this.params.labels&&this.drawer.style(d,{left:0,position:"relative"}),this.wrapper=this.container.appendChild(d),this.drawer.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height+"px"}),(c.fillParent||c.scrollParent)&&this.drawer.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"});var e=this;this.wrapper.addEventListener("click",function(a){a.preventDefault();var b="offsetX"in a?a.offsetX:a.layerX;e.fireEvent("click",b/e.scrollWidth||0)})},createCanvas:function(){var a=this.canvas=this.wrapper.appendChild(document.createElement("canvas"));this.spectrCc=a.getContext("2d"),this.wavesurfer.drawer.style(a,{position:"absolute",zIndex:4})},render:function(){this.updateCanvasStyle(),this.frequenciesDataUrl?this.loadFrequenciesData(this.frequenciesDataUrl):this.getFrequencies(this.drawSpectrogram)},updateCanvasStyle:function(){var a=Math.round(this.width/this.pixelRatio)+"px";this.canvas.width=this.width,this.canvas.height=this.height,this.canvas.style.width=a},drawSpectrogram:function(a,b){for(var c=(b.spectrCc,b.wavesurfer.backend.getDuration(),b.height),d=b.resample(a),e=b.buffer?2/b.buffer.numberOfChannels:1,f=0;f<d.length;f++)for(var g=0;g<d[f].length;g++){var h=255-d[f][g];b.spectrCc.fillStyle="rgb("+h+", "+h+", "+h+")",b.spectrCc.fillRect(f,c-g*e,1,e)}},getFrequencies:function(b){var c=this.fftSamples,d=this.buffer=this.wavesurfer.backend.buffer,e=d.getChannelData(0),f=d.length,g=d.sampleRate,h=[];if(!d)return void this.fireEvent("error","Web Audio buffer is not available");var i=this.noverlap;if(!i){var j=d.length/this.canvas.width;i=Math.max(0,Math.round(c-j))}for(var k=new a.FFT(c,g,this.windowFunc,this.alpha),l=(Math.floor(f/(c-i)),0);l+c<e.length;){for(var m=e.slice(l,l+c),n=k.calculateSpectrum(m),o=new Uint8Array(c/2),p=0;p<c/2;p++)o[p]=Math.max(-255,45*Math.log10(n[p]));h.push(o),l+=c-i}b(h,this)},loadFrequenciesData:function(b){var c=this,d=a.util.ajax({url:b});return d.on("success",function(a){c.drawSpectrogram(JSON.parse(a),c)}),d.on("error",function(a){c.fireEvent("error","XHR error: "+a.target.statusText)}),d},freqType:function(a){return a>=1e3?(a/1e3).toFixed(1):Math.round(a)},unitType:function(a){return a>=1e3?"KHz":"Hz"},loadLabels:function(a,b,c,d,e,f,g,h){var i=this.height,a=a||"rgba(68,68,68,0.5)",b=b||"12px",c=c||"10px",d=d||"Helvetica",e=e||"#fff",f=f||"#fff",g=g||"center",h=h||"#specLabels",j=i||512,k=5*(j/256),l=0,m=(this.wavesurfer.backend.ac.sampleRate/2-l)/k,n=document.querySelectorAll(h+" canvas")[0].getContext("2d");document.querySelectorAll(h+" canvas")[0].height=this.height,document.querySelectorAll(h+" canvas")[0].width=55,n.fillStyle=a,n.fillRect(0,0,55,j),n.fill();for(var o=0;o<=k;o++){n.textAlign=g,n.textBaseline="middle";var p=l+m*o,q=Math.round(p/this.sampleRate/2*this.fftSamples),q=Math.round(p/(this.fftSamples/2)*this.fftSamples),r=q/this.fftSamples/2,s=((1-r)*this.height,this.freqType(p)),t=this.unitType(p),u=16,v=2;0==o?(n.fillStyle=f,n.font=c+" "+d,n.fillText(t,u+24,j+o-10),n.fillStyle=e,n.font=b+" "+d,n.fillText(s,u,j+o-10)):(n.fillStyle=f,n.font=c+" "+d,n.fillText(t,u+24,j-50*o+v),n.fillStyle=e,n.font=b+" "+d,n.fillText(s,u,j-50*o+v))}},updateScroll:function(a){this.wrapper.scrollLeft=a.target.scrollLeft},resample:function(a,b){for(var b=this.width,c=[],d=1/a.length,e=1/b,f=0;f<b;f++){for(var g=new Array(a[0].length),h=0;h<a.length;h++){var i=h*d,j=i+d,k=f*e,l=k+e,m=j<=k||l<=i?0:Math.min(Math.max(j,k),Math.max(l,i))-Math.max(Math.min(j,k),Math.min(l,i));if(m>0)for(var n=0;n<a[0].length;n++)null==g[n]&&(g[n]=0),g[n]+=m/e*a[h][n]}for(var o=new Uint8Array(a[0].length),n=0;n<a[0].length;n++)o[n]=g[n];c.push(o)}return c}},a.FFT=function(a,b,c,d){switch(this.bufferSize=a,this.sampleRate=b,this.bandwidth=2/a*b/2,this.sinTable=new Float32Array(a),this.cosTable=new Float32Array(a),this.windowValues=new Float32Array(a),this.reverseTable=new Uint32Array(a),this.peakBand=0,this.peak=0,c){case"bartlett":for(var e=0;e<a;e++)this.windowValues[e]=2/(a-1)*((a-1)/2-Math.abs(e-(a-1)/2));break;case"bartlettHann":for(var e=0;e<a;e++)this.windowValues[e]=.62-.48*Math.abs(e/(a-1)-.5)-.38*Math.cos(2*Math.PI*e/(a-1));break;case"blackman":d=d||.16;for(var e=0;e<a;e++)this.windowValues[e]=(1-d)/2-.5*Math.cos(2*Math.PI*e/(a-1))+d/2*Math.cos(4*Math.PI*e/(a-1));break;case"cosine":for(var e=0;e<a;e++)this.windowValues[e]=Math.cos(Math.PI*e/(a-1)-Math.PI/2);break;case"gauss":d=d||.25;for(var e=0;e<a;e++)this.windowValues[e]=Math.pow(Math.E,-.5*Math.pow((e-(a-1)/2)/(d*(a-1)/2),2));break;case"hamming":for(var e=0;e<a;e++)this.windowValues[e]=.54-.46*Math.cos(2*Math.PI*e/(a-1));break;case"hann":case void 0:for(var e=0;e<a;e++)this.windowValues[e]=.5*(1-Math.cos(2*Math.PI*e/(a-1)));break;case"lanczoz":for(var e=0;e<a;e++)this.windowValues[e]=Math.sin(Math.PI*(2*e/(a-1)-1))/(Math.PI*(2*e/(a-1)-1));break;case"rectangular":for(var e=0;e<a;e++)this.windowValues[e]=1;break;case"triangular":for(var e=0;e<a;e++)this.windowValues[e]=2/a*(a/2-Math.abs(e-(a-1)/2));break;default:throw Error("No such window function '"+c+"'")}for(var e,f=1,g=a>>1;f<a;){for(e=0;e<f;e++)this.reverseTable[e+f]=this.reverseTable[e]+g;f<<=1,g>>=1}for(e=0;e<a;e++)this.sinTable[e]=Math.sin(-Math.PI/e),this.cosTable[e]=Math.cos(-Math.PI/e);this.calculateSpectrum=function(a){var b,c,d,e=this.bufferSize,f=this.cosTable,g=this.sinTable,h=this.reverseTable,i=new Float32Array(e),j=new Float32Array(e),k=2/this.bufferSize,l=Math.sqrt,m=new Float32Array(e/2),n=Math.floor(Math.log(e)/Math.LN2);if(Math.pow(2,n)!==e)throw"Invalid buffer size, must be a power of 2.";if(e!==a.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+e+" Buffer Size: "+a.length;for(var o,p,q,r,s,t,u,v,w=1,x=0;x<e;x++)i[x]=a[h[x]]*this.windowValues[h[x]],j[x]=0;for(;w<e;){o=f[w],p=g[w],q=1,r=0;for(var y=0;y<w;y++){for(var x=y;x<e;)s=x+w,t=q*i[s]-r*j[s],u=q*j[s]+r*i[s],i[s]=i[x]-t,j[s]=j[x]-u,i[x]+=t,j[x]+=u,x+=w<<1;v=q,q=v*o-r*p,r=v*p+r*o}w<<=1}for(var x=0,z=e/2;x<z;x++)b=i[x],c=j[x],d=k*l(b*b+c*c),d>this.peak&&(this.peakBand=x,this.peak=d),m[x]=d;return m}},a.util.extend(a.Spectrogram,a.Observer,a.FFT)});