Síntesis Modular con Processing

Introducción.

Buscando una herramienta para programar y experimentar con audio, efectos y síntesis, decidí probar la biblioteca Minim de Processing. Existen muchos entornos informáticos interesantes para diseñar sonido, algunos son gráficos, otros son de escribir código, algunos son libres, otros son pagos; cito algunos ejemplos: Reaktor, Max-MSP, PureData, SynthEdit, SynthMaker, JSyn, Csound, SuperCollider, ChucK.

Dentro de Processing, existen varias bibliotecas para manejar sonido, entre ellas: Ess, Sonia y Minim, siendo esta última la que viene con la distribución oficial del producto. Rápidamente, repasemos las principales ventajas e inconvenientes -a mi modesto entender- de Minim:

  • Ventajas: es simple, transparente, bastante bien documentada y Open Source.
  • Inconvenientes: utiliza Java Sound para comunicarse con el hardware de audio.

Muchas de las bibliotecas de Processing son justamente “wrappers” de las funcionalidades de Java. Minim utiliza Java Sound para el audio I/O, partes de otras bibliotecas para otras tareas -como reproducir mp3, por ejemplo- pero también agrega cosas propias e interesantes, en materia de análisis, procesamiento y generación de sonido. A su vez, uno de los principales inconvenientes del sistema de audio de Java es que no soporta ASIO, que como todos sabemos, es la única manera de obtener baja latencia, al menos bajo Windows XP.

¿Cómo funciona la generación de sonido a nivel de software?

El audio digital, a nivel del software, se maneja en buffers. Un buffer es un conjunto de muestras -por ejemplo, 1024 muestras- que se envían o se reciben en bloque hacia o desde el hardware. Mientras la tarjeta de audio reproduce esas 1024 muestras (lo que le llevará 23ms, si el muestreo es de 44,1KHz), el programa tiene tiempo de calcular el bloque siguiente. Cuanto más grande sea el buffer, o sea cuantas más muestras contenga, mayor será la latencia de la que hablábamos en el párrafo anterior (más información sobre buffers y latencia aqui).

En Minim, la clase Oscillator es la base de los generadores de sonido. Cuando se llama al método “generate”, este “llena” un buffer con una forma de onda calculada de alguna manera, por ejemplo con la función trigonométrica seno.

Una consecuencia interesante de esta naturaleza de Minim, es que si nosotros intentamos modificar un parámetro de la onda que se está generando -por ejemplo su amplitud o su frecuencia- desde el módulo principal (la Draw() de Processing) ya sea a partir de un control externo o de una función prefijada, los cambios se van a manifestar de a “saltos” equivalentes a la duración del buffer (23ms, en el ejemplo que veíamos). Esto nos impide hacer un cambio gradual y continuo de cierto parámetro, por ejemplo una envolvente de amplitud. Para lograr este tipo de control, necesitamos implementar otro “Oscillator” que genere la señal de control, y que el primer “Oscillator” consulte a esa señal al calcular cada muestra que pone en el buffer.

Aplicando esta idea a diversos módulos de la biblioteca, e implementando algunos módulos nuevos, surgió la Minim 2.02 PE (palmer edition), con la cual podemos hacer una modesta síntesis modular, que paso a describir y ejemplificar en la página a continuación.

 

Siguiente sección –> Minim 2.02 Palmer Edition

 

Be Sociable, Share!

3 Comentarios »

  1. avatar Cristian Dice:

     
     
    Pablo, acá t mando el código. Como te contaba, no logro que la frecuencia vaya subiendo sin tener ese sonido cortante.
    Se aceptan ayudas, sugerencias, lo que sea.  
    Gracias!
     
    import ddf.minim.*;
    import ddf.minim.signals.*;
     
    Minim minim;
    AudioOutput out;
    SineWave sine;
    PImage a;
    int f;
     
     
    void setup()
    {
     
      size(512, 200, P3D);
      
      a = loadImage("Gustave_Courbet_038.jpg"); 
      
      minim = new Minim(this);
      
      out = minim.getLineOut(Minim.STEREO, 2048);
      
    }
      
      
       void draw()
    {
      f=100;
      
        sine = new SineWave(f, 1, out.sampleRate());
      
        out.addSignal(sine);
       
     
      background(0);
      
      image(a, 0, 0);
      
      stroke(220,200,140);
      
      fill(random(255),0,0);
     
     
      for(int i = 0; i < out.bufferSize() – 1; i++)
      {
        line(i, 150 + out.left.get(i)*50, i+1, 150 + out.left.get(i+1)*50);
        line(i, 150 + out.right.get(i)*50, i+1, 150 + out.right.get(i+1)*50);
      }
      
      
     if (f<400){
       
       f++;
       
     }else{
        
        noLoop(); 
     }
     
     
     println(f);
     
    }
     
    void stop()
    {
     
      out.close();
      minim.stop();
     
      super.stop();
    }

  2. avatar pabloxid Dice:

    Encuentro varias cosas raras, más allá del fenómeno del sonido cortante, que es en realidad lo más fácil de explicar.
    En primer lugar, hay un f=100; al principio del loop, por lo que el valor de f se estaría reseteando permanentemente, y la frecuencia nunca iría más allá de los 100Hz, incluso si estuvieras modificando el valor de f fuera del código que tenemos a la vista.
    Por otro lado, no me parece muy conveniente que estés declarando una nueva señal cada vez que se ejecute el loop.
    Aun así, aunque ese par de cosas estuvieran arregladas, es normal que tengas un sonido cortado, justamente al definir un buffer de 2048 muestras, no vas a poder modificar ningún parámetro del sonido en lapsos de menos de 46ms. 
    No sé bien qué es lo que querés hacer, pero supongo que la Minim Palmer Edition te podría ser de utilidad.

  3. avatar Cristian Dice:

    Voy a revisar el código nuevamente. Gracias por el aporte Pablo.
    Saludos.
    Cristian.

RSS alimentación de los comentarios de esta entrada. TrackBack URL

Dejar un comentario