1787 lines
56 KiB
JavaScript
1787 lines
56 KiB
JavaScript
|
// Web Audio API Simulator
|
||
|
// Copyright (c) 2013 g200kg
|
||
|
// http://www.g200kg.com/
|
||
|
// Released under the MIT-License
|
||
|
// http://opensource.org/licenses/MIT
|
||
|
//
|
||
|
// Great thanks :
|
||
|
// FFT algo for AnalyserNode and Convolver is based on Takuya OOURA's explanation.
|
||
|
// http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/index.html
|
||
|
|
||
|
if(typeof(waapisimLogEnable)==="undefined")
|
||
|
var waapisimLogEnable=0;
|
||
|
|
||
|
// Support Float32Array&Uint8Array if unavailable (for IE9)
|
||
|
if(typeof(Float32Array)==="undefined") {
|
||
|
Float32Array=function(n) {
|
||
|
if(n instanceof Array)
|
||
|
return n;
|
||
|
var a=new Array(n);
|
||
|
a.subarray=function(x,y) {return this.slice(x,y);};
|
||
|
a.set=function(x,off) {for(var i=0;i<x.length;++i) a[off+i]=x[i];};
|
||
|
return a;
|
||
|
};
|
||
|
}
|
||
|
if(typeof(Uint8Array)==="undefined") {
|
||
|
Uint8Array=function(n) {
|
||
|
if(n instanceof Array)
|
||
|
return n;
|
||
|
var a=new Array(n);
|
||
|
a.subarray=function(x,y) {return this.slice(x,y);};
|
||
|
a.set=function(x,off) {for(var i=0;i<x.length;++i) a[off+i]=x[i];};
|
||
|
return a;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if(typeof(waapisimLogEnable)!=="undefined"&&waapisimLogEnable)
|
||
|
waapisimDebug=function(a) {console.log(a);};
|
||
|
else
|
||
|
waapisimDebug=function(){};
|
||
|
|
||
|
if(typeof(webkitAudioContext)!=="undefined") {
|
||
|
if(typeof(webkitAudioContext.prototype.createGain)==="undefined") {
|
||
|
webkitAudioContext.prototype.createScriptProcessor=webkitAudioContext.prototype.createJavaScriptNode;
|
||
|
webkitAudioContext.prototype.createGain=(function(){
|
||
|
var o=webkitAudioContext.prototype.createGainNode.call(this);
|
||
|
o._gain=o.gain; o.gain=o._gain;
|
||
|
o.gain.setTargetAtTime=o._gain.setTargetValueAtTime;
|
||
|
return o;
|
||
|
});
|
||
|
webkitAudioContext.prototype.createDelay=(function(){
|
||
|
var o=webkitAudioContext.prototype.createDelayNode.call(this);
|
||
|
o._delayTime=o.delayTime; o.delayTime=o._delayTime;
|
||
|
o.delayTime.setTargetAtTime=o._delayTime.setTargetValueAtTime;
|
||
|
return o;
|
||
|
});
|
||
|
webkitAudioContext.prototype._createOscillator=webkitAudioContext.prototype.createOscillator;
|
||
|
webkitAudioContext.prototype.createOscillator=(function() {
|
||
|
var o=webkitAudioContext.prototype._createOscillator.call(this);
|
||
|
o._frequency=o.frequency; o.frequency=o._frequency;
|
||
|
o.frequency.setTargetAtTime=o._frequency.setTargetValueAtTime;
|
||
|
o._detune=o.detune; o.detune=o._detune;
|
||
|
o.detune.setTargetAtTime=o._detune.setTargetValueAtTime;
|
||
|
o.start=o.noteOn;
|
||
|
o.stop=o.noteOff;
|
||
|
return o;
|
||
|
});
|
||
|
webkitAudioContext.prototype._createBufferSource=webkitAudioContext.prototype.createBufferSource;
|
||
|
webkitAudioContext.prototype.createBufferSource=(function() {
|
||
|
var o=webkitAudioContext.prototype._createBufferSource.call(this);
|
||
|
o._playbackRate=o.playbackRate; o.playbackRate=o._playbackRate;
|
||
|
o.playbackRate.setTargetAtTime=o._playbackRate.setTargetValueAtTime;
|
||
|
o.start=function(w,off,dur) {
|
||
|
if(off===undefined)
|
||
|
o.noteOn(w);
|
||
|
else
|
||
|
o.noteGrainOn(w,off,dur);
|
||
|
};
|
||
|
o.stop=o.noteOff;
|
||
|
return o;
|
||
|
});
|
||
|
webkitAudioContext.prototype._createBiquadFilter=webkitAudioContext.prototype.createBiquadFilter;
|
||
|
webkitAudioContext.prototype.createBiquadFilter=(function() {
|
||
|
var o=webkitAudioContext.prototype._createBiquadFilter.call(this);
|
||
|
o._frequency=o.frequency; o.frequency=o._frequency;
|
||
|
o.frequency.setTargetAtTime=o._frequency.setTargetValueAtTime;
|
||
|
o._Q=o.Q; o.Q=o._Q;
|
||
|
o.Q.setTargetAtTime=o._Q.setTargetValueAtTime;
|
||
|
o._gain=o.gain; o.gain=o._gain;
|
||
|
o.gain.setTargetAtTime=o._gain.setTargetValueAtTime;
|
||
|
return o;
|
||
|
});
|
||
|
webkitAudioContext.prototype._createDynamicsCompressor=webkitAudioContext.prototype.createDynamicsCompressor;
|
||
|
webkitAudioContext.prototype.createDynamicsCompressor=(function() {
|
||
|
var o=webkitAudioContext.prototype._createDynamicsCompressor.call(this);
|
||
|
o._threshold=o.threshold; o.threshold=o._threshold;
|
||
|
o.threshold.setTargetAtTime=o._threshold.setTargetValueAtTime;
|
||
|
o._knee=o.knee; o.knee=o._knee;
|
||
|
o.knee.setTargetAtTime=o._knee.setTargetValueAtTime;
|
||
|
o._ratio=o.ratio; o.ratio=o._ratio;
|
||
|
o.ratio.setTargetAtTime=o._ratio.setTargetValueAtTime;
|
||
|
o._attack=o.attack; o.attack=o._attack;
|
||
|
o.attack.setTargetAtTime=o._attack.setTargetValueAtTime;
|
||
|
return o;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
if((typeof(waapisimForceSim)!=="undefined"&&waapisimForceSim)
|
||
|
||(typeof(AudioContext)!=="undefined"&&typeof(AudioContext.prototype.createOscillator)==="undefined"&&(typeof(waapisimForceSimWhenLackOsc)==="undefined"||(typeof(waapisimForceSimWhenLackOsc)!=="undefined"&&waapisimForceSimWhenLackOsc)))
|
||
|
||(typeof(webkitAudioContext)==="undefined" && typeof(waapisimForceSimWhenNotWebkit)!=="undefined"&&waapisimForceSimWhenNotWebkit)
|
||
|
||(typeof(webkitAudioContext)==="undefined" && typeof(AudioContext)==="undefined")) {
|
||
|
waapisimSampleRate=44100;
|
||
|
waapisimAudioIf=0;
|
||
|
waapisimBufSize=1024;
|
||
|
waapisimFlashBufSize=1024*3;
|
||
|
if(typeof(Audio)!=="undefined") {
|
||
|
waapisimAudio=new Audio();
|
||
|
if(typeof(waapisimAudio.mozSetup)!=="undefined")
|
||
|
waapisimAudioIf=1;
|
||
|
}
|
||
|
if(waapisimAudioIf===0) {
|
||
|
waapisimOutBufSize=waapisimFlashBufSize;
|
||
|
waapisimOutBuf=new Array(waapisimOutBufSize*2);
|
||
|
}
|
||
|
else {
|
||
|
waapisimOutBufSize=waapisimBufSize;
|
||
|
waapisimOutBuf=new Float32Array(waapisimOutBufSize*2);
|
||
|
waapisimAudio.mozSetup(2,waapisimSampleRate);
|
||
|
}
|
||
|
for(var l=waapisimOutBuf.length,i=0;i<l;++i)
|
||
|
waapisimOutBuf[i]=0;
|
||
|
waapisimWrittenpos=0;
|
||
|
waapisimNodeId=0;
|
||
|
waapisimContexts=[];
|
||
|
waapisimAudioBuffer=function(ch,len,rate) {
|
||
|
var i,j;
|
||
|
if(typeof(ch)=="number") {
|
||
|
len|=0;
|
||
|
this.sampleRate=rate;
|
||
|
this.length=len;
|
||
|
this.duration=len/this.sampleRate;
|
||
|
this.numberOfChannels=ch;
|
||
|
this.buf=[];
|
||
|
for(i=0;i<2;++i) {
|
||
|
this.buf[i]=new Float32Array(len);
|
||
|
for(j=0;j<len;++j)
|
||
|
this.buf[i][j]=0;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
var inbuf;
|
||
|
this.sampleRate=44100;
|
||
|
this.buf=[];
|
||
|
this.buf[0]=new Float32Array(0);
|
||
|
this.buf[1]=new Float32Array(0);
|
||
|
this.Get4BStr=function(b,n) {
|
||
|
return String.fromCharCode(b[n],b[n+1],b[n+2],b[n+3]);
|
||
|
};
|
||
|
this.GetDw=function(b,n) {
|
||
|
return b[n]+(b[n+1]<<8)+(b[n+2]<<16)+(b[n+3]<<24);
|
||
|
};
|
||
|
this.GetWd=function(b,n) {
|
||
|
return b[n]+(b[n+1]<<8);
|
||
|
};
|
||
|
inbuf=new Uint8Array(ch);
|
||
|
var mixtomono=len;
|
||
|
var riff=this.Get4BStr(inbuf,0);
|
||
|
this.length=0;
|
||
|
if(riff=="RIFF") {
|
||
|
var filesize=this.GetDw(inbuf,4)+8;
|
||
|
var wave=this.Get4BStr(inbuf,8);
|
||
|
var fmtid=0;
|
||
|
var wavch=1;
|
||
|
var wavbits=16;
|
||
|
if(wave=="WAVE") {
|
||
|
var idx=12;
|
||
|
while(idx<filesize) {
|
||
|
var chunk=this.Get4BStr(inbuf,idx);
|
||
|
var chunksz=this.GetDw(inbuf,idx+4);
|
||
|
if(chunk=="fmt ") {
|
||
|
fmtid=this.GetWd(inbuf,idx+8);
|
||
|
wavch=this.GetWd(inbuf,idx+10);
|
||
|
this.sampleRate=this.GetDw(inbuf,idx+12);
|
||
|
wavbits=this.GetWd(inbuf,idx+22);
|
||
|
}
|
||
|
if(chunk=="data") {
|
||
|
this.length=(chunksz/wavch/(wavbits/8))|0;
|
||
|
this.buf[0]=new Float32Array(this.length);
|
||
|
this.buf[1]=new Float32Array(this.length);
|
||
|
this.numberOfChannels=wavch;
|
||
|
this.duration=this.length/this.sampleRate;
|
||
|
var v0,v1;
|
||
|
for(i=0,j=0;i<this.length;++i) {
|
||
|
if(wavbits==24) {
|
||
|
if(wavch==2) {
|
||
|
v0=inbuf[idx+j+9]+(inbuf[idx+j+10]<<8);
|
||
|
v1=inbuf[idx+j+12]+(inbuf[idx+j+13]<<8);
|
||
|
if(v0>=32768) v0=v0-65536;
|
||
|
if(v1>=32768) v1=v1-65536;
|
||
|
if(mixtomono===true)
|
||
|
v0=v1=(v0+v1)*0.5;
|
||
|
this.buf[0][i]=v0/32768;
|
||
|
this.buf[1][i]=v1/32768;
|
||
|
j+=6;
|
||
|
}
|
||
|
else {
|
||
|
v=inbuf[idx+j+9]+(inbuf[idx+j+10]<<8);
|
||
|
if(v>=32768) v=v-65536;
|
||
|
this.buf[0][i]=this.buf[1][i]=v/32768;
|
||
|
j+=3;
|
||
|
}
|
||
|
}
|
||
|
else if(wavbits==16) {
|
||
|
if(wavch==2) {
|
||
|
v0=inbuf[idx+j+8]+(inbuf[idx+j+9]<<8);
|
||
|
v1=inbuf[idx+j+10]+(inbuf[idx+j+11]<<8);
|
||
|
if(v0>=32768) v0=v0-65536;
|
||
|
if(v1>=32768) v1=v1-65536;
|
||
|
if(mixtomono===true)
|
||
|
v0=v1=(v0+v1)*0.5;
|
||
|
this.buf[0][i]=v0/32768;
|
||
|
this.buf[1][i]=v1/32768;
|
||
|
j+=4;
|
||
|
}
|
||
|
else {
|
||
|
v=inbuf[idx+j+8]+(inbuf[idx+j+9]<<8);
|
||
|
if(v>=32768) v=v-65536;
|
||
|
this.buf[0][i]=this.buf[1][i]=v/32768;
|
||
|
j+=2;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if(wavch==2) {
|
||
|
v0=inbuf[idx+j+8]/128-1;
|
||
|
v1=inbuf[idx+j+9]/128-1;
|
||
|
if(mixtomono===true)
|
||
|
v0=v1=(v0+v1)*0.5;
|
||
|
this.buf[0][i]=v0;
|
||
|
this.buf[1][i]=v1;
|
||
|
j+=2;
|
||
|
}
|
||
|
else {
|
||
|
this.buf[0][i]=this.buf[1][i]=inbuf[idx+j+8]/128-1;
|
||
|
j++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
idx+=(chunksz+8);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
this.getChannelData=function(i) {
|
||
|
return this.buf[i];
|
||
|
};
|
||
|
};
|
||
|
waapisimDummybuf=new waapisimAudioBuffer(2,waapisimBufSize,waapisimSampleRate);
|
||
|
waapisimSetupOutBuf=function(offset) {
|
||
|
var numctx=waapisimContexts.length;
|
||
|
var l,i,j,k,n,node;
|
||
|
for(l=(offset+waapisimBufSize)*2,i=offset*2;i<l;i+=2)
|
||
|
waapisimOutBuf[i]=waapisimOutBuf[i+1]=0;
|
||
|
for(n=0;n<numctx;++n) {
|
||
|
var ctx=waapisimContexts[n];
|
||
|
for(;;) {
|
||
|
for(l=ctx._Nodes.length,i=0;i<l;++i) {
|
||
|
node=ctx._Nodes[i];
|
||
|
if(node.playbackState==3) {
|
||
|
node.disconnect();
|
||
|
ctx._UnregisterNode(node);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(i==l)
|
||
|
break;
|
||
|
}
|
||
|
for(l=ctx._Nodes.length,i=0;i<l;++i)
|
||
|
ctx._Nodes[i]._Process();
|
||
|
node=ctx.destination;
|
||
|
if(node._nodein[0].from.length>0) {
|
||
|
var buf=node._nodein[0].inbuf.buf;
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
waapisimOutBuf[(i+offset)*2]+=buf[0][i];
|
||
|
waapisimOutBuf[(i+offset)*2+1]+=buf[1][i];
|
||
|
}
|
||
|
}
|
||
|
node._nodein[0].NodeClear();
|
||
|
}
|
||
|
};
|
||
|
waapisimUpdateCurrentTime=function(t) {
|
||
|
for(var i=waapisimContexts.length;i--;)
|
||
|
waapisimContexts[i].currentTime=t;
|
||
|
};
|
||
|
waapisimInterval=function() {
|
||
|
var curpos=waapisimAudio.mozCurrentSampleOffset();
|
||
|
var buffered=waapisimWrittenpos-curpos;
|
||
|
var vl,vr;
|
||
|
waapisimUpdateCurrentTime(curpos/(waapisimSampleRate*2));
|
||
|
if(buffered<16384) {
|
||
|
waapisimSetupOutBuf(0);
|
||
|
waapisimWrittenpos+=waapisimAudio.mozWriteAudio(waapisimOutBuf);
|
||
|
}
|
||
|
};
|
||
|
waapisimGetSwfPath=function() {
|
||
|
var scr=document.getElementsByTagName("SCRIPT");
|
||
|
if(scr&&scr.length>0) {
|
||
|
for(var i in scr) {
|
||
|
if(scr[i].src && scr[i].src.match(/waapisim\.js$/)) {
|
||
|
var s=scr[i].src;
|
||
|
return s.substring(0,s.length-2)+"swf";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return "";
|
||
|
};
|
||
|
waapisimAddFlashObj=function() {
|
||
|
var div=document.createElement("DIV");
|
||
|
div.setAttribute("id","WAAPISIMFLASHOBJ");
|
||
|
div.setAttribute("style","background:#ff00ff;position:static;");
|
||
|
var body=document.getElementsByTagName("BODY");
|
||
|
body[0].appendChild(div);
|
||
|
document.getElementById("WAAPISIMFLASHOBJ").innerHTML="<div style='position:fixed;right:0px;bottom:0px'> <object id='waapisim_swf' CLASSID='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' CODEBASE='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=4,0,0,0' width=150 height=20><param name=movie value='"+waapisimSwfPath+"'><PARAM NAME=bgcolor VALUE=#FFFFFF><PARAM NAME=LOOP VALUE=false><PARAM NAME=quality VALUE=high><param name='allowScriptAccess' value='always'><embed src='"+waapisimSwfPath+"' width=150 height=20 bgcolor=#FFFFFF loop=false quality=high pluginspage='http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash' type='application/x-shockwave-flash' allowScriptAccess='always'></embed></object></div>";
|
||
|
if(typeof(document.getElementById("waapisim_swf").SetReturnValue)==="undefined")
|
||
|
document.getElementById("waapisim_swf").SetReturnValue=function(v){document.getElementById("waapisim_swf").impl.SetReturnValue(v);};
|
||
|
};
|
||
|
waapisimFlashOffset=function(pos) {
|
||
|
waapisimUpdateCurrentTime(pos/1000);
|
||
|
};
|
||
|
waapisimFlashGetData=function() {
|
||
|
var s="";
|
||
|
var l;
|
||
|
for(l=waapisimOutBufSize/waapisimBufSize,i=0;i<l;++i) {
|
||
|
waapisimSetupOutBuf(waapisimBufSize*i);
|
||
|
}
|
||
|
waapisimWrittenpos+=waapisimOutBufSize*2;
|
||
|
for(l=waapisimOutBufSize*2,i=0;i<l;++i) {
|
||
|
var v=(waapisimOutBuf[i]*16384+32768)|0;
|
||
|
if(isNaN(v)) v=32768;
|
||
|
v = Math.min(49152, Math.max(16384, v));
|
||
|
s+=String.fromCharCode(v);
|
||
|
}
|
||
|
return s;
|
||
|
};
|
||
|
switch(waapisimAudioIf) {
|
||
|
case 0:
|
||
|
waapisimSwfPath=waapisimGetSwfPath();
|
||
|
window.addEventListener("load",waapisimAddFlashObj,false);
|
||
|
break;
|
||
|
case 1:
|
||
|
setInterval(waapisimInterval,10);
|
||
|
break;
|
||
|
}
|
||
|
AudioContext=webkitAudioContext=function() {
|
||
|
waapisimContexts.push(this);
|
||
|
this._Nodes=[];
|
||
|
this.destination=new waapisimAudioDestinationNode(this);
|
||
|
this.sampleRate=44100;
|
||
|
this.currentTime=0;
|
||
|
this.activeSourceCount=0;
|
||
|
this.listener=new waapisimAudioListener();
|
||
|
this.createBuffer=function(ch,len,rate) {
|
||
|
return new waapisimAudioBuffer(ch,len,rate);
|
||
|
};
|
||
|
this.createBufferSource=function() {
|
||
|
return new waapisimAudioBufferSource(this);
|
||
|
};
|
||
|
this.createScriptProcessor=this.createJavaScriptNode=function(bufsize,inch,outch) {
|
||
|
return new waapisimScriptProcessor(this,bufsize,inch,outch);
|
||
|
};
|
||
|
this.createBiquadFilter=function() {
|
||
|
return new waapisimBiquadFilter(this);
|
||
|
};
|
||
|
this.createGain=this.createGainNode=function() {
|
||
|
return new waapisimGain(this);
|
||
|
};
|
||
|
this.createDelay=this.createDelayNode=function() {
|
||
|
return new waapisimDelay(this);
|
||
|
};
|
||
|
this.createOscillator=function() {
|
||
|
return new waapisimOscillator(this);
|
||
|
};
|
||
|
this.createAnalyser=function() {
|
||
|
return new waapisimAnalyser(this);
|
||
|
};
|
||
|
this.createConvolver=function() {
|
||
|
return new waapisimConvolver(this);
|
||
|
};
|
||
|
this.createDynamicsCompressor=function() {
|
||
|
return new waapisimDynamicsCompressor(this);
|
||
|
};
|
||
|
this.createPanner=function() {
|
||
|
return new waapisimPanner(this);
|
||
|
};
|
||
|
this.createChannelSplitter=function(ch) {
|
||
|
return new waapisimChannelSplitter(this,ch);
|
||
|
};
|
||
|
this.createChannelMerger=function(ch) {
|
||
|
return new waapisimChannelMerger(this,ch);
|
||
|
};
|
||
|
this.createWaveShaper=function() {
|
||
|
return new waapisimWaveShaper(this);
|
||
|
};
|
||
|
this.decodeAudioData=function(audioData,successCallback,errorCallback) {
|
||
|
var buf=new waapisimAudioBuffer(audioData,false);
|
||
|
if(buf.length>0)
|
||
|
successCallback(buf);
|
||
|
else
|
||
|
errorCallback();
|
||
|
};
|
||
|
this.createPeriodicWave=this.createWaveTable=function(real,imag) {
|
||
|
return new waapisimPeriodicWave(real,imag);
|
||
|
};
|
||
|
this._SortNode=function() {
|
||
|
var i,j,k,n;
|
||
|
for(i=0;i<this._Nodes.length;++i) {
|
||
|
n=this._Nodes[i];
|
||
|
if(n._order>0) {
|
||
|
for(j=0;j<n._nodein.length;++j) {
|
||
|
for(k=0;k<n._nodein[j].from.length;++k) {
|
||
|
var o=n._nodein[j].from[k].node._order;
|
||
|
if(n._order<o+1)
|
||
|
n._order=o+1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
this._Nodes.sort(function(a,b){return b._order-a._order;});
|
||
|
}
|
||
|
this._RegisterNode=function(node) {
|
||
|
for(var i=this._Nodes.length;i--;) {
|
||
|
if(this._Nodes[i]===node) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
this._Nodes.push(node);
|
||
|
this._SortNode();
|
||
|
return true;
|
||
|
};
|
||
|
this._UnregisterNode=function(node) {
|
||
|
for(var i=this._Nodes.length;i--;) {
|
||
|
if(this._Nodes[i]==node) {
|
||
|
this._Nodes.splice(i,1);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
waapisimAudioListener=function() {
|
||
|
this.px=0; this.py=0; this.pz=0;
|
||
|
this.ox=0; this.oy=0; this.oz=-1;
|
||
|
this.ux=0; this.uy=1; this.uz=0;
|
||
|
this.dopplerFactor=1;
|
||
|
this.speedOfSound=343.3;
|
||
|
this.setPosition=function(x,y,z) {this.px=x;this.py=y;this.pz=z;};
|
||
|
this.setOrientation=function(x,y,z,ux,uy,uz) {this.ox=x;this.oy=y;this.oz=z;this.ux=ux;this.uy=uy;this.uz=uz;};
|
||
|
this.setVelocity=function(x,y,z) {};
|
||
|
};
|
||
|
waapisimPeriodicWave=function(real,imag) {
|
||
|
var n=4096;
|
||
|
var ar=new Array(n);
|
||
|
var ai=new Array(n);
|
||
|
this.buf=new Float32Array(n);
|
||
|
var m, mh, i, j, k;
|
||
|
var wr, wi, xr, xi;
|
||
|
for(i=0;i<n;++i)
|
||
|
ar[i]=ai[i]=0;
|
||
|
i=j=0;
|
||
|
do {
|
||
|
ar[i]=real[j];
|
||
|
ai[i]=-imag[j];
|
||
|
for(var k=n>>1;k>(i^=k);k>>=1)
|
||
|
;
|
||
|
} while(++j<real.length);
|
||
|
var theta=2*Math.PI;
|
||
|
for(mh=1;(m=mh<<1)<=n;mh=m) {
|
||
|
theta *= 0.5;
|
||
|
for(i=0;i<mh;i++) {
|
||
|
wr=Math.cos(theta*i);
|
||
|
wi=Math.sin(theta*i);
|
||
|
for(j=i;j<n;j+=m) {
|
||
|
k=j+mh;
|
||
|
xr=wr*ar[k]-wi*ai[k];
|
||
|
xi=wr*ai[k]+wi*ar[k];
|
||
|
ar[k]=ar[j]-xr;
|
||
|
ai[k]=ai[j]-xi;
|
||
|
ar[j]+=xr;
|
||
|
ai[j]+=xi;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
var max=0;
|
||
|
for(i=0;i<n;++i) {
|
||
|
var v=Math.abs(ar[i]);
|
||
|
if(v>max)
|
||
|
max=v;
|
||
|
}
|
||
|
if(max==0) {
|
||
|
for(i=0;i<n;++i)
|
||
|
this.buf[i]=0;
|
||
|
}
|
||
|
else {
|
||
|
for(i=0;i<n;++i)
|
||
|
this.buf[i]=ar[i]/max;
|
||
|
}
|
||
|
};
|
||
|
waapisimAudioNode=function(size,numin,numout) {
|
||
|
this.numberOfInputs=numin;
|
||
|
this.numberOfOutputs=numout;
|
||
|
this._nodeId=waapisimNodeId;
|
||
|
this._order=1;
|
||
|
++waapisimNodeId;
|
||
|
this._targettype=1;
|
||
|
this.context=null;
|
||
|
this._nodein=[];
|
||
|
this._nodeout=[];
|
||
|
var i;
|
||
|
for(i=0;i<numin;++i)
|
||
|
this._nodein[i]=new waapisimAudioNodeIn(this,size);
|
||
|
for(i=0;i<numout;++i)
|
||
|
this._nodeout[i]=new waapisimAudioNodeOut(this,size);
|
||
|
this.connect=function(next,output,input) {
|
||
|
if(typeof(output)==="undefined")
|
||
|
output=0;
|
||
|
if(typeof(input)==="undefined")
|
||
|
input=0;
|
||
|
if(this._nodeout[output]) {
|
||
|
if(next._targettype!==0)
|
||
|
this._nodeout[output].connect(next._nodein[input]);
|
||
|
else
|
||
|
this._nodeout[output].connect(next);
|
||
|
}
|
||
|
};
|
||
|
this.disconnect=function(output) {
|
||
|
if(typeof(this._nodeout[output])==="undefined")
|
||
|
output=0;
|
||
|
this._nodeout[output].disconnect();
|
||
|
};
|
||
|
};
|
||
|
waapisimAudioNodeIn=function(node,size) {
|
||
|
this.node=node;
|
||
|
this.from=[];
|
||
|
this.inbuf=new waapisimAudioBuffer(2,size,waapisimSampleRate);
|
||
|
this.NodeClear=function() {
|
||
|
for(var i=0;i<waapisimBufSize;++i)
|
||
|
this.inbuf.buf[0][i]=this.inbuf.buf[1][i]=0;
|
||
|
};
|
||
|
};
|
||
|
waapisimAudioNodeOut=function(node,size) {
|
||
|
this.node=node;
|
||
|
this.to=[];
|
||
|
|
||
|
this.connect=function(next) {
|
||
|
waapisimDebug("connect "+this.node._nodetype+this.node._nodeId+"=>"+next.node._nodetype+next.node._nodeId);
|
||
|
if(next===undefined)
|
||
|
return;
|
||
|
if(next.from.indexOf(this)!=-1)
|
||
|
return;
|
||
|
next.from.push(this);
|
||
|
if(this.to.indexOf(next)==-1)
|
||
|
this.to.push(next);
|
||
|
if(next.node._targettype!==0) {
|
||
|
if(this.node.context._RegisterNode(next.node)) {
|
||
|
for(var i=0;i<next.node._nodeout.length;++i) {
|
||
|
for(var ii=0;ii<next.node._nodeout[i].to.length;++ii) {
|
||
|
next.node._nodeout[i].connect(next.node._nodeout[i].to[ii]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
this.disconnectTemp=function() {
|
||
|
var i,j,k,l,n,ii,jj,ll,node,node2;
|
||
|
waapisimDebug("disconnect "+this.node._nodetype+this.node._nodeId);
|
||
|
var nodes=this.node.context._Nodes;
|
||
|
for(l=nodes.length,i=0;i<l;++i) {
|
||
|
for(ll=nodes[i]._nodein.length,ii=0;ii<ll;++ii) {
|
||
|
j=nodes[i]._nodein[ii].from.indexOf(this);
|
||
|
if(j>=0) {
|
||
|
waapisimDebug(" :"+this.node._nodeId+"=>"+nodes[i]._nodeId);
|
||
|
nodes[i]._nodein[ii].from.splice(j,1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for(i=0;i<nodes.length;++i) {
|
||
|
node=nodes[i];
|
||
|
if(node._targettype==1) {
|
||
|
n=0;
|
||
|
for(ii=0;ii<node._nodein.length;++ii)
|
||
|
n+=node._nodein[ii].from.length;
|
||
|
if(n===0) {
|
||
|
this.node.context._UnregisterNode(node);
|
||
|
for(ii=0;ii<node._nodeout.length;++ii)
|
||
|
node._nodeout[ii].disconnectTemp();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
this.disconnect=function() {
|
||
|
this.disconnectTemp();
|
||
|
this.to.length=0;
|
||
|
};
|
||
|
this.NodeEmit=function(idx,v1,v2) {
|
||
|
for(var l=this.to.length,i=0;i<l;++i) {
|
||
|
var buf=this.to[i].inbuf.buf;
|
||
|
buf[0][idx]+=v1;
|
||
|
buf[1][idx]+=v2;
|
||
|
}
|
||
|
};
|
||
|
this.NodeEmitBuf=function() {
|
||
|
for(var l=this.to.length,i=0;i<l;++i) {
|
||
|
var b0=this.to[i].inbuf.buf[0];
|
||
|
var b1=this.to[i].inbuf.buf[1];
|
||
|
for(var j=0;j<waapisimBufSize;++j) {
|
||
|
b0[j]+=this.outbuf.buf[0][j];
|
||
|
b1[j]+=this.outbuf.buf[1][j];
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
this.outbuf=new waapisimAudioBuffer(2,size,waapisimSampleRate);
|
||
|
};
|
||
|
waapisimAudioProcessingEvent=function() {
|
||
|
};
|
||
|
waapisimAudioDestinationNode=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,0);
|
||
|
this._nodetype="Destination";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this._targettype=2;
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.maxNumberOfChannels=2;
|
||
|
this.numberOfChannels=2;
|
||
|
this._Process=function() {};
|
||
|
ctx._Nodes.push(this);
|
||
|
};
|
||
|
|
||
|
waapisimAudioBufferSource=webkitAudioBufferSourceNode=AudioBufferSourceNode=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,0,1);
|
||
|
this._nodetype="BufSrc";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this._targettype=3;
|
||
|
this._order=0;
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.buffer=null;
|
||
|
this.playbackRate=new waapisimAudioParam(ctx,this,0,10,1);
|
||
|
this.gain=new waapisimAudioParam(ctx,this,0,1,1); // Undocumented
|
||
|
this.loop=false;
|
||
|
this.loopStart=0;
|
||
|
this.loopEnd=0;
|
||
|
this._bufferindex=0;
|
||
|
this._whenstart=0;
|
||
|
this._whenstop=Number.MAX_VALUE;
|
||
|
this._endindex=0;
|
||
|
this._actualLoopStart=0;
|
||
|
this._actualLoopEnd=0;
|
||
|
this.start=this.noteOn=this.noteGrainOn=function(w,off,dur) {
|
||
|
if(this.buffer===null)
|
||
|
return;
|
||
|
this.playbackState=1;
|
||
|
this._whenstart=w;
|
||
|
if(off>0)
|
||
|
this._bufferindex=off*waapisimSampleRate;
|
||
|
this._endindex=this.buffer.length;
|
||
|
if(dur>0)
|
||
|
this._endindex=Math.min(this.buffer.length,(dur+off)*waapisimSampleRate);
|
||
|
if(this.loop) {
|
||
|
if((this.loopStart||this.loopEnd)&&this.loopStart>=0&&this.loopEnd>0&&this.loopStart<this.loopEnd) {
|
||
|
this._actualLoopStart=this.loopStart;
|
||
|
this._actualLoopEnd=Math.min(this.loopEnd,this.buffer.length);
|
||
|
}
|
||
|
else {
|
||
|
this._actualLoopStart=0;
|
||
|
this._actualLoopEnd=this.buffer.length;
|
||
|
}
|
||
|
}
|
||
|
this.context._RegisterNode(this);
|
||
|
};
|
||
|
this.stop=this.noteOff=function(w) {
|
||
|
this._whenstop=w;
|
||
|
};
|
||
|
this._Process=function() {
|
||
|
this.playbackRate._Process();
|
||
|
if(this.buffer!==null && this._bufferindex>=this._endindex) {
|
||
|
if(this.playbackState==2)
|
||
|
--this.context.activeSourceCount;
|
||
|
this.playbackState=3;
|
||
|
}
|
||
|
if(this.playbackState==1 && this.context.currentTime>=this._whenstart) {
|
||
|
this.playbackState=2;
|
||
|
++this.context.activeSourceCount;
|
||
|
}
|
||
|
if(this.playbackState==2 && this.context.currentTime>=this._whenstop) {
|
||
|
this.playbackState=3;
|
||
|
--this.context.activeSourceCount;
|
||
|
}
|
||
|
if(this.playbackState!=2)
|
||
|
return;
|
||
|
var b0=this.buffer.getChannelData(0);
|
||
|
var b1=this.buffer.getChannelData(1);
|
||
|
var rate=this.buffer.sampleRate/44100;
|
||
|
if(this._nodeout[0].to.length>0) {
|
||
|
for(var i=0;i<waapisimBufSize;++i) {
|
||
|
if(this._bufferindex<this._endindex) {
|
||
|
var g=this.gain.Get(i);
|
||
|
var idx=this._bufferindex|0;
|
||
|
this._nodeout[0].outbuf.buf[0][i]=b0[idx]*g;
|
||
|
this._nodeout[0].outbuf.buf[1][i]=b1[idx]*g;
|
||
|
}
|
||
|
this._bufferindex+=rate*this.playbackRate.Get(i);
|
||
|
if(this.loop) {
|
||
|
if(this._bufferindex>=this._actualLoopEnd)
|
||
|
this._bufferindex=this._actualLoopStart;
|
||
|
}
|
||
|
}
|
||
|
this._nodeout[0].NodeEmitBuf();
|
||
|
this.playbackRate.Clear(true);
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
waapisimAudioBufferSource.UNSCHEDULED_STATE=waapisimAudioBufferSource.prototype.UNSCHEDULED_STATE=0;
|
||
|
waapisimAudioBufferSource.SCHEDULED_STATE=waapisimAudioBufferSource.prototype.SCHEDULED_STATE=1;
|
||
|
waapisimAudioBufferSource.PLAYING_STATE=waapisimAudioBufferSource.prototype.PLAYING_STATE=2;
|
||
|
waapisimAudioBufferSource.FINISHED_STATE=waapisimAudioBufferSource.prototype.FINISHED_STATE=3;
|
||
|
|
||
|
waapisimScriptProcessor=function(ctx,bufsize,inch,outch) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="ScrProc";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this._targettype=2;
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
if(typeof(bufsize)!=="number")
|
||
|
throw(new TypeError("ScriptProcessor:bufferSize"));
|
||
|
if(typeof(inch)==="undefined")
|
||
|
inch=2;
|
||
|
if(typeof(outch)==="undefined")
|
||
|
outch=2;
|
||
|
this.bufferSize=bufsize;
|
||
|
this._scrinbuf=new waapisimAudioBuffer(inch,bufsize,waapisimSampleRate);
|
||
|
this._scroutbuf=new waapisimAudioBuffer(outch,bufsize,waapisimSampleRate);
|
||
|
this._index=bufsize;
|
||
|
this.onaudioprocess=null;
|
||
|
this._Process=function() {
|
||
|
var inb=this._nodein[0].inbuf;
|
||
|
if(inb===null)
|
||
|
inb=waapisimDummybuf;
|
||
|
for(var i=0;i<waapisimBufSize;++i) {
|
||
|
if(this._index>=this.bufferSize) {
|
||
|
if(this.onaudioprocess) {
|
||
|
var ev=new waapisimAudioProcessingEvent();
|
||
|
ev.node=this;
|
||
|
ev.inputBuffer=this._scrinbuf;
|
||
|
ev.outputBuffer=this._scroutbuf;
|
||
|
this.onaudioprocess(ev);
|
||
|
}
|
||
|
this._index=0;
|
||
|
}
|
||
|
this._scrinbuf.buf[0][this._index]=inb.buf[0][i];
|
||
|
if(this._scrinbuf.numberOfChannels>=2)
|
||
|
this._scrinbuf.buf[1][this._index]=inb.buf[1][i];
|
||
|
if(this._scroutbuf.numberOfChannels>=2)
|
||
|
this._nodeout[0].NodeEmit(i,this._scroutbuf.buf[0][this._index],this._scroutbuf.buf[1][this._index]);
|
||
|
else
|
||
|
this._nodeout[0].NodeEmit(i,this._scroutbuf.buf[0][this._index],this._scroutbuf.buf[0][this._index]);
|
||
|
this._index++;
|
||
|
}
|
||
|
this._nodein[0].NodeClear();
|
||
|
};
|
||
|
ctx._RegisterNode(this);
|
||
|
};
|
||
|
waapisimBiquadFilter=webkitBiquadFilterNode=BiquadFilterNode=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="Filter";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.type=0;
|
||
|
this.frequency=new waapisimAudioParam(ctx,this,10,24000,350,0.5);
|
||
|
this.detune=new waapisimAudioParam(ctx,this,-4800,4800,0,0.5);
|
||
|
this.Q=new waapisimAudioParam(ctx,this,0.0001,1000,1,0.5);
|
||
|
this.gain=new waapisimAudioParam(ctx,this,-40,40,0,0.5);
|
||
|
this._a1=this._a2=0;
|
||
|
this._b0=this._b1=this._b2=0;
|
||
|
this._x1l=this._x1r=this._x2l=this._x2r=0;
|
||
|
this._y1l=this._y1r=this._y2l=this._y2r=0;
|
||
|
this._nodein[0].NodeClear();
|
||
|
this._Setup=function(fil) {
|
||
|
var f=fil.frequency.Get(0)*Math.pow(2,fil.detune.Get(0)/1200);
|
||
|
var q=Math.max(0.001,fil.Q.Get(0));
|
||
|
var alpha,ra0,g;
|
||
|
var w0=2*Math.PI*f/fil.context.sampleRate;
|
||
|
var cos=Math.cos(w0);
|
||
|
switch(fil.type) {
|
||
|
case "lowpass":
|
||
|
case 0:
|
||
|
q=Math.pow(10,q/20);
|
||
|
alpha=Math.sin(w0)/(2*q);
|
||
|
ra0=1/(1+alpha);
|
||
|
fil._a1=-2*cos*ra0;
|
||
|
fil._a2=(1-alpha)*ra0;
|
||
|
fil._b0=fil._b2=(1-cos)/2*ra0;
|
||
|
fil._b1=(1-cos)*ra0;
|
||
|
break;
|
||
|
case "highpass":
|
||
|
case 1:
|
||
|
q=Math.pow(10,q/20);
|
||
|
alpha=Math.sin(w0)/(2*q);
|
||
|
ra0=1/(1+alpha);
|
||
|
fil._a1=-2*cos*ra0;
|
||
|
fil._a2=(1-alpha)*ra0;
|
||
|
fil._b0=fil._b2=(1+cos)/2*ra0;
|
||
|
fil._b1=-(1+cos)*ra0;
|
||
|
break;
|
||
|
case "bandpass":
|
||
|
case 2:
|
||
|
alpha=Math.sin(w0)/(2*q);
|
||
|
ra0=1/(1+alpha);
|
||
|
fil._a1=-2*cos*ra0;
|
||
|
fil._a2=(1-alpha)*ra0;
|
||
|
fil._b0=alpha;
|
||
|
fil._b1=0;
|
||
|
fil._b2=-alpha;
|
||
|
break;
|
||
|
case "lowshelf":
|
||
|
case 3:
|
||
|
alpha=Math.sin(w0)/2*Math.sqrt(2);
|
||
|
g=Math.pow(10,fil.gain.Get(0)/40);
|
||
|
ra0=1/((g+1)+(g-1)*cos+2*Math.sqrt(g)*alpha);
|
||
|
fil._a1=-2*((g-1)+(g+1)*cos)*ra0;
|
||
|
fil._a2=((g+1)+(g-1)*cos-2*Math.sqrt(g)*alpha)*ra0;
|
||
|
fil._b0=g*((g+1)-(g-1)*cos+2*Math.sqrt(g)*alpha)*ra0;
|
||
|
fil._b1=2*g*((g-1)-(g+1)*cos)*ra0;
|
||
|
fil._b2=g*((g+1)-(g-1)*cos-2*Math.sqrt(g)*alpha)*ra0;
|
||
|
break;
|
||
|
case "highshelf":
|
||
|
case 4:
|
||
|
alpha=Math.sin(w0)/2*Math.sqrt(2);
|
||
|
g=Math.pow(10,fil.gain.Get(0)/40);
|
||
|
ra0=1/((g+1)-(g-1)*cos+2*Math.sqrt(g)*alpha);
|
||
|
fil._a1=2*((g-1)-(g+1)*cos)*ra0;
|
||
|
fil._a2=((g+1)-(g-1)*cos-2*Math.sqrt(g)*alpha)*ra0;
|
||
|
fil._b0=g*((g+1)+(g-1)*cos+2*Math.sqrt(g)*alpha)*ra0;
|
||
|
fil._b1=-2*g*((g-1)+(g+1)*cos)*ra0;
|
||
|
fil._b2=g*((g+1)+(g-1)*cos-2*Math.sqrt(g)*alpha)*ra0;
|
||
|
break;
|
||
|
case "peaking":
|
||
|
case 5:
|
||
|
alpha=Math.sin(w0)/(2*q);
|
||
|
g=Math.pow(10,fil.gain.Get(0)/40);
|
||
|
ra0=1/(1+alpha/g);
|
||
|
fil._a1=-2*cos*ra0;
|
||
|
fil._a2=(1-alpha/g)*ra0;
|
||
|
fil._b0=(1+alpha*g)*ra0;
|
||
|
fil._b1=-2*cos*ra0;
|
||
|
fil._b2=(1-alpha*g)*ra0;
|
||
|
break;
|
||
|
case "notch":
|
||
|
case 6:
|
||
|
alpha=Math.sin(w0)/(2*q);
|
||
|
ra0=1/(1+alpha);
|
||
|
fil._a1=-2*cos*ra0;
|
||
|
fil._a2=(1-alpha)*ra0;
|
||
|
fil._b0=fil._b2=ra0;
|
||
|
fil._b1=-2*cos*ra0;
|
||
|
break;
|
||
|
case "allpass":
|
||
|
case 7:
|
||
|
alpha=Math.sin(w0)/(2*q);
|
||
|
ra0=1/(1+alpha);
|
||
|
fil._a1=-2*cos*ra0;
|
||
|
fil._a2=(1-alpha)*ra0;
|
||
|
fil._b0=(1-alpha)*ra0;
|
||
|
fil._b1=-2*cos*ra0;
|
||
|
fil._b2=(1+alpha)*ra0;
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
this._Process=function() {
|
||
|
var xl,xr,yl,yr;
|
||
|
this.frequency._Process();
|
||
|
this.detune._Process();
|
||
|
this.Q._Process();
|
||
|
this.gain._Process();
|
||
|
this._Setup(this);
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
var outbuf=this._nodeout[0].outbuf.buf;
|
||
|
for(var i=0;i<waapisimBufSize;++i) {
|
||
|
xl=inbuf[0][i];
|
||
|
xr=inbuf[1][i];
|
||
|
yl=this._b0*xl+this._b1*this._x1l+this._b2*this._x2l-this._a1*this._y1l-this._a2*this._y2l;
|
||
|
yr=this._b0*xr+this._b1*this._x1r+this._b2*this._x2r-this._a1*this._y1r-this._a2*this._y2r;
|
||
|
this._x2l=this._x1l; this._x2r=this._x1r;
|
||
|
this._x1l=xl; this._x1r=xr;
|
||
|
this._y2l=this._y1l; this._y2r=this._y1r;
|
||
|
this._y1l=yl; this._y1r=yr;
|
||
|
outbuf[0][i]=yl;
|
||
|
outbuf[1][i]=yr;
|
||
|
}
|
||
|
this._nodeout[0].NodeEmitBuf();
|
||
|
this._nodein[0].NodeClear();
|
||
|
this.frequency.Clear(false);
|
||
|
this.detune.Clear(false);
|
||
|
this.Q.Clear(false);
|
||
|
this.gain.Clear(false);
|
||
|
};
|
||
|
this.getFrequencyResponse=function(f,m,p) {
|
||
|
for(var l=f.length,i=0;i<l;++i) {
|
||
|
var w=2*Math.PI*f[i]/this.context.sampleRate;
|
||
|
var cw=Math.cos(w);
|
||
|
var cw2=Math.cos(w*2);
|
||
|
var sw=Math.sin(w);
|
||
|
var sw2=Math.sin(w*2);
|
||
|
var ca=1+this._a1*cw+this._a2*cw2;
|
||
|
var sa=this._a1*sw+this._a2*sw2;
|
||
|
var cb=this._b0+this._b1*cw+this._b2*cw2;
|
||
|
var sb=this._b1*sw+this._b2*sw2;
|
||
|
m[i]=Math.sqrt((cb*cb+sb*sb)/(ca*ca+sa*sa));
|
||
|
p[i]=Math.atan2(sa,ca)-Math.atan2(sb,cb);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
waapisimBiquadFilter.LOWPASS=waapisimBiquadFilter.prototype.LOWPASS=0;
|
||
|
waapisimBiquadFilter.HIGHPASS=waapisimBiquadFilter.prototype.HIGHPASS=1;
|
||
|
waapisimBiquadFilter.BANDPASS=waapisimBiquadFilter.prototype.BANDPASS=2;
|
||
|
waapisimBiquadFilter.LOWSHELF=waapisimBiquadFilter.prototype.LOWSHELF=3;
|
||
|
waapisimBiquadFilter.HIGHSHELF=waapisimBiquadFilter.prototype.HIGHSHELF=4;
|
||
|
waapisimBiquadFilter.PEAKING=waapisimBiquadFilter.prototype.PEAKING=5;
|
||
|
waapisimBiquadFilter.NOTCH=waapisimBiquadFilter.prototype.NOTCH=6;
|
||
|
waapisimBiquadFilter.ALLPASS=waapisimBiquadFilter.prototype.ALLPASS=7;
|
||
|
|
||
|
waapisimGain=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="Gain";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.gain=new waapisimAudioParam(ctx,this,0,1,1);
|
||
|
this._nodein[0].NodeClear();
|
||
|
this._curgain=1;
|
||
|
this._Process=function() {
|
||
|
var i;
|
||
|
this.gain._Process();
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
switch(this._nodeout[0].to.length) {
|
||
|
case 0:
|
||
|
this._curgain=this.gain.Get(0);
|
||
|
break;
|
||
|
case 1:
|
||
|
var b=this._nodeout[0].to[0].inbuf.buf;
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
var g=this.gain.Get(i);
|
||
|
this._curgain+=(g-this._curgain)*0.01;
|
||
|
b[0][i]+=inbuf[0][i]*this._curgain;
|
||
|
b[1][i]+=inbuf[1][i]*this._curgain;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
var g=this.gain.Get(i);
|
||
|
this._curgain+=(g-this._curgain)*0.01;
|
||
|
this._nodeout[0].NodeEmit(i,inbuf[0][i]*this._curgain,inbuf[1][i]*this._curgain);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
this._nodein[0].NodeClear();
|
||
|
this.gain.Clear(true);
|
||
|
};
|
||
|
};
|
||
|
|
||
|
waapisimDelay=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="Delay";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.delayTime=new waapisimAudioParam(ctx,this,0,1,0);
|
||
|
this._bufl=new Float32Array(waapisimSampleRate);
|
||
|
this._bufr=new Float32Array(waapisimSampleRate);
|
||
|
for(var i=0;i<waapisimSampleRate;++i)
|
||
|
this._bufl[i]=this._bufr[i]=0;
|
||
|
this._index=0;
|
||
|
this._offscur=0;
|
||
|
this._Process=function() {
|
||
|
this.delayTime._Process();
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
var outbuf=this._nodeout[0].outbuf.buf;
|
||
|
var offs=Math.floor(this.delayTime.Get(0)*this.context.sampleRate);
|
||
|
if(offs<0)
|
||
|
offs=0;
|
||
|
if(offs>=this.context.sampleRate)
|
||
|
offs=this.context.sampleRate-1;
|
||
|
var deltaoff=(offs-this._offscur)/waapisimBufSize;
|
||
|
for(var i=0;i<waapisimBufSize;++i) {
|
||
|
var idxr=this._index-(this._offscur|0);
|
||
|
if(idxr<0)
|
||
|
idxr+=waapisimSampleRate;
|
||
|
this._bufl[this._index]=inbuf[0][i];
|
||
|
this._bufr[this._index]=inbuf[1][i];
|
||
|
outbuf[0][i]=this._bufl[idxr];
|
||
|
outbuf[1][i]=this._bufr[idxr];
|
||
|
if(++this._index>=waapisimSampleRate)
|
||
|
this._index=0;
|
||
|
this._offscur+=deltaoff;
|
||
|
}
|
||
|
this._nodeout[0].NodeEmitBuf();
|
||
|
this._nodein[0].NodeClear();
|
||
|
this.delayTime.Clear(false);
|
||
|
};
|
||
|
};
|
||
|
waapisimOscillator=webkitOscillatorNode=OscillatorNode=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,0,1);
|
||
|
this._nodetype="Osc";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this._targettype=3;
|
||
|
this._order=0;
|
||
|
this.context=ctx;
|
||
|
this.type=0;
|
||
|
this._wavtable=null;
|
||
|
this.frequency=new waapisimAudioParam(ctx,this,1,20000,440,0.9995);
|
||
|
this.detune=new waapisimAudioParam(ctx,this,-4800,4800,0,0.9995);
|
||
|
this.playbackState=0;
|
||
|
this._phase=0.5;
|
||
|
this._whenstart=0;
|
||
|
this._whenstop=Number.MAX_VALUE;
|
||
|
this._init=0;
|
||
|
this.start=this.noteOn=function(w) {
|
||
|
this._whenstart=w;
|
||
|
this.playbackState=1;
|
||
|
this.context._RegisterNode(this);
|
||
|
};
|
||
|
this.stop=this.noteOff=function(w) {
|
||
|
this._whenstop=w;
|
||
|
};
|
||
|
this.setPeriodicWave=this.setWaveTable=function(tab) {
|
||
|
this.type=4;
|
||
|
this._wavtable=tab;
|
||
|
};
|
||
|
this._Process=function() {
|
||
|
var i;
|
||
|
if(this._init==0) {
|
||
|
this.frequency.Init();
|
||
|
this.detune.Init();
|
||
|
this._init=1;
|
||
|
}
|
||
|
this.frequency._Process();
|
||
|
this.detune._Process();
|
||
|
if(this.playbackState==1 && this.context.currentTime>=this._whenstart)
|
||
|
this.playbackState=2;
|
||
|
if(this.playbackState==2 && this.context.currentTime>=this._whenstop)
|
||
|
this.playbackState=3;
|
||
|
if(this.playbackState!=2) {
|
||
|
for(i=0;i<waapisimBufSize;++i)
|
||
|
this._nodeout[0].outbuf.buf[0][i]=this._nodeout[0].outbuf.buf[1][i]=0;
|
||
|
return;
|
||
|
}
|
||
|
var t,x1,x2,y,z;
|
||
|
var obuf=this._nodeout[0].outbuf.buf;
|
||
|
var ph=this._phase;
|
||
|
var r=1/this.context.sampleRate;
|
||
|
var freq=this.frequency;
|
||
|
var detu=this.detune;
|
||
|
switch(this.type) {
|
||
|
case "sine":
|
||
|
case 0:
|
||
|
x1=0.5; x2=1.5; y=2*Math.PI; z=1/6.78;
|
||
|
break;
|
||
|
case "square":
|
||
|
case 1:
|
||
|
x1=0.5; x2=1.5; y=100000; z=0;
|
||
|
break;
|
||
|
case "sawtooth":
|
||
|
case 2:
|
||
|
x1=0; x2=2; y=2; z=0;
|
||
|
break;
|
||
|
case "triangle":
|
||
|
case 3:
|
||
|
x1=0.5; x2=1.5; y=4; z=0;
|
||
|
break;
|
||
|
case "custom":
|
||
|
case 4:
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
var f=freq.Get(i)*Math.pow(2,detu.Get(i)/1200);
|
||
|
ph+=f*r;
|
||
|
ph=ph-Math.floor(ph);
|
||
|
var out=0;
|
||
|
if(this._wavtable)
|
||
|
out=this._wavtable.buf[(4096*ph)|0];
|
||
|
obuf[0][i]=obuf[1][i]=out;
|
||
|
}
|
||
|
this._phase=ph;
|
||
|
this._nodeout[0].NodeEmitBuf();
|
||
|
this.frequency.Clear(true);
|
||
|
this.detune.Clear(true);
|
||
|
return;
|
||
|
}
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
var f=freq.Get(i)*Math.pow(2,detu.Get(i)/1200);
|
||
|
ph+=f*r;
|
||
|
ph=ph-Math.floor(ph);
|
||
|
t = ( Math.min( Math.max(ph ,x1 - ph), x2 - ph) - 0.5) * y;
|
||
|
var out=t-t*t*t*z;
|
||
|
if(out>1) out=1;
|
||
|
if(out<-1) out=-1;
|
||
|
obuf[0][i]=obuf[1][i]=out;
|
||
|
}
|
||
|
this._phase=ph;
|
||
|
this._nodeout[0].NodeEmitBuf();
|
||
|
this.frequency.Clear(true);
|
||
|
this.detune.Clear(true);
|
||
|
};
|
||
|
};
|
||
|
waapisimOscillator.SINE=waapisimOscillator.prototype.SINE=0;
|
||
|
waapisimOscillator.SQUARE=waapisimOscillator.prototype.SQUARE=1;
|
||
|
waapisimOscillator.SAWTOOTH=waapisimOscillator.prototype.SAWTOOTH=2;
|
||
|
waapisimOscillator.TRIANGLE=waapisimOscillator.prototype.TRIANGLE=3;
|
||
|
waapisimOscillator.CUSTOM=waapisimOscillator.prototype.CUSTOM=4;
|
||
|
waapisimOscillator.UNSCHEDULED_STATE=waapisimOscillator.prototype.UNSCHEDULED_STATE=0;
|
||
|
waapisimOscillator.SCHEDULED_STATE=waapisimOscillator.prototype.SCHEDULED_STATE=1;
|
||
|
waapisimOscillator.PLAYING_STATE=waapisimOscillator.prototype.PLAYING_STATE=2;
|
||
|
waapisimOscillator.FINISHED_STATE=waapisimOscillator.prototype.FINISHED_STATE=3;
|
||
|
|
||
|
waapisimAnalyser=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="Analyser";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.fftSize=2048;
|
||
|
this.frequencyBinCount=1024;
|
||
|
this.minDecibels=-100;
|
||
|
this.maxDecibels=-30;
|
||
|
this.smoothingTimeConstant=0;
|
||
|
this._fftInData=new Array(2048);
|
||
|
this._fftOutData=new Array(2048);
|
||
|
this._timeData=new Array(2048);
|
||
|
this._fftIndex=0;
|
||
|
this._fftCurrentSize=0;
|
||
|
this._fftrev=new Array(256);
|
||
|
this._fft=function(n,data,mag) {
|
||
|
var nh=n>>1;
|
||
|
var t=-2*Math.PI;
|
||
|
var m,mh,mq,i,j,jr,ji,kr,ki,xr,xi;
|
||
|
for(mh=1;(m=mh<<1)<=n;mh=m) {
|
||
|
mq=mh>>1;
|
||
|
t*=0.5;
|
||
|
for(jr=0;jr<n;jr+=m) {
|
||
|
kr=jr+mh;
|
||
|
xr=data[kr];
|
||
|
data[kr]=data[jr]-xr;
|
||
|
data[jr]+=xr;
|
||
|
}
|
||
|
for(i=1;i<mq;++i) {
|
||
|
var wr=Math.cos(t*i);
|
||
|
var wi=Math.sin(t*i);
|
||
|
for(j=0;j<n;j+=m) {
|
||
|
jr=j+i;
|
||
|
ji=j+mh-i;
|
||
|
kr=j+mh+i;
|
||
|
ki=j+m-i;
|
||
|
xr=wr*data[kr]+wi*data[ki];
|
||
|
xi=wr*data[ki]-wi*data[kr];
|
||
|
data[kr]=-data[ji]+xi;
|
||
|
data[ki]=data[ji]+xi;
|
||
|
data[ji]=data[jr]-xr;
|
||
|
data[jr]=data[jr]+xr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
data[0]=Math.min(1e-100,Math.abs(data[0]/n));
|
||
|
var stc=Math.min(1,Math.max(0,this.smoothingTimeConstant));
|
||
|
mag[0]=mag[0]*stc+(1-stc)*data[0];
|
||
|
for(i=0;i<nh;++i) {
|
||
|
var v=Math.sqrt(data[i]*data[i]+data[n-i]*data[n-i])/n;
|
||
|
if(v<1e-100)
|
||
|
v=1e-100;
|
||
|
mag[i]=mag[i]*stc+(1-stc)*v;
|
||
|
}
|
||
|
};
|
||
|
this.getByteFrequencyData=function(array) {
|
||
|
var range=this.maxDecibels-this.minDecibels;
|
||
|
for(var l=Math.min(array.length,this.frequencyBinCount),i=0;i<l;++i) {
|
||
|
var v=20*Math.LOG10E*Math.log(this._fftOutData[i]);
|
||
|
array[i]=((Math.min(this.maxDecibels,Math.max(this.minDecibels,v))-this.minDecibels)*255/range)|0;
|
||
|
}
|
||
|
};
|
||
|
this.getFloatFrequencyData=function(array) {
|
||
|
for(var l=Math.min(array.length,this.frequencyBinCount),i=0;i<l;++i)
|
||
|
array[i]=20*Math.LOG10E*Math.log(this._fftOutData[i]);
|
||
|
};
|
||
|
this.getByteTimeDomainData=function(array) {
|
||
|
for(var l=Math.min(this.frequencyBinCount,array.length),i=0;i<l;++i) {
|
||
|
var v=Math.min(1,Math.max(-1,this._timeData[i]));
|
||
|
array[i]=v*127+128;
|
||
|
}
|
||
|
};
|
||
|
this._Process=function() {
|
||
|
var i,j,k;
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
if(this.fftSize!=this._fftCurrentSize) {
|
||
|
var n=this.fftSize;
|
||
|
for(i=0;i<n;++i)
|
||
|
this._fftInData[i]=this._fftOutData[i]=0;
|
||
|
this._fftCurrentSize=n;
|
||
|
this.frequencyBinCount=n*0.5;
|
||
|
this._fftIndex=0;
|
||
|
this._fftrev[0]=0;
|
||
|
this._fftrev[n-1]=n-1;
|
||
|
for(i=0,j=1;j<n-1;++j) {
|
||
|
for(k=n>>1;k>(i^=k);k>>=1)
|
||
|
;
|
||
|
this._fftrev[j]=i;
|
||
|
}
|
||
|
}
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
var xl=inbuf[0][i];
|
||
|
var xr=inbuf[1][i];
|
||
|
this._nodeout[0].NodeEmit(i,xl,xr);
|
||
|
var v=this._timeData[this._fftIndex]=(xl+xr)*0.5;
|
||
|
var t=2*Math.PI*this._fftIndex/this._fftCurrentSize;
|
||
|
// this._fftInData[this._fftrev[this._fftIndex]]=v*(0.42-0.5*Math.cos(t)+0.08*Math.cos(t*2));
|
||
|
this._fftInData[this._fftrev[this._fftIndex]]=v*(0.5-0.5*Math.cos(t));
|
||
|
if(++this._fftIndex>=this._fftCurrentSize) {
|
||
|
this._fftIndex=0;
|
||
|
this._fft(this._fftCurrentSize,this._fftInData,this._fftOutData);
|
||
|
}
|
||
|
}
|
||
|
this._nodein[0].NodeClear();
|
||
|
};
|
||
|
};
|
||
|
waapisimConvolver=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="Convolver";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.buffer=null;
|
||
|
this.normalize=true;
|
||
|
this._scale=1;
|
||
|
this._analyzed=null;
|
||
|
this._dlybufsize=waapisimSampleRate*5;
|
||
|
this._dlybuf=new waapisimAudioBuffer(2,this._dlybufsize,44100);
|
||
|
this._dlyidx=0;
|
||
|
this._tapsize=20;
|
||
|
this._tap=[];
|
||
|
this._kernel=null;
|
||
|
this._sum=[];
|
||
|
this._sum[0]=[];
|
||
|
this._sum[1]=[];
|
||
|
this._bitrev=[];
|
||
|
this._bitrev[0]=0;
|
||
|
this._bitrev[waapisimBufSize-1]=waapisimBufSize-1;
|
||
|
var i,j,k;
|
||
|
for(i=0,j=1;j<waapisimBufSize-1;++j) {
|
||
|
for(k=waapisimBufSize>>1;k>(i^=k);k>>=1)
|
||
|
;
|
||
|
this._bitrev[j]=i;
|
||
|
}
|
||
|
for(i=0;i<2;++i)
|
||
|
for(j=0;j<2;++j)
|
||
|
this._sum[i][j]=new Float32Array(waapisimSampleRate);
|
||
|
this._Normalize=function(buffer) {
|
||
|
var GainCalibration=0.00125;
|
||
|
var GainCalibrationSampleRate=44100;
|
||
|
var MinPower=0.000125;
|
||
|
var numberOfChannels=2;
|
||
|
var length=buffer.length;
|
||
|
var power=0;
|
||
|
for(var i=0;i<numberOfChannels;++i) {
|
||
|
var sourceP=0;
|
||
|
var channelPower=0;
|
||
|
var n=length;
|
||
|
while(n--) {
|
||
|
var sample=buffer.buf[i][sourceP++];
|
||
|
channelPower+=sample*sample;
|
||
|
}
|
||
|
power+=channelPower;
|
||
|
}
|
||
|
power=Math.sqrt(power/(numberOfChannels*length));
|
||
|
if(isFinite(power)===false||isNaN(power)||power<MinPower)
|
||
|
power=MinPower;
|
||
|
var scale=1/power;
|
||
|
scale*=GainCalibration;
|
||
|
return scale;
|
||
|
};
|
||
|
this._Fft=function(n,a) {
|
||
|
var m,mh,mq,i,j,k,jr,ji,kr,ki;
|
||
|
var theta, wr, wi, xr, xi;
|
||
|
i=0;
|
||
|
for(j=1;j<n-1;j++) {
|
||
|
for(k=n>>1;k>(i^=k);k>>=1)
|
||
|
;
|
||
|
if(j<i) {
|
||
|
xr=a[j];
|
||
|
a[j]=a[i];
|
||
|
a[i]=xr;
|
||
|
}
|
||
|
}
|
||
|
theta=-2*Math.PI;
|
||
|
for(mh=1;(m=mh<<1)<=n;mh=m) {
|
||
|
mq=mh>>1;
|
||
|
theta*=0.5;
|
||
|
for(jr=0;jr<n;jr+=m) {
|
||
|
kr=jr+mh;
|
||
|
xr=a[kr];
|
||
|
a[kr]=a[jr]-xr;
|
||
|
a[jr]+=xr;
|
||
|
}
|
||
|
for(i=1;i<mq;i++) {
|
||
|
wr=Math.cos(theta*i);
|
||
|
wi=Math.sin(theta*i);
|
||
|
for(j=0;j<n;j+=m) {
|
||
|
jr=j+i;
|
||
|
ji=j+mh-i;
|
||
|
kr=j+mh+i;
|
||
|
ki=j+m-i;
|
||
|
xr=wr*a[kr]+wi*a[ki];
|
||
|
xi=wr*a[ki]-wi*a[kr];
|
||
|
a[kr]=-a[ji]+xi;
|
||
|
a[ki]=a[ji]+xi;
|
||
|
a[ji]=a[jr]-xr;
|
||
|
a[jr]=a[jr]+xr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
this._Fft2=function(n,ar,ai) {
|
||
|
var m, mh, i, j, k;
|
||
|
var wr, wi, xr, xi;
|
||
|
var theta=2*Math.PI;
|
||
|
i=0;
|
||
|
for(j=1;j<n-1;j++) {
|
||
|
for(k=n>>1;k>(i^=k);k>>=1)
|
||
|
;
|
||
|
if(j<i) {
|
||
|
xr=ar[j];
|
||
|
xi=ai[j];
|
||
|
ar[j]=ar[i];
|
||
|
ai[j]=ai[i];
|
||
|
ar[i]=xr;
|
||
|
ai[i]=xi;
|
||
|
}
|
||
|
}
|
||
|
for(mh=1;(m=mh<<1)<=n;mh=m) {
|
||
|
theta *= 0.5;
|
||
|
for(i=0;i<mh;i++) {
|
||
|
wr=Math.cos(theta*i);
|
||
|
wi=Math.sin(theta*i);
|
||
|
for(j=i;j<n;j+=m) {
|
||
|
k=j+mh;
|
||
|
xr=wr*ar[k]-wi*ai[k];
|
||
|
xi=wr*ai[k]+wi*ar[k];
|
||
|
ar[k]=ar[j]-xr;
|
||
|
ai[k]=ai[j]-xi;
|
||
|
ar[j]+=xr;
|
||
|
ai[j]+=xi;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for(i=0;i<n;++i)
|
||
|
ar[i]=ar[i]/n;
|
||
|
};
|
||
|
this._Process=function() {
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
var nh=(waapisimBufSize*0.5)|0;
|
||
|
var i,j,k,l,px,v0,v1;
|
||
|
|
||
|
if(this.buffer!==null) {
|
||
|
if(this.buffer!=this._analyzed) {
|
||
|
var kbuf=[];
|
||
|
for(i=0;i<4;++i)
|
||
|
kbuf[i]=new waapisimAudioBuffer(2,waapisimBufSize,44100);
|
||
|
this._scale=1;
|
||
|
if(this.normalize)
|
||
|
this._scale=this._Normalize(this.buffer);
|
||
|
var len=this.buffer.length;
|
||
|
for(i=len-1;i;--i) {
|
||
|
if(Math.abs(this.buffer.buf[0][i])>1e-3)
|
||
|
break;
|
||
|
if(Math.abs(this.buffer.buf[1][i])>1e-3)
|
||
|
break;
|
||
|
}
|
||
|
len=i+1;
|
||
|
for(i=0,px=0;i<this._tapsize;++i) {
|
||
|
var x=(i*len/this._tapsize)|0;
|
||
|
var sz=x-px;
|
||
|
v0=0;
|
||
|
v1=0;
|
||
|
if(sz>0) {
|
||
|
while(px<x) {
|
||
|
v0+=this.buffer.buf[0][px]*this.buffer.buf[0][px];
|
||
|
v1+=this.buffer.buf[1][px]*this.buffer.buf[1][px];
|
||
|
++px;
|
||
|
}
|
||
|
v0=Math.sqrt(v0)*this._scale*0.5;
|
||
|
v1=Math.sqrt(v1)*this._scale*0.5;
|
||
|
}
|
||
|
this._tap[i]=[x,v0,v1];
|
||
|
}
|
||
|
this._kernel=new waapisimAudioBuffer(2,waapisimBufSize,44100);
|
||
|
var p=0,maxp=0;
|
||
|
for(l=Math.min(this.buffer.length,waapisimBufSize*4),i=0,j=0,k=0;i<l;++i) {
|
||
|
v0=this.buffer.buf[0][i];
|
||
|
v1=this.buffer.buf[1][i];
|
||
|
kbuf[k].buf[0][j]=v0;
|
||
|
kbuf[k].buf[1][j]=v1;
|
||
|
p+=(v0*v0+v1*v1);
|
||
|
if(++j>=waapisimBufSize) {
|
||
|
if(p>maxp) {
|
||
|
this._kernel=kbuf[k];
|
||
|
maxp=p;
|
||
|
}
|
||
|
j=0;
|
||
|
p=0;
|
||
|
++k;
|
||
|
}
|
||
|
}
|
||
|
if(p>maxp||this._kernel===null)
|
||
|
this._kernel=kbuf[k];
|
||
|
this._Fft(waapisimBufSize,this._kernel.buf[0]);
|
||
|
this._Fft(waapisimBufSize,this._kernel.buf[1]);
|
||
|
this._analyzed=this.buffer;
|
||
|
}
|
||
|
this._Fft(waapisimBufSize,inbuf[0]);
|
||
|
this._Fft(waapisimBufSize,inbuf[1]);
|
||
|
this._sum[0][0][0]=this._sum[1][0][0]=this._sum[0][1][0]=this._sum[1][1][0]=0;
|
||
|
for(i=1,j=waapisimBufSize-1;i<nh;++i,--j) {
|
||
|
var real0=inbuf[0][i]*this._kernel.buf[0][i]-inbuf[0][j]*this._kernel.buf[0][j];
|
||
|
var imag0=inbuf[0][i]*this._kernel.buf[0][j]+inbuf[0][j]*this._kernel.buf[0][i];
|
||
|
this._sum[0][0][i]=real0;
|
||
|
this._sum[0][0][j]=real0;
|
||
|
this._sum[0][1][i]=-imag0;
|
||
|
this._sum[0][1][j]=imag0;
|
||
|
var real1=inbuf[1][i]*this._kernel.buf[1][i]-inbuf[1][j]*this._kernel.buf[1][j];
|
||
|
var imag1=inbuf[1][i]*this._kernel.buf[1][j]+inbuf[1][j]*this._kernel.buf[1][i];
|
||
|
this._sum[1][0][i]=real1;
|
||
|
this._sum[1][0][j]=real1;
|
||
|
this._sum[1][1][i]=-imag1;
|
||
|
this._sum[1][1][j]=imag1;
|
||
|
}
|
||
|
this._Fft2(waapisimBufSize,this._sum[0][0],this._sum[0][1]);
|
||
|
this._Fft2(waapisimBufSize,this._sum[1][0],this._sum[1][1]);
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
var v=(nh-Math.abs(i-nh))/nh;
|
||
|
this._dlybuf.buf[0][this._dlyidx]=this._sum[0][0][i]*v;
|
||
|
this._dlybuf.buf[1][this._dlyidx]=this._sum[1][0][i]*v;
|
||
|
v0=0; v1=0;
|
||
|
for(l=this._tap.length,j=0;j<l;++j) {
|
||
|
var idx=this._dlyidx-this._tap[j][0];
|
||
|
while(idx<0)
|
||
|
idx+=this._dlybufsize;
|
||
|
v0+=this._dlybuf.buf[0][idx]*this._tap[j][1];
|
||
|
v1+=this._dlybuf.buf[1][idx]*this._tap[j][2];
|
||
|
}
|
||
|
this._nodeout[0].NodeEmit(i,v0,v1);
|
||
|
if(++this._dlyidx>=this._dlybufsize)
|
||
|
this._dlyidx=0;
|
||
|
}
|
||
|
}
|
||
|
this._nodein[0].NodeClear();
|
||
|
};
|
||
|
};
|
||
|
waapisimDynamicsCompressor=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="DynComp";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.threshold=new waapisimAudioParam(ctx,this,-100,0,-24);
|
||
|
this.knee=new waapisimAudioParam(ctx,this,0,40,30);
|
||
|
this.ratio=new waapisimAudioParam(ctx,this,1,20,12);
|
||
|
this.reduction=new waapisimAudioParam(ctx,this,-20,0,0);//ReadOnly
|
||
|
this.attack=new waapisimAudioParam(ctx,this,0,1,0.003);
|
||
|
this.release=new waapisimAudioParam(ctx,this,0,1,0.25);
|
||
|
this._maxl=0;
|
||
|
this._maxr=0;
|
||
|
this._gain=1;
|
||
|
this._Process=function() {
|
||
|
this.threshold._Process();
|
||
|
this.knee._Process();
|
||
|
this.ratio._Process();
|
||
|
this.attack._Process();
|
||
|
this.release._Process();
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
var relratio=this.release.Get(0)*waapisimSampleRate;
|
||
|
relratio=Math.pow(1/3.16,1/relratio);
|
||
|
var atkratio=this.attack.Get(0)*waapisimSampleRate;
|
||
|
atkratio=Math.pow(1/3.16,1/atkratio);
|
||
|
var reduc=this.reduction.value;
|
||
|
var thresh=Math.pow(10,this.threshold.Get(0)/20);
|
||
|
var knee=Math.pow(10,this.knee.Get(0)/20*0.5);
|
||
|
var makeup=1/Math.sqrt(thresh)/Math.pow(10,this.knee.Get(0)/80);
|
||
|
var maxratio=0.99105;
|
||
|
var ratio=this.ratio.Get(0);
|
||
|
if(ratio<=1)
|
||
|
ratio=1;
|
||
|
for(var i=0;i<waapisimBufSize;++i) {
|
||
|
this._maxl=maxratio*this._maxl+(1-maxratio)*inbuf[0][i]*inbuf[0][i];
|
||
|
this._maxr=maxratio*this._maxr+(1-maxratio)*inbuf[1][i]*inbuf[1][i];
|
||
|
var maxc=Math.sqrt(Math.max(this._maxl,this._maxr))*1.414;
|
||
|
if(maxc>thresh) {
|
||
|
var v=Math.pow(thresh*Math.min(knee,maxc/thresh)/maxc,1-1/ratio);
|
||
|
this._gain=v+(this._gain-v)*atkratio;
|
||
|
}
|
||
|
else
|
||
|
this._gain=1+(this._gain-1)*relratio;
|
||
|
var g=this._gain*makeup;
|
||
|
this._nodeout[0].NodeEmit(i,inbuf[0][i]*g,inbuf[1][i]*g);
|
||
|
}
|
||
|
this.reduction.value=this.reduction.computedValue=reduc;
|
||
|
this._nodein[0].NodeClear();
|
||
|
this.threshold.Clear(false);
|
||
|
this.knee.Clear(false);
|
||
|
this.ratio.Clear(false);
|
||
|
this.reduction.Clear(false);
|
||
|
this.attack.Clear(false);
|
||
|
this.release.Clear(false);
|
||
|
};
|
||
|
};
|
||
|
waapisimPanner=webkitAudioPannerNode=AudioPannerNode=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="Panner";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.panningModel=0;
|
||
|
this.distanceModel=1;
|
||
|
this.refDistance=1;
|
||
|
this.maxDistance=10000;
|
||
|
this.rolloffFactor=1;
|
||
|
this.coneInnerAngle=360;
|
||
|
this.coneOuterAngle=360;
|
||
|
this.coneOuterGain=0;
|
||
|
this.px=0;
|
||
|
this.py=0;
|
||
|
this.pz=0;
|
||
|
this.setPosition=function(x,y,z) {this.px=x;this.py=y;this.pz=z;};
|
||
|
this.setOrientation=function(x,y,z) {};
|
||
|
this.setVelocity=function(x,y,z) {};
|
||
|
this._Process=function() {
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
var listener=this.context.listener;
|
||
|
var dx=this.px-listener.px;
|
||
|
var dy=this.py-listener.py;
|
||
|
var dz=this.pz-listener.pz;
|
||
|
var d=Math.max(1,Math.sqrt(dx*dx+dy*dy+dz*dz));
|
||
|
var dgain;
|
||
|
switch(this.distanceModel) {
|
||
|
case "linear":
|
||
|
case 0:
|
||
|
dgain=1-this.rolloffFactor*(d-this.refDistance)/(this.maxDistance-this.refDistance);
|
||
|
break;
|
||
|
case "inverse":
|
||
|
case 1:
|
||
|
dgain=this.refDistance/(this.refDistance+this.rolloffFactor*(d-this.refDistance));
|
||
|
break;
|
||
|
case "exponential":
|
||
|
case 2:
|
||
|
dgain=Math.pow(d/this.refDistance,-this.rolloffFactor);
|
||
|
break;
|
||
|
}
|
||
|
var rgain,lgain,tr;
|
||
|
if(Math.abs(dz)<0.001) {
|
||
|
lgain=rgain=1;
|
||
|
}
|
||
|
else {
|
||
|
tr=Math.atan(dx/dz);
|
||
|
if(dz<=0) {
|
||
|
rgain=-tr+Math.PI*0.5;
|
||
|
lgain=tr+Math.PI*0.5;
|
||
|
}
|
||
|
else {
|
||
|
switch(this.panningModel) {
|
||
|
case 0:
|
||
|
case "equalpower":
|
||
|
rgain=tr+Math.PI*0.5;
|
||
|
lgain=-tr+Math.PI*0.5;
|
||
|
break;
|
||
|
default:
|
||
|
if(dx>=0) {
|
||
|
rgain=tr+Math.PI*0.5;
|
||
|
lgain=-(-tr+Math.PI*0.5);
|
||
|
}
|
||
|
else {
|
||
|
rgain=-(tr+Math.PI*0.5);
|
||
|
lgain=-tr+Math.PI*0.5;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
var rl=Math.sqrt(rgain*rgain+lgain*lgain);
|
||
|
rgain=rgain/rl;
|
||
|
lgain=lgain/rl;
|
||
|
var a=Math.sqrt(rgain*rgain+lgain*lgain);
|
||
|
rgain=rgain/a*2*dgain; lgain=lgain/a*2*dgain;
|
||
|
for(var i=0;i<waapisimBufSize;++i)
|
||
|
this._nodeout[0].NodeEmit(i,inbuf[0][i]*lgain,inbuf[1][i]*rgain);
|
||
|
this._nodein[0].NodeClear();
|
||
|
};
|
||
|
};
|
||
|
waapisimPanner.EQUALPOWER=waapisimPanner.prototype.EQUALPOWER=0;
|
||
|
waapisimPanner.HRTF=waapisimPanner.prototype.HRTF=1;
|
||
|
waapisimPanner.SOUNDFIELD=waapisimPanner.prototype.SOUNDFIELD=2;
|
||
|
waapisimPanner.LINEAR_DISTANCE=waapisimPanner.prototype.LINEAR_DISTANCE=0;
|
||
|
waapisimPanner.INVERSE_DISTANCE=waapisimPanner.prototype.INVERSE_DISTANCE=1;
|
||
|
waapisimPanner.EXPONENTIAL_DISTANCE=waapisimPanner.prototype.EXPONENTIAL_DISTANCE=2;
|
||
|
|
||
|
waapisimChannelSplitter=function(ctx,ch) {
|
||
|
this._nodetype="ChSplit";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
if(typeof(ch)==="undefined")
|
||
|
ch=6;
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,ch);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this._Process=function() {
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
for(var i=0;i<waapisimBufSize;++i) {
|
||
|
this._nodeout[0].NodeEmit(i,inbuf[0][i],inbuf[0][i]);
|
||
|
this._nodeout[1].NodeEmit(i,inbuf[1][i],inbuf[1][i]);
|
||
|
}
|
||
|
this._nodein[0].NodeClear();
|
||
|
};
|
||
|
};
|
||
|
waapisimChannelMerger=function(ctx,ch) {
|
||
|
this._nodetype="ChMerge";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
if(typeof(ch)==="undefined")
|
||
|
ch=6;
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,ch,1);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this._Process=function() {
|
||
|
var inbuf0=this._nodein[0].inbuf.buf;
|
||
|
var inbuf1=this._nodein[1].inbuf.buf;
|
||
|
for(var i=0;i<waapisimBufSize;++i)
|
||
|
this._nodeout[0].NodeEmit(i,(inbuf0[0][i]+inbuf0[1][i])*0.5,(inbuf1[0][i]+inbuf1[1][i])*0.5);
|
||
|
this._nodein[0].NodeClear();
|
||
|
this._nodein[1].NodeClear();
|
||
|
};
|
||
|
};
|
||
|
waapisimWaveShaper=function(ctx) {
|
||
|
waapisimAudioNode.call(this,waapisimBufSize,1,1);
|
||
|
this._nodetype="Shaper";
|
||
|
waapisimDebug("create "+this._nodetype+this._nodeId);
|
||
|
this.context=ctx;
|
||
|
this.playbackState=0;
|
||
|
this.curve=null;
|
||
|
var i;
|
||
|
this._Process=function() {
|
||
|
var inbuf=this._nodein[0].inbuf.buf;
|
||
|
var curve=this.curve;
|
||
|
if(curve!==null) {
|
||
|
var len=curve.length-1;
|
||
|
if(len>=0) {
|
||
|
for(i=0;i<waapisimBufSize;++i) {
|
||
|
var xl=Math.max(-1,Math.min(1,inbuf[0][i]));
|
||
|
var xr=Math.max(-1,Math.min(1,inbuf[1][i]));
|
||
|
xl=curve[((xl+1)*0.5*len+0.5)|0];
|
||
|
xr=curve[((xr+1)*0.5*len+0.5)|0];
|
||
|
this._nodeout[0].NodeEmit(i,xl,xr);
|
||
|
}
|
||
|
this._nodein[0].NodeClear();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
for(i=0;i<waapisimBufSize;++i)
|
||
|
this._nodeout[0].NodeEmit(i,inbuf[0][i],inbuf[1][i]);
|
||
|
this._nodein[0].NodeClear();
|
||
|
};
|
||
|
};
|
||
|
waapisimAudioParam=function(ctx,node,min,max,def,tcon) {
|
||
|
this.context=ctx;
|
||
|
this._targettype=0;
|
||
|
this.node=node;
|
||
|
this.value=def;
|
||
|
this.computedValue=def;
|
||
|
this.minValue=min;
|
||
|
this.maxValue=max;
|
||
|
this.defaultValue=def;
|
||
|
if(typeof(tcon)==="undefined")
|
||
|
this.timeconst=0;
|
||
|
else
|
||
|
this.timeconst=tcon;
|
||
|
this.from=[];
|
||
|
this.inbuf={};
|
||
|
this.inbuf.buf=[];
|
||
|
this.inbuf.buf[0]=new Float32Array(waapisimBufSize);
|
||
|
this.inbuf.buf[1]=new Float32Array(waapisimBufSize);
|
||
|
this.automation=[];
|
||
|
this.deltaAdd=0;
|
||
|
this.deltaMul=1;
|
||
|
this.deltaTarget=0;
|
||
|
this.currentEvent=null;
|
||
|
for(var i=0;i<waapisimBufSize;++i)
|
||
|
this.inbuf.buf[0][i]=this.inbuf.buf[1][i]=0;
|
||
|
this.AddEvent=function(ev) {
|
||
|
var t=ev[0];
|
||
|
for(var l=this.automation.length,i=0;i<l;++i) {
|
||
|
if(this.automation[i][0]>t)
|
||
|
break;
|
||
|
}
|
||
|
this.automation.splice(i,0,ev);
|
||
|
};
|
||
|
this.setValueAtTime=function(v,t) {
|
||
|
this.AddEvent([t,0,v]);
|
||
|
};
|
||
|
this.linearRampToValueAtTime=function(v,t) {
|
||
|
this.AddEvent([t,1,v]);
|
||
|
};
|
||
|
this.exponentialRampToValueAtTime=function(v,t) {
|
||
|
this.AddEvent([t,2,v]);
|
||
|
};
|
||
|
this.setTargetAtTime=this.setTargetValueAtTime=function(v,t,c) {
|
||
|
this.AddEvent([t,3,v,c]);
|
||
|
};
|
||
|
this.setValueCurveAtTime=function(values,t,d) {
|
||
|
this.AddEvent([t,4,values,d]);
|
||
|
};
|
||
|
this.cancelScheduledValues=function(t) {
|
||
|
for(var l=this.automation.length,i=0;i<l;++i) {
|
||
|
if(this.automation[i][0]>=t) {
|
||
|
this.automation.length=i;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
this._Process=function() {
|
||
|
this.value+=this.deltaAdd;
|
||
|
this.value=(this.value-this.deltaTarget)*this.deltaMul+this.deltaTarget;
|
||
|
if(this.currentEvent!==null) {
|
||
|
if(this.currentEvent[1]==4) {
|
||
|
var i=(this.currentEvent[2].length-1)*(this.context.currentTime-this.currentEvent[0])/this.currentEvent[3];
|
||
|
this.value=this.currentEvent[2][Math.min(this.currentEvent[2].length-1,i)|0];
|
||
|
}
|
||
|
}
|
||
|
if(this.automation.length>0) {
|
||
|
if(this.context.currentTime>=this.automation[0][0]) {
|
||
|
this.deltaAdd=0;
|
||
|
this.deltaMul=1;
|
||
|
this.deltaTarget=0;
|
||
|
this.currentEvent=this.automation.shift();
|
||
|
switch(this.currentEvent[1]) {
|
||
|
case 0:
|
||
|
case 1:
|
||
|
case 2:
|
||
|
this.value=this.currentEvent[2];
|
||
|
break;
|
||
|
case 3:
|
||
|
this.deltaMul=Math.pow(0.367879,1/(waapisimSampleRate/waapisimBufSize*this.currentEvent[3]));
|
||
|
this.deltaTarget=this.currentEvent[2];
|
||
|
break;
|
||
|
}
|
||
|
if(this.automation.length>0) {
|
||
|
var n=waapisimSampleRate/waapisimBufSize*(this.automation[0][0]-this.context.currentTime);
|
||
|
switch(this.automation[0][1]) {
|
||
|
case 1:
|
||
|
this.deltaAdd=(this.automation[0][2]-this.value)/n;
|
||
|
break;
|
||
|
case 2:
|
||
|
this.deltaMul=Math.pow(this.automation[0][2]/this.value,1/n);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
this.Init=function() {
|
||
|
this.computedValue=parseFloat(this.value);
|
||
|
}
|
||
|
this.Get=function(n) {
|
||
|
if(this.from.length>0)
|
||
|
this.computedValue=parseFloat(this.value)+(this.inbuf.buf[0][n]+this.inbuf.buf[1][n])*0.5;
|
||
|
else
|
||
|
this.computedValue=this.computedValue*this.timeconst+(1-this.timeconst)*parseFloat(this.value);
|
||
|
return this.computedValue;
|
||
|
};
|
||
|
this.Clear=function(arate) {
|
||
|
if(arate) {
|
||
|
for(var i=0;i<waapisimBufSize;++i)
|
||
|
this.inbuf.buf[0][i]=this.inbuf.buf[1][i]=0;
|
||
|
}
|
||
|
else
|
||
|
this.inbuf.buf[0][0]=this.inbuf.buf[1][0]=0;
|
||
|
};
|
||
|
};
|
||
|
}
|