En este ejemplo vamos a utilizar la librería Minim para cargar y reproducir un fichero de extensión y formato .MP3 en nuestro equipo. Además vamos a visualizar “la canción” por lo que respecta a las amplitudes de sus canales izquierdo y derecho (estéreo). La librería Minim se instala por defecto con Processing aunque es recomendable actualizarla. Veamos el resultado visual en la figura 1.
Figura 1. Reproducimos un fichero de música y generamos dos ecualizaciones para sus canales izquierdo y derecho.
En el código 1 empezamos por importar la librería de sonido. Además creamos dos objetos que necesitaremos posteriormente. Por una parte un gestor de audio (AudioManager) y por otra el contenedor de la canción a reproducir (MySong). En la función de inicialización setup() creamos una ventana de 256 por 256 píxeles de resolución y a continuación inicializamos los objetos llamando a sus constructores. En el caso de MySong se observa como llamamos a su método loadFile para cargar un fichero de audio de nombre “sweet.mp3” que previamente hemos guardado en el subdirectorio “data”, dentro del directorio del sketch de Processing. Además pasamos un segundo parámetro que indica el tamaño de nuestro “buffer” de almacenamiento de muestras. Por defecto se trata de 1024. En nuestro caso cargaremos de 256 en 256 y eso es lo que mostraremos por pantalla a cada iteración.
Una vez cargado el archivo de sonido sobre nuestro objeto, llamaremos al método de éste que lo reproduce (play()) y definiremos un ancho de 5 píxeles para nuestras líneas de las gráficas.
// We will use MINIM as our Audio Library
// To draw colored vertical lines that stand for
// a song's waveform
// Therefore we import it
import ddf.minim.* ;
// We create a Minim Object to manage audio
Minim AudioManager;
// We create an AudioPlayer Object to load a track (song)
AudioPlayer MySong;
// The Setup function that we'll be called only once
void setup(){
// Our window is 256 x 256 pixels
size(256, 256);
// We initialize the AudioManager object
AudioManager = new Minim(this);
// We use the "loadFile" method inside the "minim" object
// to load an MP3 file, 256 samples everytime
MySong = AudioManager.loadFile("sweet.mp3",256);
// We start playing the track/song/file
MySong.play();
// Our lines we'll be 5 pixels wide
strokeWeight(5);
}
Código 1. Librería, objetos e inicialización de nuestro reproductor de audio.
En el código 2 observamos la función draw() que será la encargada de “visualizar” 256 muestras musicales por iteración.
// The infinite loop
void draw(){
String s;
// The offset allows us to work within
// the [0,2] window instead of [-1,1]
float offset;
// Black color for the background
background(0);
// A randomized color for our lines
stroke(random(256),random(256),random(256));
// Every loop, 256 samples to iterate within
// MySong.bufferSize() tells us the length of the audio buffer
// We'll draw a vertical line every 10 samples
for(int counter = 0; counter < MySong.bufferSize() - 1; counter+=10){
// We draw our vertical lines! remember how Processing manages vertical coords!
// We convert the amplitude of the sample
// from the [-1,1] range to the [0,2] range
// Firstly the left channel
offset = MySong.left.get(counter)+1;
line(counter,0, counter, offset*64);
// Secondly the right channel
offset = MySong.right.get(counter)+1;
line(counter,128, counter, 128+offset*64);
// We will provide with some yellow text to identify channels
fill(255,255,0);
// Text for the left channel
s = "Left channel";
text(s, 5, 100, 100, 80);
// Text for the right channel
s = "Right channel";
text(s, 5, 230, 100, 80);
}
}
Código 2. “Dibujando” música.
Inicialmente definimos algunas variables que necesitaremos para mostrar texto por pantalla (String s) así como para desplazar nuestras muestras y dibujarlas adecuadamente como se explicará en un momento (float offset). Además definimos un fondo negro para nuestra ventana (background(0)) y un color aleatorio para nuestras líneas (stroke(random(256),random(256),random(256))), que indicaran la amplitud de cada una de las 256 muestras que pintaremos.
A partir de ahí recorreremos nuestro “buffer” de muestras a cada iteración y por lo tanto daremos 256 vueltas gracias a la estructura de control de tipo bucle FOR implementada. Fijémonos en qué es posible conocer la longitud del “buffer” gracias al método bufferSize() de nuestro objeto MySong. No iteramos de 1 en 1 muestra sino que lo hacemos de 10 en 10 para evitar ralentizar el sistema demasiado. Eso quiere decir que realmente pintaremos 256/10 muestras, es decir entre 25 y 26 a cada iteración.
Tal y como se explica detalladamente en la guía de iniciación a la librería Minim, las muestras que se obtienen tienen valores entre -1 y 1. Esto no nos conviene a la hora de pintarlas así que les sumaremos un cierto desplazamiento. En concreto sumaremos 1 y por lo tanto pasaremos a tener muestras que oscilan entre los valores de 0 y 2. El resultado de la muestra “desplazada” lo almacenamos en la variable offset, primero para el canal izquierdo (offset = MySong.left.get(counter)+1) y después para el derecho (offset = MySong.right.get(counter)+1). En ambos casos utilizamos este valor para pintar una línea que va desde la parte de superior de la ventana hasta la muestra (canal izquierdo) o desde la parte central de la ventana hasta la muestra (canal derecho). Es decir que para una altura de 256 píxeles en nuestra ventana, estamos utilizando dos espacios de 128 píxeles cada uno para pintar las “barras” musicales. Veámoslo con atención:
- Canal izquierdo: el código que pinta es line(counter,0, counter, offset*64) y por lo tanto se genera una línea que tiene como componente horizontal el número de muestra al que estemos accediendo (de 0 a 255) y que se prolonga desde la parte superior de la ventana (Y = 0) hasta la parte central de ésta (Y = 128) por lo que respecta a la componente vertical. Los valores de muestras, entre 0 y 2, hacen que las líneas asuman longitudes de entre 0 y 128 píxeles.
- Canal derecho: el código que pinta es line(counter,128, counter, 128+offset*64) y por lo tanto se genera una línea que tiene como componente horizontal el número de muestra al que estemos accediendo (de 0 a 255) y que se prolonga desde la parte central de la ventana (Y = 128) hasta la parte inferior de ésta (Y = 256) por lo que respecta a la componente vertical. Los valores de muestras, entre 0 y 2, hacen que las líneas asuman longitudes de entre 0 y 128 píxeles.
Recordemos que en Processing la coordenada (0,0) es la superior izquierda mientras que la (256,300) es la inferior derecha. Por tanto, las X’s aumentan hacia la derecha y las Y’s hacia abajo, tal y como se muestra en la figura 2.
Finalmente mostramos dos textos amarillos en la ventana indicativos de cada uno de los canales. Para ello modificamos la variable de tipo cadena de texto (String s) según el caso y situamos los textos donde mejor nos conviene con la función text().
Por último y como observamos en el código 3, creamos una función que es obligatoria para el buen funcionamiento de la librería Minim (void stop()). Ésta se ejecutará al salir del programa y en ella se detiene la reproducción de la canción (MySong.close()), el gestor de audio (AudioManager.stop()) y el proceso en sí (super.stop()).
// We will use MINIM as our Audio Library
// To draw colored vertical lines that stand for
// a song's waveform
// Therefore we import it
import ddf.minim.* ;
// We create a Minim Object to manage audio
Minim AudioManager;
// We create an AudioPlayer Object to load a track (song)
AudioPlayer MySong;
// The Setup function that we'll be called only once
void setup(){
// Our window is 256 x 256 pixels
size(256, 256);
// We initialize the AudioManager object
AudioManager = new Minim(this);
// We use the "loadFile" method inside the "minim" object
// to load an MP3 file, 256 samples everytime
MySong = AudioManager.loadFile("sweet.mp3",256);
// We start playing the track/song/file
MySong.play();
// Our lines we'll be 5 pixels wide
strokeWeight(5);
}
// The infinite loop
void draw(){
String s;
// The offset allows us to work within
// the [0,2] window instead of [-1,1]
float offset;
// Black color for the background
background(0);
// A randomized color for our lines
stroke(random(256),random(256),random(256));
// Every loop, 256 samples to iterate within
// MySong.bufferSize() tells us the length of the audio buffer
// We'll draw a vertical line every 10 samples
for(int counter = 0; counter < MySong.bufferSize() - 1; counter+=10){
// We draw our vertical lines! remember how Processing manages vertical coords!
// We convert the amplitude of the sample
// from the [-1,1] range to the [0,2] range
// Firstly the left channel
offset = MySong.left.get(counter)+1;
line(counter,0, counter, offset*64);
// Secondly the right channel
offset = MySong.right.get(counter)+1;
line(counter,128, counter, 128+offset*64);
// We will provide with some yellow text to identify channels
fill(255,255,0);
// Text for the left channel
s = "Left channel";
text(s, 5, 100, 100, 80);
// Text for the right channel
s = "Right channel";
text(s, 5, 230, 100, 80);
}
}
// Minim needs a method to stop
void stop(){
MySong.close();
AudioManager.stop();
super.stop();
}
Código 4. Programa al completo. Reproducción de audio.
Probando y editando codigo, creo que me hacen falta mas opciones pero lo que aquí muestra es bueno.
Útil la librería MINIM, como herramienta la librería para realizar capturas de audio, sintesis de midi, así como midi básico.
excelente desarrollo del ejercicio, un abrazo.