///por jorge2017a2-26-dic-2025
ditty.bpm = 20;
const sawLeadBlueMonday = synth.def((phase, env, tick) => {
// --------------------------------------------------
// osc_saw(x, v)
// --------------------------------------------------
function osc_saw(x, v) {
const n = (x / (2 * Math.PI)) % 1; // mod(x/2π, 1)
const saw = Math.max(-1, Math.min(1, n * 2 - 1));
return saw * v;
}
// --------------------------------------------------
// sawlead01 / sawlead02
// --------------------------------------------------
function sawlead01(f) {
return osc_saw(f, 1.0) + osc_saw(f * 2.01, 1.0);
}
function sawlead02(f) {
return osc_saw(f * 0.51, 0.51) + osc_saw(f * 1.025, 1.0);
}
// --------------------------------------------------
// Equivalente a: ff * time * 3.0
// phase ya es frecuencia integrada → usamos phase
// --------------------------------------------------
const f = phase * 3.0;
let fx1 = sawlead01(f);
let fx2 = sawlead02(f);
let fx = (fx1 + fx2) * 0.5;
const t = phase / (2 * Math.PI);
// saw principal
const saw1 = (t % 1) * 2 - 1;
// sub oscilador (octava abajo)
const saw2 = ((t * 0.5) % 1) * 2 - 1;
// mezcla
const sig = (saw1 * 0.7 + saw2 * 0.3);
// ligera saturación estilo analógico
fx = Math.tanh((fx * 1.3+sig*2.0)/2.0);
return fx * env.value * 0.7;
});
const testNotes = [
[0.00000, 53, 0.06250],
[0.06250, 53, 0.12500],
[0.12500, 53, 0.18750],
[0.18750, 55, 0.25000],
[0.25000, 48, 0.31250],
[0.31250, 48, 0.37500],
[0.37500, 48, 0.43750],
[0.43750, 50, 0.50000],
[0.50000, 50, 0.56250],
[0.56250, 50, 0.62500],
[0.62500, 50, 0.68750],
[0.68750, 50, 0.75000],
[0.75000, 50, 0.81250],
[0.81250, 50, 0.87500],
[0.87500, 50, 0.93750],
[0.93750, 50, 1.00000],
[1.00000, 53, 1.06250],
[1.00000, 53, 1.06250],
[1.06250, 53, 1.12500],
[1.12500, 53, 1.18750],
[1.12500, 53, 1.18750],
[1.18750, 55, 1.25000],
[1.25000, 48, 1.31250],
[1.31250, 48, 1.37500],
[1.37500, 48, 1.43750],
[1.43750, 54, 1.50000],
[1.50000, 54, 1.56250],
[1.56250, 54, 1.62500],
[1.62500, 54, 1.68750],
[1.68750, 54, 1.75000],
[1.75000, 54, 1.81250],
[1.81250, 54, 1.87500],
[1.87500, 54, 1.93750],
[1.93750, 54, 2.00000],
[2.00000, 29, 2.06250],
[2.06250, 41, 2.12500],
[2.12500, 29, 2.18750],
[2.18750, 41, 2.25000],
[2.25000, 24, 2.31250],
[2.31250, 36, 2.37500],
[2.37500, 24, 2.43750],
[2.43750, 36, 2.50000],
[2.50000, 26, 2.56250],
[2.56250, 38, 2.62500],
[2.62500, 26, 2.68750],
[2.68750, 38, 2.75000],
[2.75000, 26, 2.81250],
[2.81250, 38, 2.87500],
[2.87500, 26, 2.93750],
[2.93750, 38, 3.00000],
[3.00000, 31, 3.06250],
[3.06250, 43, 3.12500],
[3.12500, 31, 3.18750],
[3.18750, 43, 3.25000],
[3.25000, 24, 3.31250],
[3.31250, 36, 3.37500],
[3.37500, 24, 3.43750],
[3.43750, 36, 3.50000],
[3.50000, 26, 3.56250],
[3.56250, 38, 3.62500],
[3.62500, 26, 3.68750],
[3.68750, 38, 3.75000],
[3.75000, 26, 3.81250],
[3.81250, 38, 3.87500],
[3.87500, 26, 3.93750],
[3.93750, 38, 4.00000],
[4.00000, 29, 4.06250],
[4.00000, 45, 4.06250],
[4.00000, 50, 4.06250],
[4.06250, 41, 4.12500],
[4.06250, 45, 4.12500],
[4.06250, 53, 4.12500],
[4.12500, 29, 4.18750],
[4.12500, 45, 4.18750],
[4.12500, 50, 4.18750],
[4.18750, 41, 4.25000],
[4.18750, 45, 4.25000],
[4.18750, 53, 4.25000],
[4.18750, 45, 4.25000],
[4.25000, 24, 4.31250],
[4.25000, 45, 4.31250],
[4.25000, 50, 4.31250],
[4.31250, 36, 4.37500],
[4.31250, 45, 4.37500],
[4.31250, 53, 4.37500],
[4.37500, 24, 4.43750],
[4.37500, 45, 4.43750],
[4.37500, 50, 4.43750],
[4.43750, 36, 4.50000],
[4.43750, 45, 4.50000],
[4.43750, 48, 4.50000],
[4.50000, 26, 4.56250],
[4.50000, 45, 4.62500],
[4.50000, 50, 4.62500],
[4.56250, 38, 4.62500],
[4.62500, 45, 4.75000],
[4.62500, 48, 4.75000],
[4.62500, 26, 4.68750],
[4.68750, 38, 4.75000],
[4.75000, 26, 4.81250],
[4.75000, 45, 4.87500],
[4.75000, 50, 4.87500],
[4.81250, 38, 4.87500],
[4.87500, 26, 4.93750],
[4.93750, 38, 5.00000],
[5.00000, 31, 5.06250],
[5.00000, 50, 5.06250],
[5.00000, 53, 5.06250],
[5.06250, 43, 5.12500],
[5.06250, 50, 5.12500],
[5.06250, 53, 5.12500],
[5.12500, 31, 5.18750],
[5.12500, 50, 5.18750],
[5.12500, 53, 5.18750],
[5.18750, 43, 5.25000],
[5.18750, 50, 5.25000],
[5.18750, 53, 5.25000],
[5.25000, 24, 5.31250],
[5.25000, 50, 5.31250],
[5.25000, 53, 5.31250],
[5.31250, 36, 5.37500],
[5.31250, 50, 5.37500],
[5.31250, 53, 5.37500],
[5.37500, 24, 5.43750],
[5.37500, 50, 5.43750],
[5.37500, 53, 5.43750],
[5.43750, 36, 5.50000],
[5.43750, 50, 5.50000],
[5.43750, 53, 5.50000],
[5.50000, 26, 5.56250],
[5.50000, 50, 5.62500],
[5.50000, 53, 5.62500],
[5.56250, 38, 5.62500],
[5.62500, 26, 5.68750],
[5.62500, 50, 5.75000],
[5.62500, 55, 5.75000],
[5.68750, 38, 5.75000],
[5.75000, 26, 5.81250],
[5.75000, 45, 5.87500],
[5.75000, 50, 5.87500],
[5.81250, 38, 5.87500],
[5.87500, 26, 5.93750],
[5.93750, 38, 6.00000],
];
// ------------------------------------------------------------
// Player
// ------------------------------------------------------------
let i = 0;
const totalTime = testNotes[testNotes.length - 1][2];
loop(() => {
let t = 0;
const step = 0.03125;
while (t <= totalTime) {
while (i < testNotes.length && Math.abs(testNotes[i][0] - t) < 1e-6) {
const [s, note, e] = testNotes[i];
sawLeadBlueMonday.play(note+36, {
duration: e - s,
attack: 0.01,
release: 0.09,
amp: 0.8
});
i++;
}
sleep(step);
t += step;
}
i = 0;
}, { name: "Blue Monday (Dittytoy)" });