You are not recognized as the original poster of this topic.
var sampleRate=32000,
PI=Math.PI,TAU=PI*2;
class Filter{ //generated by o1
constructor(fs=sampleRate,freq=1000,Q=0.707){
this.fs=fs; //sampling frequency
this.g=0; //coefficient
this.R=1; //damping factor (computed from Q)
this.S1=0;this.S2=0; //state variables
this.type=0; //filter type parameter (continuous LP/BP/HP control, 0 to 2)
this.freq=freq; //cutoff frequency
this.Q=Q; //Q(uality) factor
}
setParams(freq,Q,type){
this.freq=freq;this.Q=Q;this.type=type;
var wd=TAU*freq, //digital angular frequency
T=1/this.fs, //sampling period
wa=(2/T)*Math.tan(wd*T/2); //pre-warping the frequency for bilinear transform
this.g=wa*T/2; //compute filter coefficient
this.R=1/(2*Q); //compute damping factor from Q
}
process(x){
//calculate the intermediate value 'v' using the state variables
var v=(x-this.S2-this.R*this.S1)/(1+this.g*this.R+this.g*this.g),
//update the state variables for the next sample
S1_next=this.g*v+this.S1,
S2_next=this.g*S1_next+this.S2,
//compute the filter outputs
LP=S2_next, //low-pass
BP=S1_next, //band-pass
HP=x-this.R*S1_next-S2_next; //high-pass
//update the state variables
this.S1=S1_next;this.S2=S2_next;
//blend the outputs based on the 'type' parameter
return this.type<=1?
LP*(1-this.type)+BP*this.type: //between low-pass and band-pass
BP*(2-this.type)+HP*(this.type-1); //between band-pass and high-pass
}
}
//GPT-4o mini's miniFilter
function miniFilter(){this.z=0;this.fc=0.5;this.q=1;this.a=0;this.b=0;this.set_fc(0.5);this.set_q(1);}
miniFilter.prototype.set_fc=function(fc){this.fc=fc<0?0:fc>1?1:fc;this.a=Math.sin(PI*this.fc)/(2*this.q);};
miniFilter.prototype.set_q=function(q){this.q=q<0.1?0.1:q>10?10:q;};
miniFilter.prototype.run=function(x){this.b=this.a*(x-this.z)+this.z;this.z=this.b;return this.b;};
/* This is what a real/working filter looks like:
function DiodeFilter(){
this.k=0;
this.A=0;
this.z=[0,0,0,0,0];
this.ah;this.bh;this.fc;
this.set_q(0);
this.set_hpf(0.5);
this.set_fc(.5);
}
DiodeFilter.prototype.set_hpf=function(fc){
var K=fc*PI;
this.ah=(K-2)/(K+2);
this.bh=2/(K+2);
};
DiodeFilter.prototype.reset=function(){if(this.k<17)this.z=[0,0,0,0,0];};
DiodeFilter.prototype.set_q=function(q){
this.k=20*q;this.A=1+.5*this.k;
};
DiodeFilter.prototype.set_fc=function(cutoff){
cutoff*=cutoff;
this.fc=cutoff<=0
?.02
:(cutoff>=1?.999:cutoff);
};
function clip(x){return x/(1+Math.abs(x));}
DiodeFilter.prototype.run=function(x){
var a=PI*this.fc;
a=2*Math.tan(.5*a); //dewarping, not required with 2x oversampling
var ainv=1/a,
a2=a*a,
b=2*a+1,
b2=b*b,
c=1/(2*a2*a2-4*a2*b2+b2*b2),
g0=2*a2*a2*c,
g=g0*this.bh,
//current state
s0=(a2*a*this.z[0]+a2*b*this.z[1]+this.z[2]*(b2-2*a2)*a+this.z[3]*(b2-3*a2)*b)*c,
s=this.bh*s0-this.z[4],
//solve feedback loop (linear)
y5=(g*x+s)/(1+g*this.k),
//input clipping
y0=clip(x-this.k*y5);
y5=g*y0+s;
//compute integrator outputs
var y4=g0*y0+s0,
y3=(b*y4-this.z[3])*ainv,
y2=(b*y3-a*y4-this.z[2])*ainv,
y1=(b*y2-a*y3-this.z[1])*ainv;
//update filter state
this.z[0]+=4*a*(y0-y1+y2);
this.z[1]+=2*a*(y1-2*y2+y3);
this.z[2]+=2*a*(y2-2*y3+y4);
this.z[3]+=2*a*(y3-2*y4);
this.z[4]=this.bh*y4+this.ah*y5;
return this.A*y4;
};
*/
function note(n,base=440,keys=12){return base*Math.pow(2,n/keys);}
var f=[],//new Filter(),
c=[0,3,7,10,14,15,17,22,26];
for(let i=0;i<c.length;i++){f.push(new Filter());}
function dsp(t){
/*var x=(t*60)%1<.5;
f.setParams(
1300+(Math.sin(t*3)*650),
4,.2
);
return f.process(x)*.5;*/
var x=0,i=0;
for(;i<c.length;i++){
f[i].setParams(
1400+(Math.sin(t*(1.3+i)*1.3)*650),
14,(((Math.sin((t+(i*TAU/c.length))*5)+1)/2)**7)*2
);
x+=f[i].process(
((t*note(c[i]+[5,10,3,8,1,6,-1][Math.floor(t*.8)%7],131.4,12.18))%1)-.5
);
}
return x/16;
}
return dsp;