Ejercicio: Theremin

De Edutec Wiki
Saltar a: navegación, buscar


Introducción

En este ejercicio aprenderemos varias cosas, entre ellas el uso de listas, además de profundizar un poco más respecto al envío y recepción de mensajes entre objetos. Para ello lo que haremos será crear un Theremín con la ayuda de un sensor infrarrojo.


Esquema del circuito

Aunque posteriormente se pueden añadir más componentes, el esquema básico incluye un sensor infrarrojo conectado a los pines de alimentación del Arduino y a uno de los pines de entrada analógica:


Theremin 01.png


Para transformar el valor del sensor analógico a centímetros necesitaremos usar la siguiente fórmula (más información en este enlace):

   distancia (cm) = (3027.4/valor sensor)^1.2134

Como no tenemos el operador de potencia (^) en S4A, debemos transformar la fórmula usando logaritmos, con lo cual queda de la siguiente forma:

   distancia (cm) = e^(1.2134 * ln(3027.4/valor sensor)) 

O bien:

   distancia (cm) = 10^(1.2134 * log(3027.4/valor sensor))

Ambas fórmulas son correctas, aunque nosotros usaremos la primera.

Tocar notas mediante el sensor

Lo primero que intentaremos hacer es crear la melodía mediante el sensor de infrarrojos. Primero, aprovecharemos la fórmula ya desarrollada anteriormente para transformar el valor que obtenemos del sensor de infrarrojos.


Theremin 02.png


Para poder crear sonidos y melodías disponemos de la categoría Sonido, en concreto usaremos la pieza tocar nota[60]durante[0.5]pulsos. Si hacemos click en la flecha de la casilla de la nota, veremos que se despliega un pequeño teclado musical:


Theremin 03.png


Veremos que cada nota se corresponde con una frecuencia y, a su vez, esta se representa con una letra (ver gráfica siguiente):


Theremin 04.png


También podemos ver que el rango de numeración de las notas va desde 48 hasta 72, con lo que tenemos un total de 24 notas que escoger para tocar.

Cómo las tocamos entonces? No podemos usar directamente la variable distancia, ya que acabamos de ver que las notas tienen un rango específico. Podríamos trabajar con una serie de condicionales encadenados, de forma similar a cómo se hizo en el ejercicio 4. Aunque la ventaja principal de usar este método es que tendríamos más control en el rango entre notas, hay que tener en cuenta que también nos quedaría un trozo de código bastante largo (sobretodo si quisiéramos usar las 24 notas!).

Por otra parte, una forma mucho más simple y eficiente sería un sencillo cálculo para transformar el valor de la distancia en centímetros en uno que esté dentro de dicho rango musical. Si recordamos algo del ejercicio 4 (o si ponemos en marcha el programa y observamos la variable distancia), sabremos que el valor mínimo en centímetros que obtenemos mediante el valor que nos da el sensor es algo menor que 7.

Antes de nada crearemos la variable nota donde guardaremos la nota numérica resultante del cálculo. Lo primero que tendríamos que hacer con el valor de la variable distancia es pasarlo a un número entero, ya que éste es decimal:


Theremin 05.png


Ahora, como sabemos que el valor mínimo de la distancia es 7, qué hemos de hacerle para que se transforme en el valor numérico de la nota más baja que podemos tocar, el cual es 48?


Theremin 06.png


Fácil no? Solamente hay que sumarle la diferencia entre ambos (48-7=41). Ahora podemos unirlo al programa principal y añadir la instrucción para tocar la nota obtenida:


Theremin 07.png


Parece que funciona bien, salvo que emite un sonido raro en vez de tocar una nota cuando alejamos la mano del sensor. A qué se debe? Pues principalmente a estamos intentando tocar notas con un valor numérico muy alto (>4000), y eso es debido a que no hemos puesto límite máximo de nota! Eso lo podemos hacer simplemente con un condicional, haciendo que el programa toque la nota sólo cuando esta sea menor o igual a cierto valor, el cual puede ser como máximo 72. La cosa podría quedar así:


Theremin 08.png


De manera que aunque la variable nota adquiera valores más altos que 72, ésta solamente se tocará cuando sea menor que 73 (es decir o menor o igual a 72). Con esto queda cubierto el límite máximo.

Guardar notas en una lista

Ahora que ya tenemos la forma de tocar la melodía, vamos a pensar una manera de “guardarla”. Aquí es donde entran las listas, las cuales, al igual que las variables, son espacios de memoria donde se puede guardar información, con la diferencia de que las variables solo pueden guardar un valor a la vez y las listas varios.

Para empezar, vamos a crear una lista. En la categoría Variables creamos una lista dándole a Nueva lista. Veremos que aparecen una serie de bloques nuevos, de la misma forma que cuando creamos una variable nueva:


Theremin 09.png


Podemos observar que tenemos disponibles varias operaciones con las que trabajar con ellas: añadir un dato, borrarlo o insertarlo dada una posición, reemplazar un valor por otro, y por supuesto consultar uno dada una posición. Hasta podemos obtener el tamaño (actual) de la lista o saber si contiene cierto valor.

Vamos a empezar guardando las notas en la lista. Como ya tenemos una variable con el valor de la nota, lo único que tenemos que hacer es lo usar el bloque de añadir un elemento a la lista, de manera que el programa quede así:


Theremin 10.png


En adelante cada vez que toquemos una nota también la añadiremos a la lista. Vamos a ver cómo hacemos para reproducirla!


Reproducir la lista de notas

Ahora que ya hemos tocado y almacenado unas cuantas notas en nuestra lista, vamos a probar a reproducirlas. Para ello, tenemos que aprender a recorrer una lista.

Imaginemos que hemos guardado una lista de 10 notas. Cómo hacemos para acceder a cada posición de la lista y tocar la nota guardada en ella? Vamos a empezar por lo más básico: tocar una nota:


Theremin 11.png


Hasta aquí bien, pero no es la nota “60” la que queremos tocar, sino la que hay en cada posición de la lista. Entonces tenemos que usar la pieza correspondiente para obtener el valor en una posición de la lista:


Theremin 12.png


Vale, pero si lo dejamos así sólo reproducirá la nota de la primera posición, y además sólo una vez. Cómo hacer que toque tantas veces como notas tiene la lista? Tenemos varias opciones, la más sencilla es usando un bucle de repetición finito:


Theremin 13.png


Haciendo esto repetirá 10 veces la instrucción para tocar la nota, de acuerdo, pero qué ocurre si guardamos más notas? Tenemos que estar cambiando cada vez el número de repeticiones? Lo correcto sería usar el mismo tamaño de la lista, es decir, la cantidad de notas guardadas. Y ya hemos visto antes que tenemos una pieza que nos da dicho tamaño:


Theremin 14.png


Perfecto, pero seguimos teniendo el problema de que sólo tocaremos la primera nota tantas veces como notas tenemos. Qué necesitamos para “apuntar” en cada repetición a una posición consecutiva, empezando por 1 y acabando en un número equivalente al tamaño de la lista? Con una variable que haga de contador. La creamos (la podemos llamar contador o posición o como querais) y la inicializamos con un valor “1”:


Theremin 15.png


Con esto nos aseguramos de que empezará tocando la nota de la primera posición. Ahora toca usar la variable:


Theremin 16.png


Ahora solo queda que la variable aumente en uno en cada iteración. Lo podemos hacer de la siguiente forma:


Theremin 17.png


Así, después de tocar la primera nota en la primera vuelta, aumentará en uno la posición, de forma que en la segunda vuelta la variable valdrá “2”, y así hasta acabar de recorrer la lista. Es importante recordar esto, ya que es el mecanismo básico usado para recorrer listas en la mayoría de lenguajes de programación.

Cómo disparamos este proceso? Tenemos varias opciones, podemos usar simplemente una tecla o dibujar un objeto nuevo y activar este bloque cada vez que le clickeamos encima. Aunque en clase seguramente hayamos hecho un objeto, en este documento simplemente le asignaremos una tecla para ahorrarnos trabajo (por ejemplo p de play):


Theremin 18.png


Si lo probamos, veremos que funciona perfectamente. Sólo hay un problema: si por casualidad tocamos una nota (ya sea adrede o por accidente) ésta sonará y se añadirá a la lista, solapándose con su reproducción y alterándola, cuando quizás no la queríamos añadir. No pasa nada, se puede borrar luego, pero hay una manera de evitar este comportamiento?


Anular el toque y guardado de la nota mientras reproducimos la lista

Vamos a pensar una forma de cómo evitar tocar y guardar una nota mientras se está reproduciendo la lista. Una manera fácil sería usar una variable como un flag, es decir, para determinar el estado en que se encuentra la reproducción. Veamos cómo haríamos esto: primero creamos una variable, llamándola reproduciendo o playing, y entonces podemos decidir que cuando playing esté a 0 significa que no se está reproduciendo la lista de notas y a 1 cuando sí lo esté:


Theremin 19.png


Así pues, ponemos dicha variable a 1 justo antes de iniciar el proceso de reproducir la melodía y al acabar la volvemos a poner a 0. Ahora podemos usarla en el bloque principal. Cómo? Fácil, usando un condicional:


Theremin 20.png


Ahora, solo tocaremos una nota y la añadiremos cuando playing sea igual a 0, es decir, cuando no esté activo el proceso de reproducir la lista de notas. En realidad podríamos unir los dos condicionales en uno solo con un operador lógico y:


Theremin 21.png


Seria conveniente inicializar la variable playing (a 0, ya que por defecto cuando se inicie el programa no estaremos reproduciendo):


Theremin 22.png


Controlar la grabación de las notas en la lista

Ahora que sabemos cómo reproducir las notas, también deberíamos probar a controlar su guardado en la lista. Es decir, por defecto cada vez que tocamos una nota también la guardamos en la lista, pero podríamos sólo tocarla y no guardarla. Cómo podemos controlar eso? Ya hemos visto cómo usar un flag, así que podríamos utilizar otro para este propósito.

Primero crearemos una variable nueva (que se llame grabando o recording) la cual cambiará de estado cada vez que pulsemos una tecla (por ejemplo la r), el cual puede ser a 0 cuando no queramos grabar y 1 cuando sí queramos. Así pues, asumiendo que inicializamos la variable recording a 0 al principio del programa, en caso de que no queramos grabar por defecto, podemos añadir otro condicional para saber cuando añadir la nota o no, dependiendo del valor de dicha variable:


Theremin 23.png


Y añadimos la parte de cambiar el valor de la variable recording:


Theremin 24.png


Pero esto solo nos permite cambiar la variable a 1! Cómo hacemos para que, en caso de volver a darle a la tecla r, se vuelva a poner a 0? Mediante un condicional, podemos comprobar que valor tiene y después asignarle el adecuado:


Theremin 25.png


Pausar y reanudar la reproducción

Ahora que ya podemos controlar la grabación, volvamos con la reproducción de la melodía: qué ocurre si queremos pararla en cualquier momento y que continúe por la misma nota en vez de empezar por el principio? Porque el problema es ese, que cuando pulsamos la tecla p se ejecuta el código desde el principio.

Ésta es quizá la parte más complicada del ejercicio. Cómo podemos saber en qué punto nos paramos? Bueno ésa es fácil, ya que tenemos una variable que nos indica la posición de las notas de la lista. El problema está en que, cada vez que pulsamos la tecla p, inicializamos dicha variable a 1. Eliminemos pues ese problema quitando dicha inicialización:


Theremin 26.png


Pero en algún momento hemos de inicializar la variable posicion a 1, no? Bueno, lo podemos hacer junto a las otras variables, justo después de arrancar el programa:


Theremin 27.png


Ok, ya tenemos un problema resuelto. Ahora bien, cuando sabe el programa cuándo empezar o pausar la reproducción? Nos pasa lo mismo que con la grabación, necesitamos saber en qué estado se encuentra la reproducción para pausarla o para empezarla/reanudarla. Podemos usar entonces el mismo sistema: cuando pulsemos la tecla p, si la variable playing está a 0, la cambiaremos a 1 (cosa que ya hacemos) y cuando este a 1 que la cambie a 0:


Theremin 28.png


Fijaros que ahora hay dos formas de que la variable playing sea 0, una es que acabe la reproducción y que la otra que le pulsemos a la tecla p. En resumen, si la reproducción no finaliza, es decir, que el bucle repetir no acaba porque pulsamos dicha tecla y comienza de nuevo, la variable conserva el valor 1, por lo tanto la condición no se cumple y cambia su valor a 0.

En principio ya estaríamos verdad? Pues no, porque seguimos teniendo otro problema: imaginemos que tenemos 10 notas, y que pausamos la reproducción en la nota número 5. Qué pasará cuando le demos otra vez para que siga? Sólo nos quedaran 5 notas por reproducir, pero el bucle se repetirá 10 veces, con lo que el programa nos tirará un error. Cómo solucionamos esto? Obviamente, cambiando el número de veces que se repite el bucle. El cálculo parece fácil, restar las notas que hemos tocado al número total:

   notas por tocar = notas totales - notas reproducidas = 10 - 5 = 5

Cierto? Pues vamos a implementarlo:


Theremin 29.png


Lo probamos y… funciona? Si pero no del todo bien: la última nota no suena! Por qué? Si lo pensamos un poco mejor, tenemos que cuando la reproducción se pausa, la variable posición se mantiene, y empieza reproduciendo desde ella, es decir, que la repite. En nuestro caso, si pausamos en la nota 5 y reanudamos, el bucle hace lo siguiente:

tocar nota 5 tocar nota 6 tocar nota 7 tocar nota 8 tocar nota 9

Si? La solución más sencilla es hacer una repetición más, o sea hacer 6 repeticiones en vez de 5. Para ello debemos modificar el cálculo anterior:

   notas por tocar = notas totales - (notas reproducidas - 1) = 10 - (5 - 1) = 10 - 4 = 6

Hacemos la pertinente modificación en nuestro programa y comprobamos:


Theremin 30.png


Y eso es todo :)