function dB_to_linear(db){return Math.pow(10,db/20);} class Fifo{ //FIFO (first in, first out) for samples. constructor(){this.buffer=[];} write(samples){this.buffer=this.buffer.concat(samples);} read(len){return this.buffer.splice(0,len);} peek(len){return this.buffer.slice(0,len);} push(sample){this.buffer.push(sample);} } class Filter{ constructor(size){ this.size=size; this.buffer=new Array(size).fill(0); this.ptr=0; this.store=0; } advance(){this.ptr=(this.ptr+1)%this.size;} //Simple comb filter process. combProcess(input,feedback,hfDamping){ const output=this.buffer[this.ptr]; this.store=output+(this.store-output)*hfDamping; this.buffer[this.ptr]=input+this.store*feedback; this.advance(); return output; } //Simple allpass filter process. allpassProcess(input){ const output=this.buffer[this.ptr]; this.buffer[this.ptr]=input+output*0.5; this.advance(); return output-input; } } class FilterArray{ //FilterArray with one set of comb and allpass filters. constructor(sampleRate,scale){ //Set the delays relative to a reference sampleRate of 44100Hz. let combDelay=Math.round(scale*sampleRate/44100*1200); let allpassDelay=Math.round(sampleRate/44100*400); this.comb=new Filter(combDelay); this.allpass=new Filter(allpassDelay); } process(input,feedback,hfDamping,gain){ //Process through comb filter. let outVal=this.comb.combProcess(input,feedback,hfDamping); //Process through allpass filter. outVal=this.allpass.allpassProcess(outVal); return outVal*gain; } } class Reverb{ //Reverb (a single-channel version) constructor(sampleRate,wetGain_dB,roomScale,reverberance,hfDamping,preDelay_ms){ //Pre-delay is implemented as a FIFO delay. this.delaySamples=Math.round((preDelay_ms/1000)*sampleRate); this.fifo=new Fifo(); //Preload delay with zeros. this.fifo.write(new Array(this.delaySamples).fill(0)); //Scale and gain settings. this.scale=roomScale/100*0.9+0.1; //Simplified feedback calculation. this.feedback=1-Math.exp((reverberance-50)/50); this.hfDamping=hfDamping/100*0.3+0.2; this.gain=dB_to_linear(wetGain_dB)*0.015; //Create a filter array. this.filterArray=new FilterArray(sampleRate,this.scale); } //Process one sample. processSample(inputSample){ //Push the incoming sample into the FIFO. this.fifo.push(inputSample); //Read one sample from the FIFO to get the pre-delayed sample. let delayed=this.fifo.read(1)[0]||0; //Process the delayed sample through our filter array. let wet=this.filterArray.process(delayed,this.feedback,this.hfDamping,this.gain); return wet; } //Process a block of samples. processBlock(inputSamples){ let outputSamples=[]; for(let i=0;ix*m); } return processedSignal; } let kicks=[]; for(let i=0;i<24;i++) kicks.push(kickGenerator()); console.log(kicks); function readArray(a,t){return t>=0&&t*sR=timer.stop){ timer.current=timer.current%timer.stop; voices.push({ kickIndex:Math.floor(Math.random()*kicks.length), time:0, speed:.2+(Math.random()**2)*6, reverse:Math.random()>.5,channel:+(Math.random()>.5) }); } let master=[0,0]; for(let i=0;i=kicks[voice.kickIndex].length){ voices.splice(i,1); i--; } } return[Math.tanh(master[0]),Math.tanh(master[1])]; }