ditty.bpm = 170;
// it's extremely minified because it's reused from a schroeder algorithm I had made. also, some of the implementations
// might be wrong
class CircularBuffer{constructor(size){this.buffer=Array(size).fill(0);this.bufferLength=size;this.head=this.tail=0}cubicInterpolation(y0,y1,y2,y3,mu){var a0=0;var a1=0;var a2=0;var a3=0;var mu2=mu*mu;a0=-.5*y0+1.5*y1-1.5*y2+.5*y3;a1=y0-2.5*y1+2*y2-.5*y3;a2=-.5*y0+.5*y2;a3=y1;return a0*mu*mu2+a1*mu2+a2*mu+a3}getSample(inValue){this.tail=this.head-inValue;if(this.tail>this.bufferLength-1){this.tail-=this.bufferLength}else if(this.tail<0){this.tail+=this.bufferLength}return this.buffer[this.tail]}write(inValue){this.head=(this.head+=1)%this.bufferLength;this.buffer[this.head]=inValue}readCubic(index){var mu=index-Math.floor(index);return this.cubicInterpolation(this.getSample(Math.floor(index)+1),this.getSample(Math.floor(index)),this.getSample(Math.floor(index)-1),this.getSample(Math.floor(index)-2),mu)}}
class AllPass{constructor(inValue,inG,sr){this.delayLength=inValue;this.g=inG;this.delayedSample=this.feedForwardSample=0;this.buffer=new CircularBuffer(sr)}process(sample){this.delayedSample=this.buffer.readCubic(this.delayLength);this.buffer.write(sample+this.delayedSample*this.g);this.feedForwardSample=sample*-this.g;return this.delayedSample+this.feedForwardSample}setFeedback(i){this.g=i}}
class Reverb {
constructor (SR,delays,decay=.5,dry=1.,wet=.5,enableLowpass=true,cutoff=.4) {
this.SR = SR;
this.options = {decay:decay,dry:dry,wet:wet};
this.t = Array(delays.length).fill(0);
this.bufs = delays.map(x=>Array(x).fill(0));
this.alls = delays.map(x=>new AllPass(x,Math.random()/4+.25,SR))
this.eLP = enableLowpass;
this.l = 0;
this.rl = 0
this.cutoff = cutoff;
}
changeOptions (opt) {
for (let i=0;i<Object.values(opt).length;i++) {
this.options[Object.keys(opt)[i]] = Object.values(opt)[i];
}
}
process (x) {
for (let i=0;i<this.bufs.length;i++) {
this.t[i] = (this.t[i]+1)%this.bufs[i].length;
this.bufs[i][this.t[i]] = (i==0?x:this.bufs[i-1][this.t[i-1]])+this.alls[i].process(this.bufs[i][this.t[i]]*(this.options.decay))||0
}
let o;
if (this.eLP) {
this.l = this.bufs[this.bufs.length-1][this.t[this.bufs.length-1]]*this.cutoff+this.l*(1-this.cutoff);
this.rl = x*this.cutoff+this.rl*(1-this.cutoff);
o = this.l;
}
else {
o=this.bufs[this.bufs.length-1][this.t[this.bufs.length-1]]-x;
}
return (o-(this.eLP?this.rl:0))*this.options.wet+x*this.options.dry
}
}
let StereoReverbPreset = filter.def(class {
constructor (options) {
this.SR = 48e3;
this.eff = [new Reverb(48e3,[5481,9547,12287,13256],.5,1.,.2),new Reverb(48e3,[5485,9549,12288,13257],.5,1.,.2)];
this.x = [0,0]; // i'm adding a hp filter because it's really unstable sometimes
}
process (input,options) {
let unFiltOut = [this.eff[0].process(input[0]),this.eff[1].process(input[1])];
// there might be a quicker method for this but idrk
unFiltOut.forEach((x,i)=>{
this.x[i] += (x - this.x[i])/80;
})
return this.x.map((x,i)=>x-unFiltOut[i]);
}
}
)
const syn = synth.def( (phase, env) => ((phase*2&1)-.5) * env.value );
const syn2 = synth.def( (phase, env) => ((phase%1)-.5) * env.value );
loop( () => {
scale(db,scales.minor,1).forEach(x=>{
syn.play(x);
sleep(1)
})
scale(db,scales.minor,1).forEach(x=>{
syn2.play(x);
sleep(.5)
})
scale(db,scales.minor,1).forEach(x=>{
syn2.play(x-12);
sleep(.5)
})
}).connect(StereoReverbPreset.create());