//Axel F-Beverly Hills Cop-v2
ditty.bpm = 35;
// --- Sintetizadores ---
// Melodía: sierra brillante
const lead = synth.def((phase, env, tick, opt) =>
{
// Normalizar fase a 0..1
//const ph = (phase / (2 * Math.PI)) % 1.0;
const wave = 2 * (phase % 1) - 1; // saw
return wave * env.value;
});
// Bajo: cuadrada gruesa + más sustain
const bass = synth.def((phase, env, tick, opt) => {
const wave = (phase % 1 < 0.5) ? 1 : -1; // square
return wave * env.value;
});
// Kick: ruido con pitch descendente (simulación simple)
const kick = synth.def((phase, env, tick, opt) => {
const t = tick; // tiempo real estable en Dittytoy
// Evitar valores inválidos
const freq = Math.max(20, 150 - t * 300);
const s = Math.sin(t * freq * 2.0 * Math.PI);
// Protecciones contra NaN
if (isNaN(s)) return 0;
return s * env.value * 0.9;
});
// Hi-hat: ruido blanco corto
const hihat = synth.def((phase, env, tick, opt) => {
return (Math.random() * 2 - 1) * env.value * 0.4;
});
// === MELODÍA PRINCIPAL (tu secuencia original) ===
const leadNotes = [
[0.00000, 53.0, 0.25000],
[0.25000, 56.0, 0.43750],
[0.43750, 53.0, 0.56250],
[0.56250, 53.0, 0.62500],
[0.62500, 58.0, 0.75000],
[0.75000, 53.0, 0.87500],
[0.87500, 51.0, 1.00000],
[1.00000, 53.0, 1.25000],
[1.25000, 60.0, 1.43750],
[1.43750, 53.0, 1.56250],
[1.56250, 53.0, 1.62500],
[1.62500, 61.0, 1.75000],
[1.75000, 60.0, 1.87500],
[1.87500, 56.0, 2.00000],
[2.00000, 53.0, 2.12500],
[2.12500, 60.0, 2.25000],
[2.25000, 65.0, 2.37500],
[2.37500, 53.0, 2.43750],
[2.43750, 51.0, 2.56250],
[2.56250, 51.0, 2.62500],
[2.62500, 48.0, 2.75000],
[2.75000, 55.0, 2.87500],
[2.87500, 53.0, 3.62500],
[4.37500, 53.0, 4.62500],
[4.62500, 56.0, 4.81250],
[4.81250, 53.0, 4.93750],
[4.93750, 53.0, 5.00000],
[5.00000, 58.0, 5.12500],
[5.12500, 53.0, 5.25000],
[5.25000, 51.0, 5.37500],
[5.37500, 53.0, 5.62500],
[5.62500, 60.0, 5.81250],
[5.81250, 53.0, 5.93750],
[5.93750, 53.0, 6.00000],
[6.00000, 61.0, 6.12500],
[6.12500, 60.0, 6.25000],
[6.25000, 56.0, 6.37500],
[6.37500, 53.0, 6.50000],
[6.50000, 60.0, 6.62500],
[6.62500, 65.0, 6.75000],
[6.75000, 53.0, 6.81250],
[6.81250, 51.0, 6.93750],
[6.93750, 51.0, 7.00000],
[7.00000, 48.0, 7.12500],
[7.12500, 55.0, 7.25000],
[7.25000, 53.0, 8.00000],
[8.00000, 29.0, 8.25000],
[8.25000, 41.0, 8.43750],
[8.43750, 27.0, 8.56250],
[8.56250, 39.0, 8.62500],
[8.62500, 24.0, 8.75000],
[8.75000, 36.0, 8.87500],
[8.87500, 27.0, 9.00000],
[9.00000, 29.0, 9.25000],
[9.25000, 41.0, 9.43750],
[9.62500, 24.0, 9.68750],
[9.68750, 36.0, 9.81250],
[9.81250, 39.0, 9.93750],
[9.93750, 41.0, 10.06250],
[10.06250, 25.0, 10.31250],
[10.31250, 37.0, 10.50000],
[10.50000, 27.0, 10.62500],
[10.62500, 39.0, 10.68750],
[10.68750, 24.0, 10.81250],
[10.81250, 36.0, 10.93750],
[10.93750, 27.0, 11.06250],
[11.06250, 29.0, 11.31250]
];
// === LÍNEA DE BAJO (basada en la progresión de acordes: Fm - C - Ab - Bb) ===
const bassNotes = [
[0.0, 36, 0.5], // C2 (raíz de C)
[0.5, 41, 1.0], // F2 (raíz de Fm)
[1.0, 36, 1.5],
[1.5, 41, 2.0],
[2.0, 36, 2.5],
[2.5, 41, 3.0],
[3.0, 43, 4.0], // Ab2 (raíz de Ab)
[4.0, 36, 4.5],
[4.5, 41, 5.0],
[5.0, 36, 5.5],
[5.5, 41, 6.0],
[6.0, 36, 6.5],
[6.5, 41, 7.0],
[7.0, 43, 8.0],
[8.0, 36, 8.5],
[8.5, 41, 9.0],
[9.0, 36, 9.5],
[9.5, 41, 10.0],
[10.0, 36, 10.5],
[10.5, 41, 11.0],
[11.0, 43, 12.0]
];
// === PERCUSIÓN (patrón de 2 compases: kick en 1 y 3, hi-hat en corcheas) ===
const percNotes = [];
const loopLength = 8.0; // 2 compases de 4/4 a 120 BPM = 8 beats
for (let t = 0; t < 12; t += 0.25) {
// Hi-hat en cada 1/4 nota
percNotes.push({ type: 'hat', time: t, dur: 0.05 });
// Kick en tiempos 0, 2, 4, 6...
if (t % 2 === 0) {
percNotes.push({ type: 'kick', time: t, dur: 0.3 });
}
}
// Añadir un extra kick en el compás 2 (como en la canción)
percNotes.push({ type: 'kick', time: 2.5, dur: 0.2 });
// --- Combinar y ordenar todo ---
// Convertir a formato común y mezclar
const allEvents = [];
// Añadir melodía
leadNotes.forEach(([start, midi, end]) => {
allEvents.push({ time: start, type: 'lead', midi, dur: end - start });
});
// Añadir bajo
bassNotes.forEach(([start, midi, end]) => {
allEvents.push({ time: start, type: 'bass', midi, dur: end - start });
});
// Añadir percusión
percNotes.forEach(e => {
allEvents.push({ time: e.time, type: e.type, dur: e.dur });
});
// Ordenar por tiempo
allEvents.sort((a, b) => a.time - b.time);
// --- Loop principal ---
loop(() => {
let currentTime = 0;
const minStep = 0.0625; // 1/16
let eventIndex = 0;
const totalDuration = Math.max(...allEvents.map(e => e.time + e.dur)) + 0.5;
while (currentTime <= totalDuration) {
// Disparar eventos en este instante
while (
eventIndex < allEvents.length &&
Math.abs(allEvents[eventIndex].time - currentTime) < 1e-5
) {
const ev = allEvents[eventIndex];
if (ev.type === 'lead') {
lead.play(ev.midi+24, {
duration: ev.dur,
attack: 0.01,
decay: 0.15,
sustain: 0.2,
release: 0.1,
amp: 0.7
});
} else if (ev.type === 'bass') {
bass.play(ev.midi, {
duration: ev.dur,
attack: 0.02,
decay: 0.3,
sustain: 0.4,
release: 0.2,
amp: 0.85
});
} else if (ev.type === 'kick') {
kick.play(0, { // nota no importa, pitch interno
duration: ev.dur,
attack: 0.01,
//decay: ev.dur - 0.02,
decay: Math.max(0.01, ev.dur - 0.02),
sustain: 0,
release: 0.05,
amp: 0.9
});
} else if (ev.type === 'hat') {
hihat.play(0, {
duration: ev.dur,
attack: 0.001,
decay: ev.dur,
sustain: 0,
release: 0.02,
amp: 0.5
});
}
eventIndex++;
}
sleep(minStep);
currentTime += minStep;
}
// Reiniciar
eventIndex = 0;
}, { name: 'Axel F - Full Version (Bass+D)' });