We choose the Moon

Melodías con Arduino: Für Elise

Jorge García Tíscar| September 05, 2011

Nada más natural cuando uno se encuentra un altavoz que intentar que suene. La opción más razonable pues, si eres una persona normal, es conectarlo a un reproductor de música. Pero, ¿y si no lo eres mucho, tienes un Arduino y tiempo libre? La cita de Scott Adams de ahí arriba deja poco lugar a dudas! Vamos a pasar de una partitura a música real, a través de la física, la electrónica, y lo que se tercie.

1. Planteamiento teórico

1.1. De música a física

¿Y qué podríamos hacer sonar? Por ejemplo, todo un clásico: Für Elise (Para Elisa) de Ludwig van Beethoven. Si uno se entretiene en buscar su partitura, se encuentra algo así:

Fur-elise-preview

¿Pero, qué representa esto físicamente? Según mi ínfima educación musical, el sonido tiene cuatro cualidades básicas: altura, intensidad, duración y timbre. Vamos a considerar, como lo haría monsieur Fourier, cada sonido como una señal formada por una combinación de \(i\) ondas senoidales:

\[ S(t)=\sum_{i=1}^\infty A_i\sin(2\pi f_i t+\phi_i) \]

De esta manera, podemos darle sentido físico a las distintas cualidades del sonido:

  • Altura: se refiere a lo agudo o grave del sonido, lo que físicamente viene dado por la frecuencia de la onda (o armónico) principal (la de más amplitud) del sonido.
  • Intensidad: dada por la amplitud de las ondas que forman el sonido.
  • Duración: simplemente, tiempo durante el cual las ondas son audibles
  • Timbre: distingue un instrumento de otro, y depende de los distintos armónicos secundarios del espectro de Fourier, distintos del principal que define la altura.

Lo que la notación musical hace es representar estas cualidades físicas. ¿Y cómo lo hace? Cada figura situada en un pentagrama representa una altura y una duración. La figura indica su duración: la negra (♩) indica 1 tiempo, la corchea 1/2 tiempo, la blanca 2 tiempos, etc. Para traducirlas a segundos se emplea el tempo, que indica los tiempos o beats por minuto (bpm):

\[\text{duracion}(\text{figura})=\frac{60}{\text{tempo}\cdot\text{figura}}\]

En cuanto a la altura, viene dada por su posición en el pentagrama. En este caso vamos a considerar la escala cromática (12 semitonos). El sostenido lo representamos con una “s”:

Hay que tener en cuenta que esta escala se repite a lo largo de octavas. Un sonido está a una octava de otro cuando su frecuencia fundamental se dobla o se reduce a la mitad. Así, vamos a representar 7 octavas de 12 semitonos iguales (~26.1638 Hz) partiendo del la de la 4ª octava, de valor de referencia 440Hz (lo que se conoce como afinación de temperamento igual) mediante la fórmula:

\[ \text{frecuencia}(\text{nota},\text{octava})=440\cdot 2^{(\text{octava}-4)+(\text{nota}-10)/12} \]

1.2. De física a electrónica

Ahora ya conocemos [un poquito de] la física detrás de la música; debemos representar ondas de distintas frecuencias durante tiempos determinados. Desgraciadamente, generar una senoide de esa manera es muy complicado con un humilde Arduino, así que en su lugar emplearemos un pulso cuadrado PWM de la misma frecuencia. Para la nota La4:

Vemos que esta señal posee las características mencionadas: una cierta intensidad, en voltios, una duración, y una altura, dada por el período (= inverso de la frencuencia). En cuanto al timbre, vendrá dado por la descomposición de Fourier de la onda cuadrada como suma de varias ondas senoidales (armónicos):

Se observa que la señal senoidal sólo tiene una componente, de frecuencia 440Hz (nota la4 totalmente pura); la señal cuadrada se representa como la suma de muchos armónicos. Así pues, el objetivo será asociar cada nota a una señal cuadrada de cierta frecuencia, que controlaremos con su “tiempo arriba” (señalado en el gráfico) de esta manera:

\[ \text{tiempo arriba}(\text{nota},\text{octava})=\frac{1}{2\ \text{frecuencia}(\text{nota},\text{octava})} \]

2. Aplicación práctica

2.1. Programación

Ahora que ya tenemos claro que vamos a generar ondas cuadradas de distinta frencuencia, pasamos a programar el Arduino para ello. En primer lugar necesitamos una tabla de cada nota con su “tiempo arriba”, que generamos en Mathematica a partir de las fórmulas anteriores:

1
2
3
4
5
6
7
ta = Flatten[Table[Round[10^6/(2*(440*2^((o - 4) + (n - 10)/12)))],
     {n, 1, 12}, {o, 1, 7}]];
notas=Flatten[Table[n<>ToString[o],{n,{"do","dos","re","res","mi","fa",
     "fas","sol","sols","la","las","si"}},{o,1,7}]];
def=Flatten[Table["#define",{i,1,7 12}]];
tabla=Transpose[{def,notas,ta}];
Export["C:\\bla bla\\Fur Elise/tabla.xls",tabla]

Esto genera un documento Excel que copiaremos al bloc de notas y renombraremos como “notas.h” para incluirlo en el código del Arduino. Para éste último, necesitamos convertir la melodía de Für Elise en un vector de notas y duraciones (figuras):

1
int m[] = {4,mi5,4,res5,4,mi5,4,res5,4,mi5,4,si4,4,re5,4,do5}

De esta manera se han representado los dos primeros compases. A continuación, el código de Arduino; en primer lugar, iniciar las variables (hemos seleccionado un tempo de 69 bmp que se corresponde aproximadamente al “poco moto” que indica la partitura):

1
2
3
4
5
6
7
8
9
10
11
12
#include "notas.h" // Definiciones de notas
// Variables de control
int pinAltavoz = 5;
int tempo = 69;
int m[] = {4,mi5,4,res5,4,mi5,4,res5,4,mi5,4,si4,4,re5,4,do5};

// Variables auxiliares 
long d[sizeof(m)/sizeof(int)];
int ta[sizeof(m)/sizeof(int)];
int i = 0;
int id = 0;
long t = 0;

Después, repartimos el vector melodía en dos vectores, uno de notas y otro de duraciones, diferenciando entre duraciones negativas, que indican una duración de x negras y positivas, que indican una duración de 1/x negras. (Por ejemplo, 4 = semicorchea):

1
2
3
4
5
6
7
8
9
10
11
12
13
void setup(){
pinMode(pinAltavoz, OUTPUT);
//Redistribuir vector melodia
for(i=0;i
    if(m[i]&gt;0){ //Obtener duración de cada nota
      d[id] = 60000/(tempo*m[i]);
    }else{
      d[id] = (-60000*m[i])/tempo;
    }
    ta[id]=m[i+1]; //Vector de tiempos arriba
    id++;
  }
}

Finalmente, podemos generar la señal cuadrada en función del “tiempo arriba” de cada nota y su duración; hemos añadido también la nota “sil”, silencio, con un “tiempo arriba” nulo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void loop(){
  i = 0;
  for (i=0;i
    t = 0;
    if(ta[i]==0){
      delay(d[i]); // Silencio
    }else{
      while(t&lt;d[i]*1000){             //Generar onda cuadrada:
        digitalWrite(pinAltavoz, HIGH);  //Subir señal
        delayMicroseconds(ta[i]);        //Mantener tiempo arriba
        digitalWrite(pinAltavoz, LOW);   //Bajar señal
        delayMicroseconds(ta[i]);        //Mantener abajo el mismo tiempo
        t = t + 2*ta[i];
      }
    }
    delay(10); // Pausa tras cada nota
  }
  delay(1000); // Pausa tras melodia
}

2.2. Del software al hardware

Ahora que ya tenemos el programa escrito, podemos pasar a realizar el montaje necesario con el Arduino, basándonos en el montaje para PWM que hemos explicado en entradas anteriores.

Dado que el altavoz consume más potencia de la que el Arduino puede suministrar, añadimos una alimentación externa, controlada mediante un transistor MOSFET, a cuya puerta enviamos la señal.

En nuestro caso concreto, el altavoz tiene una resistencia de 32 \(\Omega\) y admite una potencia máxima de 0.25W; esto significa que se le debe suministrar aproximadamente 3V de tensión.

Sin embargo, nosotros le suministramos los 3.3V de una fuente de alimentación reutilizada. Recomendamos no exceder la potencia máxima de los componentes si les tenéis aprecio. El montaje físico del Arduino es el siguiente:

2.3. Funcionamiento!

Después de transcribir un poco más de la melodía a la forma de vector, y corregir los interminables bugs del código…por fin, un pequeño ejemplo del sistema ya montado y reproduciendo, tras mucho esfuerzo, los primeros compases del Für Elise y del Himno a la alegría, otra archifamosa obra de Beethoven:

3. Conclusiones

Y eso es todo! A pesar de lo largo de la entrada (~1200 palabras), me ha parecido oportuno contar cómo suelen ser los problemas de ingeniería, de principio a fin. Se empieza, en primer lugar, por una idea, un problema… A continuación, se analiza el problema de manera teórica: ¿sé lo suficiente para resolverlo? ¿qué me falta por entender? ¿cuál debe ser mi plan de acción?.

Una vez con la teoría clara, se pasa a la práctica: trasladar el plan teórico al mundo real, con ayuda de códigos informáticos, electrónica, hidráulica, mecánica… pero ésta es la fase que, desgraciadamente, menos se practica en las escuelas de ingeniería.

Así que espero que esta entrada larga y rollera sirva para hacerse una idea de lo que representa hasta el problema de ingeniería más sencillo: un largo camino, desde las fórmulas hasta el soldador.

Pensadlo la próxima vez que oigáis una melodía electrónica... =)

avatar Thanks for reading! To share this post, use this permalink

Comments