// ┌────────────────────────────────┐
// 2024 nabr ^;.;^
// dittytoy.net/ditty/3e2ac07cd2
// └────────────────────────────────┘
class Song
{
period = 0;
constructor()
{
this.revInst = new v1Rev();
this.revBass = new v1Rev();
};
stwidth(xin, kw)
{
const tmp = 0.5 * kw;
const nL = tmp * (xin[1] - xin[0]);
const nR = (1.0 - tmp) * (xin[1] + xin[0]);
return [nR + nL, nR - nL]
};
fio(fadeout, fadein, scenetime) { return smoothstep(fadeout, fadein, scenetime)};
decimator(x, n) //from musicdsp.org
{
let qn = 4; // pow( 2.0, 2 );
return lerp(x, floor(x * qn + .5) / qn, .5);
};
drm(t, ch)
{
let kk = 0.0, sn = 0.0, x = 0.0;
let tt = t * 3., ft = mod(tt, 1.);
let ut = int(tt) % 12;
// kick
kk = .316 * sin(471. * ft * exp(-10. * ft)) + .707 * sin(311. * ft * exp(-2. * ft));
kk *= elem(1., 0., 0., 1., 0., 0., 1., 0., 1., 1., 0., 0.)[ut];
kk *= atan2(1 - ft, 10. * ft); //fun
// snare
x = t * 5623.123456;
sn = mod(x * mod(t + x, 1.), 1.);
sn += .5 * sin(6.283185 * t * 170.) + sin(6.283185 * t * 300.) * exp(-10. * ft);
sn *= elem(0., 0., 1., 0., 0., 1., 0., 1., 0., 0., 1., 1.)[ut];
let isrev = 0;
let att = 60.;
let sna = .51;
if (ch === isrev) {
att = 10.; // opens env. to let more snare noise pass
kk = this.decimator(kk); // a bit more fur
sna = .23;
}
sn *= (max(0, (1 - ft), min(1, 20. * ft)) / max(1, att * ft));
return (kk + sna * sn);
};
bass(t, ch)
{
let o = 0.0, x = 0.0;
let ft = (mod(.5 * t, 4.) < 3 ? .5 * t : 2. * t);
let y = sin(tau * 1.51 * t);
let p = tau * 200. * elem((mod(2. * t, 4.) > 2 ? .544 : .614), 0.368, .411, (mod(2. * t, 4.) > 2 ? .737 : 0.548))[int(.5 * t) % 4];
x = (ch == 0) ? (y * cos(.5 * p * t) + sin(4. * p * t)) : (y * cos(2. * p * t) + sin(4. * p * t));
o = .7 * x + cos(2. * exp(-.72 * x));
o *= min(1., 100. * fract(ft)) * max(0., 1. - fract(ft));
return .32 * o;
};
pipes(t)
{
let y = 0., ph = 0., mph = 0., env = 0.;
ph = (elem(.5, 1)[int(1. * t) % 2] * elem(220, 330)[int(6. * t) % 2]);
mph = elem(0.375, .5 * 0.899, 0.75)[int(3. * t) % 3];
env = fract(6. * t) * exp(1. + log(1. - fract(6. * t)));
y = mod(.1 * env + 1.5 * ph * t / mph, 1);
y = (abs(cos(tau * y) - .5) * pow(env, 1.));
return y;
};
//main instrument
inst(t)
{
let ylw = 0., yhg = 0., env = 0, a = 0., b = 0.;
a = fract(6. * t)
b = (1. - a);
env = min(1., 50. * fract(6. * t)) * (1. - fract(6. * t));
let ph = elem(110, 165, 110, 165, 110, 165, 220, 330, 220, 330, 220, 330)[int(6. * t) % 12];
ph /= (mod(t, 3.) < 1.0 ? (elem(0.375, 0.89, 0.75)[int(3. * t) % 3]) : (elem(0.25, 0.375, 1, 0.5)[int(3. * t) % 4]));
ylw = sin(mod(.2 * env + ph * t, 1) * tau + lerp(-1., 1, sin(.3 * floor(t)) * .5 + .5));
yhg = (sin(3. * ylw - .5) / elem(.7, .4)[int(t) % 2] * ylw * ylw);
ylw *= copysign(min(a, b) / max(a, b), 1.);
yhg *= abs(cos(3. * yhg) - (.7 + .755 * cos(t))) * env;
return tanh(ylw + yhg * (.4 + .4 * sin(t)));
};
process()
{
let oinst = [0, 0], obass = [0, 0], otsum = [0, 0];
this.period += 1;
let t = int(this.period) / ditty.sampleRate;
let dplucked = .7 * this.inst(t) + (mod(t, 22.) > [19, 14][int(t / 5) % 2] ? .5 * this.pipes(t) : 0);
let dbass = (.7 * this.bass(t, 1) + .3 * this.bass(t, 0));
//
oinst = this.revInst.process(dplucked, int(this.period), 0.82, 0.95);
oinst = [0, 0].map((v, id) => lerp(dplucked, oinst[id], .3));
oinst = this.stwidth(oinst, 0.72);
//
obass = this.revBass.process(dbass + .4 * this.drm(t, 0), int(this.period), 0.7, 0.767);
obass = [0, 0].map((v, id) => lerp(dbass, obass[id], .4));
obass = this.stwidth(obass, 0.55);
let fdt = mod(t, 116.);
return [0, 0].map((v, id) => this.fio(57., 58., fdt) * this.fio(116., 115., fdt) * (obass[id] + this.drm(t, 1)) + oinst[id]);
};
};
class v1Rev
{
z1 = Array(8).fill().map(() => Array(6859).fill(0));
dt = [2048, 2187, 3125, 2401, 1331, 2197, 4913, 6859];
lpf_fltrState1RC = [0, 0];
dump_fltrState = 0;
constructor() { };
damp(xin, a, state)
{
let q = tau * exp(a * 10) / 44100;
q = q / (1 + q);
let yot = q * xin + (1 - q) * state;
state = yot;
return yot;
};
lpf_1rc(xin) //from musicdsp.org
{
let state = this.lpf_fltrState1RC;
let c = 0.2002674; //pow(0.5, (128-0.71*128) / 16.0);
let r = 0.3120826; //pow(0.5, (0.12*24+24) / 16.0);
state[0] = (1 - r * c) * state[0] - c * state[1] + c * xin;
state[1] = (1 - r * c) * state[1] + c * state[0];
let tmp = state[1];
state[0] = (1 - r * c) * state[0] - c * state[1] + c * xin;
state[1] = (1 - r * c) * state[1] + c * state[0];
return (tmp + state[1]) * 0.5;
}
process(sgnl_in, samp, damping_amount, feedback)
{
let rv_ot = [0,0];
let x = Array(8).fill(0), y = Array(8).fill(0);
//diffuser
sgnl_in = this.lpf_1rc(sgnl_in);
const tanksz = 8;
for (let i = 0; i < tanksz; i++) {
const g = exp(-2. * ditty.dt * this.dt[i]); // second gain damping
y[i] = this.z1[i][samp % this.dt[i]];
for (let k = 0; k < tanksz; k++) {
let beatingtremolo = exp(1. / (1 + k % 7));
x[i] =
this.damp(sgnl_in * beatingtremolo, damping_amount, this.dump_fltrState)
* [[1, 1, 1, 1, 1, 1, 1, 1],
[1, -1, 1, -1, 1, -1, 1, -1],
[1, 1, -1, -1, 1, 1, -1, -1],
[1, -1, -1, 1, 1, -1, -1, 1],
[1, 1, 1, 1, -1, -1, -1, -1],
[1, -1, 1, -1, -1, 1, -1, 1],
[1, 1, -1, -1, -1, -1, 1, 1],
[1, -1, -1, 1, -1, 1, 1, -1]][i][k] * g;
}
this.z1[i][samp % this.dt[i]] = g * x[i] + y[i] * feedback;
y[i] *= (-g) * 0.353553;
//out
rv_ot[0] += y[i] * elem(1, 1, -1, -1, 1, -1)[i % 6];
rv_ot[1] += y[i] * elem(-1, 1, -1, 1, 1, 1)[i % 6];
}
return rv_ot;
};
};
synth.def(Song).play(0, { duration: 500 });
// stuff i frequently use
const { abs, acos, asin, atan, atan2, ceil, clz32, cos, exp, floor, imul, fround, log, max, min, pow, random, round, sin, sqrt, tan, log10, log2, log1p, expm1, cosh, sinh, tanh, acosh, asinh, atanh, hypot, trunc, sign, cbrt,/*E,*/LOG2E, LOG10E, LN2, LN10, PI, SQRT2, SQRT1_2 } = Math;
const int = (x) => x | 0;
const tau = PI * 2.0;
const hpi = PI * .5;
const fract = (x) => (x - floor(x));
const mod = (x, y) => (x - floor(x / y) * y);
const exp2 = (x) => exp(x * LN2);
const dbg = (x) => debug.warn(' ', x);
const elem = (...x) => [...x];
const copysign = (x, y) => sign(x) === sign(y) ? x : -x;
const smoothstep = (e0, e1, value) => { const x = max(0, min(1, (value - e0) / (e1 - e0))); return x * x * (3 - 2 * x); }