You are not recognized as the original poster of this topic.
class DelayLine{
constructor(options={}){
const{
maxDelayInSamples=44100,numChannels=2,sampleRate=44100,interpolationType="linear"
}=options;
this.interpolationType=interpolationType;
this.sampleRate=sampleRate;
this.numChannels=numChannels;
this.maxDelay=maxDelayInSamples;
this.totalSize=Math.max(4,maxDelayInSamples+2);
//circular buffer for each channel
this.buffer=Array.from({length:numChannels},()=>new Array(this.totalSize).fill(0));
this.writePos=new Array(numChannels).fill(0);
this.readPos=new Array(numChannels).fill(0);
this.v=new Array(numChannels).fill(0); //for Thiran interpolation
this.setDelay(0); //initialize delay parameters
}
setDelay(newDelay){
const upperLimit=this.totalSize-2;
this.delay=Math.max(0,Math.min(newDelay,upperLimit));
this.delayInt=Math.floor(this.delay);
this.delayFrac=this.delay-this.delayInt;
this.updateInternalVariables();
}
updateInternalVariables(){
switch(this.interpolationType){
case"lagrange3rd":
if(this.delayFrac<2&&this.delayInt>=1){
this.delayFrac++;this.delayInt--;
}
break;
case"thiran":
if(this.delayFrac<.618&&this.delayInt>=1){
this.delayFrac++;this.delayInt--;
}
this.alpha=(1-this.delayFrac)/(1+this.delayFrac);
break;
}
}
pushSample(channel,sample){
this.buffer[channel][this.writePos[channel]]=sample;
this.writePos[channel]=(this.writePos[channel]+this.totalSize-1)%this.totalSize;
}
popSample(channel,delayInSamples=-1,updateReadPointer=true){
if(delayInSamples>=0)this.setDelay(delayInSamples);
let result;
switch(this.interpolationType){
case"none":result=this.interpolateNone(channel);break;
case"linear":result=this.interpolateLinear(channel);break;
case"lagrange3rd":result=this.interpolateLagrange3rd(channel);break;
case"thiran":result=this.interpolateThiran(channel);break;
}
if(updateReadPointer)
this.readPos[channel]=(this.readPos[channel]+this.totalSize-1)%this.totalSize;
return result;
}
//interpolation methods
interpolateNone(channel){
const index=(this.readPos[channel]+this.delayInt)%this.totalSize;
return this.buffer[channel][index];
}
interpolateLinear(channel){
const idx1=(this.readPos[channel]+this.delayInt)%this.totalSize,
idx2=(idx1+1)%this.totalSize,
y1=this.buffer[channel][idx1],y2=this.buffer[channel][idx2];
return y1+this.delayFrac*(y2-y1);
}
interpolateLagrange3rd(channel){
const idx1=(this.readPos[channel]+this.delayInt)%this.totalSize,
idx2=(idx1+1)%this.totalSize,idx3=(idx2+1)%this.totalSize,idx4=(idx3+1)%this.totalSize,
[y1,y2,y3,y4]=[
this.buffer[channel][idx1],
this.buffer[channel][idx2],
this.buffer[channel][idx3],
this.buffer[channel][idx4]
],d=this.delayFrac,d1=d-1,d2=d-2,d3=d-3;
return y1*(-d1*d2*d3/6)+d*(
y2*(d2*d3*.5)+
y3*(-d1*d3*.5)+
y4*(d1*d2/6)
);
}
interpolateThiran(channel){
const idx1=(this.readPos[channel]+this.delayInt)%this.totalSize,idx2=(idx1+1)%this.totalSize,
[y1,y2]=[this.buffer[channel][idx1],this.buffer[channel][idx2]],
output=this.delayFrac<1e-9?y1:y2+this.alpha*(y1-this.v[channel]);
this.v[channel]=output;
return output;
}
//utility methods
reset(){
this.writePos.fill(0);this.readPos.fill(0);this.v.fill(0);
this.buffer.forEach(ch=>ch.fill(0));
}
setMaxDelay(samples){
this.totalSize=Math.max(4,samples+2);
this.buffer=Array.from({length:this.numChannels},()=>new Array(this.totalSize).fill(0));
this.reset();
}
}
// === Usage example ===
let delay=new DelayLine({
maxDelayInSamples:8000,
numChannels:1,
interpolationType:"lagrange3rd"
}),last=0;
return t=>{
let o=(sin(PI*2*300*t)*(1-t%.3)**90)+(sin(PI*2*800*t)*(1-t%.45)**100)*.5;
delay.pushSample(0,o-last*.7);
delay.setDelay(1000+sin(t*2.5)*1000);
last=delay.popSample(0);
return Math.tanh(o+last);
};