// The Dittytoy Library v1.01 - https://github.com/srtuss/dittytoy-library
const{max,min,abs,random,sin,cos,exp,tan,tanh,pow,log,PI}=Math;const TAU=2*PI;function rhy(k,n){let nx=k,ny=n-k,x=[1],y=[0];while(nx&&ny>1){if(nx<ny){x=x.concat(y);ny-=nx;}else{let t=nx-ny;nx=ny;ny=t;let k=x.concat(y);y=x;x=k;}}return Array(nx).fill(x).flat().concat(Array(ny).fill(y).flat());}const llerp=(a,b,v)=>exp(lerp(log(a),log(b),v));const __pks=synth.def((p,e,t,o)=>o.f.length?(o.f.pop()(),0):0);const poke=(f)=>__pks.play(1,{f:[f]});let _st=0;function sleeps(v){if(ditty.swing>0){let sf=min(ditty.swing,.5);let oo=1+~~(v*100);v/=oo;let zz=0;for(let i=0;i<oo;++i){let pha=(_st*2)%1;let st=pha<.5;zz+=v*(st?1+sf:1-sf);_st+=v;}sleep(zz);}else sleep(v);}function tanhXdX(x){var a=x*x;return((a+105)*a+945)/((15*a+420)*a+945);}class Bob{constructor(q=.9,f=.01){this.zi=0;this.s=[0,0,0,0];this.sf(f);this.sq(q);this.lp=0;}sf(fc){this.f=Math.tan(Math.PI*fc*ditty.dt);return this;}sq(reso){this.r=(40.0/9.0)*reso;return this;}process(v0){var s=this.s;var f=this.f;var r=this.r;var ih=0.5*(v0+this.zi);this.zi=v0;var t0=tanhXdX(ih-r*s[3]);var t1=tanhXdX(s[0]);var t2=tanhXdX(s[1]);var t3=tanhXdX(s[2]);var t4=tanhXdX(s[3]);var g0=1/(1+f*t1),g1=1/(1+f*t2);var g2=1/(1+f*t3),g3=1/(1+f*t4);var f3=f*t3*g3;var f2=f*t2*g2*f3;var f1=f*t1*g1*f2;var f0=f*t0*g0*f1;var y3=(g3*s[3]+f3*g2*s[2]+f2*g1*s[1]+f1*g0*s[0]+f0*v0)/(1+r*f0);var xx=t0*(v0-r*y3);var y0=t1*g0*(s[0]+f*xx);var y1=t2*g1*(s[1]+f*y0);var y2=t3*g2*(s[2]+f*y1);s[0]+=2*f*(xx-y0);s[1]+=2*f*(y0-y1);s[2]+=2*f*(y1-y2);s[3]+=2*f*(y2-t4*y3);this.lp=y3;return this;}}class Delayline{constructor(n){this.n=n;this.p=0;this.data=new Float32Array(n);}get end(){return this.data[this.p];}tap(offset){var x=this.p+offset;x=~~x;x%=this.n;return this.data[x];}sample(offset){if(offset<0)offset=0;var p=offset;var pi=~~p;return lerp(this.tap(pi),this.tap(pi+1),p-pi);}clock(input){var end=this.data[this.p];this.data[this.p]=input;if(++this.p>=this.n){this.p=0;}return end;}}const echo=filter.def(class{constructor(opt){this.lastOut=[0,0];var division=opt.division||1;var pan=clamp01((opt.pan||0)*.5+.5);var sidetime=(opt.sidetime||0)/ditty.dt;var time=60*division/ditty.bpm;this.fb=clamp(opt.feedback||0,-1,1);this.kl=1-pan;this.kr=pan;this.wet=opt.wet||.5;this.stereo=isFinite(opt.stereo)?opt.stereo:1;var n=~~(time/ditty.dt);this.delay=[new Delayline(n),new Delayline(n)];this.dside=new Delayline(~~sidetime);}process(inv,opt){this.dside.clock(inv[0]);var l=this.dside.end*this.kl;var r=inv[1]*this.kr;var nextl=l+this.delay[1].end*this.fb;var nextr=r+this.delay[0].end*this.fb;this.lastOut[0]=inv[0]+this.delay[0].end*this.wet;this.lastOut[1]=inv[1]+this.delay[1].end*this.wet;this.delay[0].clock(nextl);this.delay[1].clock(nextr);if(this.stereo!=1){var m=(this.lastOut[0]+this.lastOut[1])*.5;var s=(this.lastOut[0]-this.lastOut[1])*.5;s*=this.stereo;this.lastOut[0]=m+s;this.lastOut[1]=m-s;}return this.lastOut;}},{sidetime:.01,division:3/3,pan:.5,wet:.3,feedback:.5,stereo:1.5});function fft(real,imag,fftFrameSize,sign){var temp;for(var i=1;i<fftFrameSize-1;++i){for(var j=0,bitm=1;bitm<fftFrameSize;bitm<<=1){j<<=1;if(i&bitm)j+=1;}if(i<j){temp=real[i];real[i]=real[j];real[j]=temp;temp=imag[i];imag[i]=imag[j];imag[j]=temp;}}for(var le=1;le<fftFrameSize;){var le2=le;le<<=1;var ur=1,ui=0;var arg=PI/le2;var wr=cos(arg);var wi=sign*sin(arg);for(var j=0;j<le2;++j){var p1=j;var p2=p1+le2;for(var i=j;i<fftFrameSize;i+=le){var tr=real[p2]*ur-imag[p2]*ui;var ti=real[p2]*ui+imag[p2]*ur;real[p2]=real[p1]-tr;imag[p2]=imag[p1]-ti;real[p1]+=tr;imag[p1]+=ti;p1+=le;p2+=le;}tr=ur*wr-ui*wi;ui=ur*wi+ui*wr;ur=tr;}}}class SVF{constructor(){Object.assign(this,{lp:0,hp:0,ap:0,no:0,ic1eq:0,ic2eq:0,a1:0,a2:0,a3:1,q:1});}sp(fc,q){fc=clamp(fc*ditty.dt,1e-9,.5);let s=this;if(isFinite(q))s.q=max(q,0);let g=tan(PI*fc);s.k=1/s.q;s.a1=1/(1+g*(g+s.k));s.a2=g*s.a1;s.a3=g*s.a2;return s;}process(v0){let s=this;let v1,v2,v3;v3=v0-s.ic2eq;v1=s.a1*s.ic1eq+s.a2*v3;v2=s.ic2eq+s.a2*s.ic1eq+s.a3*v3;s.ic1eq=2*v1-s.ic1eq;s.ic2eq=2*v2-s.ic2eq;s.lp=v2;s.bp=v1;s.hp=v0-s.k*v1-v2;s.no=s.lp+s.hp;s.ap=s.lp+s.hp-s.k*s.bp;return s;}cp(){let r=new SVF();r.k=this.k;r.a1=this.a1;r.a2=this.a2;r.a3=this.a3;return r;}}
ditty.bpm = 90;
let bass = synth.def((p,e,t,o)=>tanh(sin(p*TAU)*(exp(-t*4)*4+t*2+sin(p*TAU*2.01)))*e.value);
let filtered = synth.def((p,e,t,o)=>o.filter.process(Math.random()-.5).bp * e.value);
let hash = (v) => sin(v*135897)*.5+.5
loop(lc => {
let scal = scale(d5, scales["aeolian"], 2);
let range = [4, 6, 12, 7, 15][lc%5];
[0, 0, 4, 3, 0, 0, 5, 6].forEach(cho => {
if(ditty.tick >= 16)
bass.play(scal[cho]-12*4, {attack: 0, duration:1.9, release:.1, amp:.5});
for(let i = 0; i < 4; ++i) {
let n = scal[~~(hash(i+cho)*range) % scal.length];
let filter = new SVF().sp(midi_to_hz(n), 40);
filtered.play(n, {attack: .01, duration:0, release:1.5, filter:filter.cp(), pan:-1, amp:.5});
filtered.play(n, {attack: .01, duration:0, release:1.5, filter:filter.cp(), pan:1, amp:.5});
sleep(.5);
}
});
}, {amp:(t,o)=>min(t/16,1)*.4});