Laboratorio de Síntesis con SC

Deriva pasa del método al cuaderno de sesión

Esta quinta entrada recoge lo aprendido construyendo pad, bajo, ritmo y espacio dentro de DERIVA, y convierte ese proceso en un cuaderno técnico centrado en el continuo entre tono y ruido.

Sesión 05 de 06 ProxySpace + JITLib Cuaderno técnico expandido

Este cuaderno no introduce una pieza nueva, sino una capa más concreta de Deriva: cómo se resolvieron en práctica la sintaxis de ProxySpace, el control de parámetros, el diseño del pad, el bajo, la rejilla rítmica, la reverb compartida y la forma macro. El hilo conductor es claro: usar el continuo tono-ruido como principio tímbrico y compositivo.

Origen: HTML de trabajo convertido en entrada del laboratorio.

Idea central: documentar aprendizajes técnicos reales de una sesión completa, no solo un patch suelto.

Cruce: síntesis sustractiva, bajo orgánico, L-System rítmico, bus de reverb y macroforma.

KAIROS · Cuaderno técnico SuperCollider 3 / JITLib

DERIVA
sesión de síntesis

Lo aprendido construyendo el pad, el bajo, el ritmo y el espacio — con el continuo entre el tono y el ruido como hilo conductor.

← tono · banda estrecha · rq 0.0001 ruido · banda ancha · rq 0.4 →
01

ProxySpace · contexto

Dos sintaxis: ~name y p[\name]

~pad es azúcar para currentEnvironment[\pad], y currentEnvironment se resuelve en tiempo de ejecución según el contexto. p[\pad] accede directamente a p, que es una variable global de letra, ajena al environment. En el top-level los dos coinciden porque el ProxySpace es el environment actual.

El problema aparece en contextos diferidos. Un Tdef, Routine o Task se reanuda en su propio hilo, donde currentEnvironment ya no es p. Ahí ~pad devuelve nil en silencio y revienta al primer .set — no al definir el Tdef, lo que lo hace traicionero.

~name

Evaluación interactiva en el editor: definir y redefinir proxies, pruebas en vivo, ~pad.play.

p[\name]

Dentro de Tdef, Routine, Task, callbacks, funciones en .do. El orquestador macro va siempre por aquí.

Regla

Una vez empujado el ProxySpace, ~ queda reservado para audio. Los datos (arrays, diccionarios, secuencias, rutinas auxiliares) van en variables de letra — q, r — nunca en ~, o se convierten en NodeProxy y se corrompen.

02

Suavizado de parámetros

Dónde colocar .lag

.lag persigue su entrada de forma exponencial. El mismo UGen tiene efecto opuesto según dónde se enchufe: sobre un control escalar (la amplitud, una nota) convierte un salto en una rampa suave; sobre una señal de audio es un filtro paso-bajo de un polo con corte ≈ 1/(2π·t) — con .lag(3) eso son ~0,05 Hz, una guillotina que borra todo lo audible.

// MAL: lag sobre el audio -> LPF a 0.05 Hz -> silencio
sig = BrownNoise.ar(1).lag(3);

// BIEN: lag sobre el VALOR del control -> rampa suave
sig = sig * \amp.kr(0.2).lag(3);

Para automatizar un parámetro desde un Tdef, .lag dentro del proxy es superior a .xset: el Tdef manda saltos con .set y el Lag.kr los suaviza en el servidor.

Por qué no .xset aquí

.xset no interpola el valor: hace un crossfade entre dos copias enteras del synth durante el fadeTime. Sobre una fuente aleatoria como BrownNoise, las dos copias generan ruido descorrelacionado y la transición se oye como engrosamiento, no como rampa de volumen. .xset se reserva para recrear la fuente con crossfade (cambios tímbricos grandes), no para mover un número.

03

Síntesis sustractiva

El pad: drone por filtro resonante

Un banco de BPF estrechos sobre BrownNoise, afinados a las notas de un acorde y esparcidos al estéreo con Splay. Cada nota es un control escalar independiente (\n0\n3) con .lag para que los cambios de altura sean glissandos.

~pad = {
    var sig, n0, n1, n2, n3, rq, gain;
    n0 = \n0.kr(60).lag(2);
    n1 = \n1.kr(67).lag(2);
    n2 = \n2.kr(72).lag(2);
    n3 = \n3.kr(76).lag(2);
    rq = \rq.kr(0.005).lag(4);
    gain = (0.005 / rq).sqrt * 10;
    sig = BrownNoise.ar(1);
    sig = BPF.ar(sig, [n0, n1, n2, n3].midicps, rq, gain);
    sig = Splay.ar(sig);
    sig = sig * \amp.kr(0.2).lag(3);
};
~pad.play;
Dos trampas que ya mordieron

Toda variable local va declarada en var (si no: Variable not defined). Y los NamedControls multicanal se indexan como notes, notes1… — por eso se usan controles escalares independientes y no un único \notes con array.

04

Curtis Roads · Microsound

El continuo tono ↔ ruido

El sonido no se divide en categorías limpias sino que ocupa un continuo, y lo que fija dónde caes es la concentración de energía en frecuencia: el ancho de banda. Energía en una banda estrechísima → el oído percibe pitch. Energía repartida en una banda ancha → percibe ruido. Entre medias vive la zona de tonos sucios, parcialmente afinados, donde respira buena parte de la música ambient.

0.0001 TONO 0.02 0.05 ¿tono o color? 0.4 RUIDO
ancho de banda = frecuencia central × rq · el rq es el mando del continuo

En un BPF, el ancho de banda es frecuencia × rq. Con rq 0.005 sobre 261 Hz la banda mide ~1,3 Hz: casi un oscilador puro. El factor Q es el inverso: Q alto = banda estrecha = rq bajo. La resonancia del MoogFF en el bajo es ese mismo Q.

Llevado al extremo, rq 0.0001 da una banda de ~0,026 Hz: un resonador al borde de la autooscilación que canta la nota casi limpia; el ruido apenas lo excita. Es lento de cargar (régimen transitorio largo) y sube mucho de nivel — la compensación (0.005/rq).sqrt daría ×71, así que hay que vigilar el medidor.

Gesto compositivo

Barrer el rq de 0.0001 a 0.4 y volver es recorrer el continuo de Roads en tiempo real: el pad pasa de armonía cristalina a nube de ruido. En el FreqScope se ve como picos finos que se ensanchan en colinas. Cruzar ese umbral puede ser un evento de forma — revelar la armonía en el clímax.

05

Voz de bajo

El bajo orgánico

Lo orgánico se construye apilando inestabilidades pequeñas: tres saws ligeramente desafinados que laten entre sí, micro-deriva de afinación, un cutoff que respira y una amplitud que nunca queda fija.

~bass = {
    var sig, note, freq, cutoff, amp;
    note = \bn.kr(48).lag(5);
    note = note + LFNoise2.kr(0.08).range(-0.04, 0.04);
    freq = note.midicps;
    sig = Saw.ar(freq * [0.998, 1, 1.002]);
    cutoff = (freq * 4 + (SinOsc.kr(0.06) * freq * 2)).clip(40, 6000);
    sig = MoogFF.ar(sig, cutoff, 1.5);
    amp = \amp.kr(0.3).lag(3);
    sig = sig * amp * LFNoise2.kr(0.07).range(0.7, 1.0);
    Splay.ar(sig);
};

El beating es el continuo en su zona más fina: a 0,2–0,3 Hz de diferencia el oído no separa dos tonos ni los funde en uno, percibe fluctuación — la frontera donde una diferencia de frecuencia se oye como ritmo lento, no como altura. El patrón del cutoff es el canónico de modulación con offset: (base + (mod * profundidad)).clip(min, max), con los paréntesis externos obligatorios para que .clip envuelva toda la suma.

Adam A3X

El sub a freq * 0.5 cae en ~65 Hz, dentro del rolloff de los monitores. Verificar en FreqScope antes de fiarse del oído; si no aporta o enturbia, quitarlo.

06

Coherencia de reloj

Una sola fuente de tempo

El tempo vive solo en t. Todo lo que deba seguirlo se lanza con .play(t, quant: 4) — nunca con .play a secas, que va al TempoClock.default y queda sordo a los cambios de t. Ese fue justo el motivo de que t.tempo_(120/60) no cambiara nada: los Tdefs corrían en otro reloj.

// inmediato (salto)
t.tempo_(120/60);

// rampa suave — en SystemClock, NO en t (sus waits son segundos reales)
(
r = Routine({
    var from = t.tempo, to = 120/60, dur = 15;
    var n = (dur * 20).asInteger;
    n.do { |i|
        t.tempo_(from + ((to - from) * ((i+1)/n)));
        (dur / n).wait;
    };
}).play(SystemClock);
)
Principio

t como única fuente de verdad. Cambiar t reajusta pad, bajo y ritmo a la vez, gratis. El reverso oscuro — la triple fuente de verdad (t + ~bpm + un \bpm.kr hardcodeado) — es un bug a evitar. La rampa es la única excepción que vive en otro reloj.

07

SynthDef percusivos

Percusión sintetizada

Bombo (barrido de pitch + click + tanh), caja (cuerpo de ruido BPF + parciales tonales) y charles (ruido HPF + pulsos inarmónicos, con rel que decide cerrado u abierto).

SynthDef(\hh, { |out=0, amp=0.3, pan=0, rel=0.04|
    var aenv, noise, metal, sig;
    aenv  = EnvGen.kr(Env.perc(0.001, rel), doneAction: 2);
    noise = HPF.ar(WhiteNoise.ar, 9000);
    metal = Mix(Pulse.ar([4100, 5300, 6700, 8200], 0.5)) * 0.15;
    metal = HPF.ar(metal, 8000);
    sig   = (noise + metal) * aenv * amp;
    Out.ar(out, Pan2.ar(sig, pan));
}).add;
doneAction según contexto

En synths percusivos efímeros disparados por evento: doneAction: 2 (deben autodestruirse). Dentro de NodeProxies de señal continua: doneAction: 0 (matar el nodo silenciaría el proxy). Contextos opuestos — no es contradicción.

08

Ritmo generativo

L-System para el ritmo

Un sistema de reescritura produce una secuencia determinista, autosimilar e irregular — pulso reconocible sin caer en bucles cuadrados. Implementado a mano en sclang termina seguro y se puede inspeccionar:

q = {
    var rules = ( \k: [\k,\r,\s,\r], \s: [\s,\r,\k,\r], \r: [\r] );
    var seq = [\k];
    3.do { seq = seq.collect({ |s| rules.atFail(s, { [s] }) }).flat };
    // rellena a múltiplo de 16 -> encaja en compases
    seq = seq ++ Array.fill((16 - (seq.size % 16)) % 16, \r);
    seq
}.value;
Sincronía

Sin el relleno, la secuencia mide 22 pasos × 0.25 = 5,5 beats — no múltiplo de 4, así que el patrón se desliza contra la rejilla y el hi-hat lo delata como "desincronizado". Rellenar a 32 pasos lo encaja en 2 compases exactos.

09

Espacio compartido

Reverb como bus de retorno

Una sola reverb como retorno crea un espacio coherente para todo. Se usa JPverb — evita el crash de GVerb por encima de roomsize 290 y sus parámetros (t60, size) son modulables sin sustos. La percusión, al ser Synths, escribe a un bus auxiliar con un segundo Out.ar escalado por \rvb:

SynthDef(\sn, { |out=0, amp=0.5, pan=0, rvb=0.25|
    /* … síntesis … */
    Out.ar(out, sig);
    Out.ar(~rvbBus.index, sig * rvb);
}).add;
Bajo + reverb = barro

Reverberar contenido grave enturbia toda la mezcla. Mandar el bajo muy poco al bus o no mandarlo; reservar el espacio para pad y percusión. El charles va bajo (~0.15) porque demasiada cola lo vuelve un siseo difuso.

10

Indeterminación controlada

Aleatorizar eventos

Tres grados de azar para el orden de los acordes, de menos a más orgánico:

  • .scramble — baraja: oyes todos antes de repetir, variedad asegurada.
  • .choose — azar puro: puede insistir en el mismo (a veces bonito, a veces se atasca).
  • .reject({ |x| x == prev }).choose — azar que evita la repetición inmediata: la deriva natural sin atascos.
loop {
    var ac = acordes.reject({ |x| x == prev }).choose;
    prev = ac;
    p[\pad].set(\n0, ac[0], \n1, ac[1], \n2, ac[2], \n3, ac[3]);
    [8, 13, 21].choose.wait;
};

Aleatorizar también el wait con valores Fibonacci hace que el tiempo entre cambios respire de forma irregular pero dentro del sistema de proporciones de DERIVA: el azar vive dentro de la estructura, no contra ella.

Evaluación por bloques

prev es local al bloque del Tdef. Evaluar una línea suelta que la use da Variable not defined — en SC se evalúa la función entera entre paréntesis, nunca una línea interior aislada.

11

Higiene de servidor

Limpiar nodos

s.freeAll · Cmd-.

Pánico: mata todos los synths del servidor pero deja vivos los objetos NodeProxy. Quedan listados pero mudos — se recuperan con .play.

Reset ordenado

Tdef.all.do(_.clear) para los disparadores; currentEnvironment.clear libera todos los proxies. Deja servidor y t intactos.

// barrido ordenado: parar disparadores + liberar proxies
Tdef.all.do(_.clear);
currentEnvironment.clear;

Tras freeAll los proxies vuelven con ~pad.play; ~bass.play; ~verb.play; y los Tdefs con .play(t, quant: 4). El TempoClock sobrevive a Cmd-. por estar marcado .permanent_(true).

12

Roads · escalas temporales

Forma: navegar las escalas

Del análisis del set de co34pt salió una arquitectura formal legible en términos de Roads — la pieza navega entre la micro-escala (granos, onsets), la meso (frases, compases) y la macro (forma global), transformando la relación entre ellas:

ambigüedad acumulación lente de pulso clímax · 2 vectores
ambigüedad métrica → acumulación → recontextualización del pulso → intensificación

El giro clave: introducir tarde un elemento de rejilla estable (el hi-hat en 1 y 3) que recontextualiza retroactivamente todo el material flotante anterior — el oído reinterpreta como "colocado" lo que oía como libre. No cambió el material; cambió el marco de referencia.

El clímax usa dos vectores de tensión a la vez: temporal (tempo arriba) y tímbrico (un elemento conocido vuelve deformado). La memoria del oyente de la versión limpia es lo que carga de sentido su retorno corrupto.

Hacia DERIVA

El arco Fibonacci con clímax en el compás 21 (21/34 ≈ 1/φ) puede adoptar el doble vector: que el cruce del continuo tono→ruido del pad coincida con la deformación del bajo, ambos en su extremo en el bar 21. Y la entrada del ritmo no como "ahora empieza la batería" sino como la lente que revela un pulso oculto en la deriva previa.

DERIVA · KAIROS — cuaderno de sesión
SuperCollider 3 / ProxySpace · JITLib
Marco teórico: Curtis Roads, Microsound & The Computer Music Tutorial
Referencias de escucha: Alva Noto + Sakamoto · Basinski · Huerco S · co34pt
Monitoreo: Adam A3X — verificar sub-graves en FreqScope

Seguir desde aquí

Sesión 04 · Deriva: método de composición algorítmica Volver al cuaderno metodológico que formaliza las capas temporales, el reloj compartido y la macroforma. Sesión 06 · Cuatro vías para transformar la batería Bajar al terreno rítmico y ver cómo bd, sn y hh cambian al intervenir golpe, timbre, espacio y tiempo. Sesión 03 · Síntesis granular Cruzar el continuo tono-ruido con la escala microtemporal y el marco teórico de Roads. Curtis Roads · Audio Digital Releer la base teórica integrada en el laboratorio para fase, aliasing y mezcla digital.