Lección 4.2: Polifonía Dinámica con poly~: Asignación de Voces, Enrutamiento (target), mute y thispoly~
“Un sintetizador monofónico es una línea melódica solitaria; la polifonía es arquitectura armónica en el tiempo. Pero multiplicar 16 voces de síntesis por la fuerza bruta de copiar y pegar parches es la bancarrota del CPU: la polifonía digital profesional exige asignación dinámica, reciclaje de voces y apagado absoluto de hilos inactivos.”
— F. Richard Moore, Elements of Computer Music
Objetivos Pedagógicos y de Ingeniería
- Comprender el paradigma de instanciación masiva de
poly~: Explicar cómo un único archivo.maxpatde voz se replica veces en memoria sin duplicación visual en el lienzo. - Dominar los algoritmos de asignación de voces:
midievent: Enrutamiento automático de Note-On / Note-Off con asignación voice stealing (robo de voz inteligente).target $1: Direccionamiento explícito a una voz específica () o a todas simultáneamente (target 0).
- Optimización Radical de CPU mediante
[thispoly~]y el mensajemute: Demostrar matemáticamente por qué una voz que terminó su envolvente no debe procesar ceros y cómomute 1, 0desactiva el bucleperform64de esa voz en el árbol de DSP. - Analizar la estructura interna de
poly~en el SDK de Max C: Inspeccionar cómo el objeto gestor mantiene un arreglo de estructurast_patcher*y conmuta punteros de vector de audio de forma condicional.
1. La Anatomía del Objeto poly~
[poly~] recibe dos argumentos fundamentales:
poly~ [nombre_subparche_voz] [cantidad_de_voces]
Ejemplo: poly~ voz_polifonica 8
[ Patch Principal ] | (midievent / target) v +-------------------+ | poly~ 8 | +-------------------+ | Voz 1 (Activa) | ---> perform64() (CPU ON) | Voz 2 (Activa) | ---> perform64() (CPU ON) | Voz 3 (Muteada) | ---> [Bypass] (CPU 0%) | Voz 4 (Muteada) | ---> [Bypass] (CPU 0%) | ... | +-------------------+ | (Suma de Audio out~) v [out~ 1]Los Objetos Especiales de Comunicación Interna
Dentro del subparche de voz instanciado por poly~, los inlets y outlets estándar se reemplazan por:
[in 1]/[in 2]: Inlets de control (mensajes).[in~ 1]/[in~ 2]: Inlets de señal de audio.[out 1]: Outlets de mensajes de control hacia el padre.[out~ 1]: Outlets de audio (quepoly~suma automáticamente en su salida correspondiente).
2. El Ciclo de Vida de una Voz: thispoly~ y el Protocolo mute
El error más común en principiantes es dejar que las 16 o 32 voces de un sintetizador corran constantemente en segundo plano. Aunque no suenen, multiplicar ceros en filtros y osciladores consume la misma cantidad de ciclos de instrucción por muestra que tocar un acorde masivo.
Para solucionar esto, cada voz incluye un objeto [thispoly~]:
[adsr~ 10. 150. 0.5 300.] | | (3er Outlet: Estado de Actividad 1/0) v v [*~ audio] [!= 0.] (1 cuando suena, 0 cuando calla) | [message: mute $1, $1] | [thispoly~]Sintaxis del Mensaje mute [estado_mute] [estado_busy]
mute 1 0: Mute ON, Busy OFF. La voz se apaga en el motor DSP (consume 0% de CPU) y se marca como libre para quepoly~pueda asignarle una nueva nota.mute 0 1: Mute OFF, Busy ON. La voz se enciende en el hilo DSP y se marca como ocupada para que ninguna otra nota la interrumpa.
3. Direccionamiento: midievent, target y note
Max ofrece tres métodos de interacción con las voces internas:
A. Mensaje midievent [status] [pitch] [velocity]
El método más eficiente y elegante. poly~ decodifica internamente los mensajes MIDI:
- Si
velocity > 0(Note On): Busca la primera voz libre (busy == 0), le envía la nota y la marca como ocupada. - Si
velocity == 0(Note Off): Busca la voz que está ejecutando ese tono exacto y le envía el Note-Off para disparar la fase de Release. - Si todas las voces están ocupadas: Aplica voice stealing (roba la voz con la nota más antigua o con el nivel de envolvente más bajo).
B. Mensaje target $1
Permite control quirúrgico manual:
target 3: Todos los mensajes subsiguientes irán exclusivamente a la voz 3.target 0: Todos los mensajes subsiguientes irán en broadcast a todas las voces simultáneamente (ideal para cambiar el filtro o el ataque de todo el sintetizador al mismo tiempo).
4. Bajo el Capó: Análisis en C del Bucle de Despacho de poly~
En el motor DSP de Max (ext_obex.h, z_dsp.h), poly~ administra una lista de instancias de patchers. En cada bloque de procesamiento de audio de 64 bits:
// Modelo conceptual del despachador DSP de poly~void poly_dsp_chain(t_poly *x, double **ins, double **outs, long frames) { // 1. Limpiar el buffer de salida acumulativo memset(outs[0], 0, sizeof(double) * frames);
// 2. Iterar sobre cada voz instanciada for (int v = 0; v < x->num_voices; v++) { t_voice *voice = &x->voices[v];
// EVALUACIÓN DE MUTE: Si está muteada, saltar inmediatamente if (voice->is_muted) { continue; // ¡Ahorro absoluto de CPU! }
// Ejecutar el sub-árbol DSP de esta voz específica voice_perform64(voice, ins, voice->temp_out, frames);
// Sumar vectorialmente al master out (SIMD Vector Add) for (int i = 0; i < frames; i++) { outs[0][i] += voice->temp_out[i]; } }}5. Escenarios Reales de Producción
- Sintetizador Polifónico de 16 Voces: Un pad estéreo complejo con 2 osciladores por voz, filtro bicuadrático y generador de ruido que reduce su consumo de CPU del al cuando no hay teclas presionadas.
- Generador de Enjambre de Granos (Granular Cloud Engine): Un
poly~ 64donde cada voz reproduce un micro-grano de audio en una posición aleatoria de un buffer, activándose y muteándose en lapsos de 30 ms. - Banco de Filtros Resonadores Modal: Un objeto
poly~ 32configurado contarget $1en tiempo de inicio para cargar 32 modos armónicos de una placa metálica o membrana de tambor. - Procesador Multiefecto con Voces Desfasadas: Crear un efecto de ensanchamiento estéreo con 6 líneas de delay moduladas, donde cada voz recibe un tiempo de retardo derivado de su ID interno (
[thispoly~] -> outlet de número de voz).
6. Desafíos de Ingeniería
Desafío 1: El Lector de Número de Voz Interno
Utilizá la salida de mensaje de [thispoly~] (que emite el número de voz al recibir un bang) para generar una dispersión estéreo automática (pan spread), asignando las voces impares a la izquierda y las pares a la derecha.
Desafío 2: Voice Stealing Manual con umbral de prioridad
Diseñá un mecanismo de control antes de poly~ que verifique mediante poly~ 8 args si hay voces disponibles y, de no haberlas, fuerce el corte inmediato de la nota más baja en lugar de la más antigua.
Desafío 3: El Medidor de Carga de Voces Activas
Construí una interfaz en el parche principal que consulte periódicamente cuántas voces están activas (busy) simultáneamente y grafique el uso porcentual de polifonía en una barra [live.meter~] o [multislider].
7. Archivos del Laboratorio
voz_polifonica.maxpat: Subparche atómico de voz con oscilador de sierra (saw~), filtro resonante, envolventeadsr~y conexión al protocolomute/busyen[thispoly~].laboratorio_16_poly.maxpat: Parche maestro con teclado MIDIkslider, instanciaciónpoly~ voz_polifonica 8, control de parámetros globales mediantetarget 0y monitoreo del consumo de CPU.