Minim es una de las librerías de sonido más potentes que hay, y encontramos sus inicios en 2007, cuando el desarrollador Damien Di Fede’s la incorporó a Processing. Está basada en JavaSound e, inicialmente, ya soportaba la reproducción de ficheros MP3, generadores de ruido y osciladores. En 2009 se incluyó, junto con Anderson Mills, un modelo Ugen, que permite encadenar diferentes efectos y generadores que tratan la información en tiempo real. El modelo Ugen es un modelo que se implementa en la generación de música sintética desde los años 50 y cuya primera implementación la realizó el equipo Max Mathews en Bell Labs.
A pesar de que hay bastante documentación de cómo utilizar los diferentes componentes de Minim, no hay muchos ejemplos de cómo es posible ampliar sus funcionalidades para adaptarlo a nuestros proyectos. Es necesario un nivel avanzado de Java para analizar el código y saber cómo realizar las adaptaciones de las clases existentes. En este artículo vamos a explicar cómo funcionan internamente los Ugen y cómo podemos ampliarlos para generar nuestros propios efectos o conectores que nos permitan, por ejemplo, realizar visualizaciones o disparar eventos al detectar un nivel o un rango de frecuencias. Será necesario tener unos conocimientos básicos de Processing para poder seguir el artículo, ya que se dan por supuesto varios conceptos de cómo funciona Processing a la hora de visualizar información en pantalla, inicializar variables, etc. En esta propia revista se pueden localizar diversos artículos para familiarizarse con el lenguaje, como ‘Introducción a Processing‘.
El modelo Ugen
Este modelo se basa en diferentes módulos independientes que generan o reciben una señal y la sirven hacia otro elemento. Cada vez que la señal pasa por un elemento, esta se modifica hasta llegar a la salida final que es por norma la tarjeta de sonido. La velocidad a la que se solicita la información a la cadena de Ugens viene determinada por la frecuencia configurada en el elemento AudioOutput, es decir, la salida hacia la tarjeta de sonido. Por lo tanto, si tenemos configurada la frecuencia a 8Khz, se realizarán 8000 peticiones en un segundo, por lo que una de las premisas fundamentales es que los diferentes módulos deben ser rápidos en procesar la señal de forma que no agreguen retardos indeseados al sonido final.
Explicado de otro modo, tomemos como ejemplo el juego del teléfono descompuesto, en el cual una persona le va diciendo una frase al siguiente jugador y, al final, la frase queda alterada con respecto a la frase original porque cada persona le ha ido añadiendo un matiz diferente. Si lo extrapolamos, cada persona sería un módulo Ugen: la primera persona que inicia el juego sería un Ugen generador de sonido (oscil, por ejemplo), las siguientes personas serian módulos Ugen que modifican la frase (Ugen del tipo amplitude, pan, moogfilter, etc), y la persona que indica la frase final sería un AudioOutput.
El ejemplo más simple de una configuración de muestra seria un elemento Oscil con una frecuencia y amplitud configuradas a 1Khz u 0.5 de amplitud conectado a la salida con AudioOutput.
Todos los módulos Ugen descienden de una clase Ugen abstracta que implementa algunos buffers y funciones comunes a cualquier módulo de este tipo. Así, por ejemplo, todos los módulos que extiendan la clase tendrán disponible la función patch que permite ir enlazando los diferentes módulos. La clase, además, obliga a implementar la función uGenerate, que es la función principal de manipulación, cada vez que el sistema necesita datos, hará una llamada al método uGenerate de su módulo continuo, este a su vez pedirá datos a su módulo continuo, etc. Por ejemplo, supongamos que tenemos un oscil, un moogfilter y el audioouput conectados entre si, cuando el audioOput necesite datos, le pedirá al moogfilter una muestra a reproducir, este a su vez le solicitará datos al Oscil, el cual, como es un generador, servirá una muestra de datos. El moogfilter modificará los datos obtenidos de Oscil y los servirá al AudioOuput que los enviará a la tarjeta de sonido.
Creando nuestro propio Ugen
Para entender los diferentes conceptos explicados hasta ahora, vamos a implementar un módulo Ugen que permita visualizar el sonido que pasa a través de él. De este modo podremos visualizar el sonido antes de pasar por un filtro y después de pasar por el filtro.
Las características que tendrá nuestro visualizador serán las siguientes:
- Visualizar espectrograma
- Visualizar en forma de onda canal izquierdo y derecho
- Obtener el buffer con el que se han dibujado las muestras
- Volcado de buffer manual
Para poder dibujar la forma de onda, el módulo al que llamaremos Visualizador deberá ir almacenando en un buffer cada vez que el sistema le solicite datos, cuando el buffer se llene, se volcará el contenido en un buffer intermedio de dibujado, lo que permitirá tener información estable en el momento de dibujar la onda. El buffer donde se almacenan los datos, será un buffer circular, este tipo de buffer es ideal para almacenar datos temporales, cuando llega al final del buffer vuelve a rellenar información desde el principio por lo que siempre se dispondrá de las últimas n muestras del sonido.
Para crear una nueva clase, crearemos un fichero Visualizador.pde, en el cual deberemos importar las librerías minim e indicar el nombre de la clase, que debe coincidir con el del archivo.
El primer paso será declarar todas las variables que hacen falta para realizar las operaciones en el módulo, por lo tanto, hay que declarar un conector hacia otros módulos Ugen, los buffers de almacenaje temporal y de dibujado.
El constructor de la clase será el encargado de inicializar las variables, asignar espacio de memoria a los buffers, etc.
Para poder iniciar el tamaño del analizador FFT, tenemos que esperar a que se asigne el tamaño del sampleRate en el sistema, por lo que no es posible inicializar la variable en el constructor. Para ello Minim incluye una función que es llamada cada vez que se cambia el sampleRate de salida, de forma que todos los módulos uGen conectados puedan reconfigurarse.
La función uGenerate, será la que irá obteniendo muestras y las irá guardando en el buffer, por lo tanto, devolverá la información sin manipular
La clase incluye, además, varias funciones que permiten trabajar con el buffer obtenido o forzar el volcado manual.
Para realizar el dibujado de la onda, se suministran dos funciones que, básicamente, pintan los valores del buffer en la pantalla, tal y como haríamos con la salida del AudioOput con mix, left o right
Funciones similares se implementan para el pintado del espectro
Usando el componente
Se puede ver el uso del componente con el siguiente ejemplo, para el cual se utiliza un oscilador y un filtro paso bajo. La frecuencia del oscilador se puede variar moviendo el ratón por la pantalla. Se visualizará la señal antes y después de actuar el filtro.
El filtro se configura a una frecuencia de corte de 1.8Khz, y el oscilador variará la frecuencia entre 200 Hz y 5Khz. En la pantalla se podrá observar cómo se atenúa la señal conforme la frecuencia aumenta.
Descarga en este link el código fuente completo del artículo.
Enlaces relacionados
Oscil http://code.compartmental.net/minim/oscil_class_oscil.html
Ugen http://code.compartmental.net/minim/index_ugens.html
Teléfono descompuesto https://es.wikipedia.org/wiki/Teléfono_descompuesto
Minim http://code.compartmental.net/minim/
Documentación
https://en.wikipedia.org/wiki/Unit_generator
http://courses.cs.washington.edu/courses/cse490s/11au/Readings/SynthesisChapt3.pdf
Deja un comentario