Knight Rider v4 - Procs

Knight Rider v4 - Procs

Log in to post a comment.

ditty.bpm = 30; // Knight Rider está alrededor de 100 BPM

// --- Synth: Knight Rider Procs ---
const knightRider = synth.def(class {
  constructor(options) {
    this.time = 0; // tiempo absoluto en segundos
  }
  
  process(note, env, tick, options) {
    // Actualizar tiempo
    this.time += ditty.dt;
    const t = this.time;
    
    // Convertir MIDI a frecuencia
    const freq = 440 * Math.pow(2, (note - 69) / 12);
    
    // --- Osciladores ---
    // Onda de sierra 1
    const saw1 = (t * freq) % 1 * 2 - 1;
    // Onda de sierra 2 (desafinada +1.25%)
    const saw2 = (t * freq * 1.0125) % 1 * 2 - 1;
    // Onda cuadrada
    const sqr = Math.sin(2 * Math.PI * freq * t) >= 0 ? 1 : -1;
    
    // Mezcla base
    const osc = (0.5 * (saw1 + saw2) * 0.6 + sqr * 0.4);
    
    // Vibrato (5 Hz)
    const vib = Math.sin(2 * Math.PI * 5 * t) * 0.003;
    const oscV = Math.sin(2 * Math.PI * freq * (1 + vib) * t);
    
    // Mezcla con vibrato (25%)
    const raw = osc * 0.75 + oscV * 0.25;
    
    // --- Filtro dinámico simulado ---
    // Envolvente de tiempo: alto al inicio, baja con el tiempo
    // Aproximamos con env.value (pero env es por nota, no global)
    // Así que usamos una envolvente basada en tiempo desde el inicio de la nota
    // → Pero Dittytoy no da "tiempo desde inicio de nota"...
    // → Solución: usar env.value como proxy (no perfecto, pero funciona)
    const envVal = env.value;
    // Simular corte: atenuar altas frecuencias cuando envVal es bajo
    // Hacemos un "low-pass" simple: mezclar con versión suavizada
    const cutoff = 0.3 + 0.6 * envVal; // más abierto al inicio
    // Aplicar suavizado proporcional al cutoff (simulación de filtro)
    const filtered = raw * cutoff + (raw > 0 ? 1 : -1) * (1 - cutoff) * 0.3;
    
    // --- Saturación suave ---
    const saturated = Math.tanh(filtered * 1.8);
    
    return saturated * 0.6; // ajustar volumen global
  }
});

// --- Notas de Knight Rider (tu secuencia) ---
const notes = [
  [0.000,18.0,0.125], [0.000,37.0,0.125], [0.125,38.0,0.188], [0.188,37.0,0.250], [0.250,37.0,0.313], [0.250,18.0,0.375],
  [0.313,38.0,0.375], [0.375,37.0,0.438], [0.438,37.0,0.500], [0.500,38.0,0.563], [0.500,18.0,0.625], [0.563,37.0,0.625],
  [0.625,37.0,0.688], [0.688,37.0,0.750], [0.750,36.0,0.813], [0.813,37.0,0.875], [0.875,37.0,0.938], [0.938,37.0,1.000],
  [1.000,37.0,1.125], [1.000,18.0,1.125], [1.125,38.0,1.188], [1.188,37.0,1.250], [1.250,37.0,1.313], [1.313,38.0,1.375],
  [1.375,37.0,1.438], [1.438,37.0,1.500], [1.500,38.0,1.563], [1.500,18.0,1.625], [1.563,37.0,1.625], [1.625,37.0,1.688],
  [1.688,37.0,1.750], [1.750,36.0,1.813], [1.813,37.0,1.875], [1.875,37.0,1.938], [1.938,37.0,2.000], [2.000,35.0,2.125],
  [2.000,16.0,2.125], [2.125,36.0,2.188], [2.188,35.0,2.250], [2.250,35.0,2.313], [2.250,16.0,2.375], [2.313,36.0,2.375],
  [2.375,35.0,2.438], [2.438,35.0,2.500], [2.500,36.0,2.563], [2.500,16.0,2.625], [2.563,35.0,2.625], [2.625,35.0,2.688],
  [2.688,35.0,2.750], [2.750,34.0,2.813], [2.813,35.0,2.875], [2.875,35.0,2.938], [2.938,35.0,3.000], [3.000,35.0,3.125],
  [3.000,16.0,3.125], [3.125,36.0,3.188], [3.188,35.0,3.250], [3.250,35.0,3.313], [3.313,36.0,3.375], [3.375,35.0,3.438],
  [3.438,35.0,3.500], [3.500,36.0,3.563], [3.500,16.0,3.625], [3.563,35.0,3.625], [3.625,35.0,3.688], [3.688,35.0,3.750],
  [3.750,34.0,3.813], [3.813,35.0,3.875], [3.875,35.0,3.938], [3.938,35.0,4.000], [4.000,37.0,4.125], [4.000,18.0,4.125],
  [4.125,38.0,4.188], [4.188,37.0,4.250], [4.250,37.0,4.313], [4.250,18.0,4.375], [4.313,38.0,4.375], [4.375,37.0,4.438],
  [4.438,37.0,4.500], [4.500,38.0,4.563], [4.500,18.0,4.625], [4.563,37.0,4.625], [4.625,37.0,4.688], [4.688,37.0,4.750],
  [4.750,36.0,4.813], [4.813,37.0,4.875], [4.875,37.0,4.938], [4.938,37.0,5.000], [5.000,37.0,5.125], [5.000,18.0,5.125],
  [5.125,38.0,5.188], [5.188,37.0,5.250], [5.250,37.0,5.313], [5.313,38.0,5.375], [5.375,37.0,5.438], [5.438,37.0,5.500],
  [5.500,38.0,5.563], [5.500,18.0,5.625], [5.563,37.0,5.625], [5.625,37.0,5.688], [5.688,37.0,5.750], [5.750,36.0,5.813],
  [5.813,37.0,5.875], [5.875,37.0,5.938], [5.938,37.0,6.000], [6.000,35.0,6.125], [6.000,16.0,6.125], [6.125,36.0,6.188],
  [6.188,35.0,6.250], [6.250,35.0,6.313], [6.250,16.0,6.375], [6.313,36.0,6.375], [6.375,35.0,6.438], [6.438,35.0,6.500],
  [6.500,36.0,6.563], [6.500,16.0,6.625], [6.563,35.0,6.625]
];

// Agrupar por tiempo de inicio (algunas notas coinciden)
const events = {};
notes.forEach(([start, midi, end]) => {
  if (!events[start]) events[start] = [];
  events[start].push({ midi, duration: end - start });
});

const times = Object.keys(events).map(Number).sort((a, b) => a - b);

// --- Loop principal ---
loop(() => {
  let currentTime = 0;
  
  for (const t of times) {
    if (t > currentTime) {
      sleep(t - currentTime);
      currentTime = t;
    }
    
    events[t].forEach(note => {
      knightRider.play(note.midi+36, {
        duration: note.duration,
        attack: 0.01,
        release: 0.05,
        amp: 1.0
      });
    });
  }
  
  // Repetir cada 8 beats (Knight Rider loop)
  const last = times[times.length - 1] || 0;
  if (last < 8) sleep(8 - last);
}, { name: "Knight Rider v4 - Procs" });