Mellora o teu Xilófono

xilofono_emulador

Obxectivo

Imos a mellorar o noso Xil

ófono, de tal xeito que agora rexistre  a música xerada.

Esta app permite:

  • Pulsar un botón para reproducir as notas que tocamos antes (e que a app rexistraría).
  • Pulsar un botón para que a app esqueza as notas que rexistrou anteriormente e o usuario poida volver empezar dende cero cunha nova canción.

Que aprenderemos

Trataremos os seguintes conceptos e compoñentes:

  • Usaremos un compoñente reloxo para medir e facer cumprir os tempos de espera entre nota e nota.
  • Veremos como é posible que un procedemento se chame a si mesmo dando lugar ao que se coñece en terminoloxía de programación un procedemento recursivo.
  • Faremos un uso avanzado de variables de tipo lista e veremos como engadir elementos, eliminalos, etc. E todo iso en tempo de execución.

xilofono_emulador

Deseño dos compoñentes

Abre un proxecto co nome Xilófono. Fai clic no menú Conectar e configura o teu dispositivo (ou emulador) para poder facer probas en vivo.

Interface

A interface da nosa app dispón dun teclado con oito notas musicais para unha escala maior pentatónica (de sete notas). Segue os puntos seguintes:

1. Engade un compoñente DisposiciónHorizontal1 debaixo dos botóns.

  • Engade un primeiro botón dentro, renoméao a BtPlay e cambia a súa propiedade Texto á palabra Play.
  • Engade un segundo botón á dereita do anterior, renoméao a BtReset e cambia a súa propiedade Texto á palabra Reset.

2. Dende a categoría Sensores, engade o compoñente Reloj á pantalla. Este compoñente quedará na sección de compoñentes non visibles.

desenoxilofono

Rexistrar as notas musicais

A reprodución de notas musicais cos botóns da app é bastante divertida, con todo, se ademais fose capaz de gravar todas as notas e reproducilas máis tarde entón sería aínda máis divertido.

Se queremos reproducir as notas que tocou o usuario teremos que rexistrar cales foron, en que orde e tamén a cantidade de tempo que transcorre entre unha e outra nota. Pola contra, non seriamos capaces de distinguir entre dúas notas tocadas nunha sucesión rápida e dúas reproducidas cun silencio de 1 segundo entre elas.

Para conseguir o noso obxectivo, a app necesitará dúas variables de tipo lista que chamaremos notas e tempos, e tamén inicializamos para cada variable unha lista baleira:

variables_notas_tempos

  • A lista notas gardará as notas (arquivos de son) que se tocaron e a orde no que se fixo.
  • A lista tempos servirá para gardar en que instante do tempo tocouse  cada nota, así, permite coñecer o lapso de tempo que transcorre entre unha nota e a seguinte.

Agora temos que ampliar os bloques do procedemento ReproducirNota.

reproducirnota_v2

Agora, cada vez que o usuario toca unha nota, temos que gardar o nome do arquivo que se reproduciu (Sonido1.Origen) nas lista notas e o instante no que o fixo nos lista tempos.

Para rexistrar os tempos, facemos uso do bloque Reloj1.Ahora que devolve o valor actual do reloxo do dispositivo, por exemplo, 26 de xaneiro de 2017, 12:00:00, cunha precisión de milisegundos.

Por exemplo, se o usuario toca [C C C D E], as listas terán cinco entradas que poden ser:

  • Notas: 1.mp3, 1.mp3, 1.mp3, 2.mp3, 3.mp3
  • Tempos (omitimos a data): 12:00:01, 12:00:02, 12:00:03, 12:00:03.5, 12:00:04

Botón de Reset

Cando o usuario toque o botón BtReset queremos que as dúas listas (notas e tempos) queden totalmente baleiras. Dado que o usuario non verá ningún cambio na pantalla despois de pulsar este botón, faremos que o dispositivo vibre durante un pequeno lapso de tempo para informarlle de que a app ha reseteado as listas.

Para baleirar as listas cando o usuario toca o botón Reset:

bloquesbtreset

Reproducir notas musicais

Para simplificar o traballo, imos propoñer unha primeira versión do procedemento que se encargará de reproducir as notas gravadas e que non ten en conta o intervalo de tempo entre nota e nota. Despois, complicaremos un pouco máis os bloques para obter a versión definitiva do procedemento.

Procedemento para reproducir as notas gravadas

Para empezar, necesitaremos:

  • Unha nova variable, que chamaremos contador e que usaremos para ir percorrendo a lista de notas e reproducir o ficheiro de son que corresponda para cada entrada desa lista.
  • Un novo procedemento, que chamaremos ReproducirGravacion que se encargará de reproducir unha nota da lista de notas e pasar á seguinte.
  • Un controlador de eventos que se executará cando o usuario toca o botón BtPlay e que se encargará de chamar ao procedemento ReproducirGrabacion.

Bloques necesarios:

reproducirnotasguardadas

Imos a analizar detidamente os bloques de código:

Primeiro, declaramos unha nova variable. Chamámola contador e a inicializamos ao valor cero.

En segundo lugar, temos que definir as accións que debe executar a app cando o usuario toca o botón BtPlay, e para facelo, utilizamos o controlador de evento cuandoBtPlay.Clic. Dentro deste evento:

Primeiro comprobamos (si) se existen notas gardadas e, para sabelo, comparamos se o número de elementos que hai gardados na lista notas é maior a cero.

Se a comparación dá verdadeiro (executarase a parte entonces), é dicir, que si hai notas na lista, inicializamos o variable contador ao valor 1 e despois chamamos ao procedemento ReproducirGravacion.

Se a comprobación dá falso, non sucede nada.

Chegados a este punto, e para explicar cun exemplo como funciona o procedemento ReproducirGravacion, imaxina que temos unha gravación de 3 notas, ou o que é o mesmo, a lista notas contén 3 elementos, por exemplo 1.mp3, 3.mp3 e 6.mp3:

1. A primeira vez que se chama ao procedemento ReproducirGravacion a variable contador é igual a 1.

  1. Sonido1.Origen establécese ao primeiro elemento da lista notas, que é 1.mp3.
  2. Chámase a Sonido1.Reproducir e reprodúcese o son.
  3. Xa que contador (ten o valor 1) é menor que a lonxitude da lista notas (que é 3), a variable contador increméntase en 1. E, acto seguido, volve chamar ao mesmo procedemento ReproducirGravacion. Este feito, que un procedemento se chame a el mesmo, coñécese o terminoloxía de programación co nome de recursividade.

2. A 2ª vez que se chama a ReproducirGravacion a variable contador é igual a 2.

  1. Sonido1.Origen establécese ao segundo elemento das lista notas, que é 3.mp3.
  2. Chámase a Sonido1.Reproducir e reprodúcese o son correspondente ao arquivo 3.mp3.
  3. Posto que contador (ten o valor 2) é menor que a lonxitude das lista notas (que é 3), a variable contador increméntase en 1. E, acto seguido, volve chamar ao mesmo procedemento ReproducirGravacion.

3. A 3ª vez que se chama a ReproducirGravacion a variable contador é igual a 3.

  1. Sonido1.Origen establécese ao terceiro elemento das lista notas, que é 6.mp3.
  2. Chámase a Sonido1.Reproducir e reprodúcese o son correspondente ao arquivo 6.mp3.
  3. Posto que contador (ten o valor 3) que xa non é menor que a lonxitude das lista notas (que é 3), xa non se volve a chamar ao procedemento ReproducirGravacion e a recursividade remata. E tamén remata a reprodución das notas gravadas.

Completar o procedemento ReproducirGravacion, engadimos os tempos

Imos a completar o procedemento ReproducirGravacion considerando os retardos que poden existir entre nota e nota para marcar o ritmo da música.

Para conseguilo, faremos uso do compoñente Reloj1 que xa temos  engadido na interface, por exemplo, se despois de reproducir unha nota musical, a seguinte reprodúcese 3.000 milisegundos (3 segundos) despois, fixaremos a propiedade Reloj1.IntervaloDelTemporizador a 3.000 e habilitaremos o reloxo (Reloj1), transcorrido ese tempo executarase o evento Reloj1.Temporizador e dende alí volveremos chamar ao procedemento ReproducirGravacion para reproducir a nota seguinte.

Seguindo estas indicacións, a imaxe seguinte mostra cales son os cambios que debemos introducir no procedemento ReproducirGravacion:

modificarreproducirgrabacion

Esta será a versión final do procedemento ReproducirGravación.

Tamén teremos que crear os bloques necesarios para o evento Reloj1.Temporizador que terá lugar despois da reprodución de cada nota musical:

bloquereloj

Para tratar de explicalo mellor usaremos un exemplo. Imaxina que as listas conteñen a seguinte información:

  • Lista notas: 1.mp3, 3.mp3 e 6.mp3
  • Lista tempos: 12:00:00, 12:00:01 e 12:00:04

Os pasos que executará a app son os seguintes:

1. A primeira vez que se chama a ReproducirGravacion a variable contador ten o valor 1.

  1. Sonido1.Origen establécese ao primeiro elemento da lista, que é 1.mp3
  2. Chámase a Sonido1.Reproducir para reproducir o son anterior.
  3. Dado que contador (que ten o valor 1) é menor que a cantidade de notas (que é 3):
    1. Reloj1.IntervaloDelTemporizador establécese na diferenza de tempo entre a segunda nota e a primeira, é dicir, 12:00:01 menos 12:00:00 será 1 segundo.
    2. A variable contador increméntase nunha unidade, é dicir, agora xa ten o valor 2.
    3. Actívase a conta atrás do reloxo cambiando o valor de Reloj1.Habilitado a Cierto.
    4. Transcorrido 1 segundo, executarase o evento Reloj1.Temporizador que se encarga de desactivar o compoñente Reloj1 (para que non siga contando de momento) e chamar novamente ao procedemento ReproducirGravacion.

2. A segunda vez que se chama a ReproducirGravacion, a variable contador ten o valor 2.

  1. Sonido1.Origen establécese ao segundo elemento da lista, que é 2.mp3.
  2. Chámase a Sonido1.Reproducir para reproducir o son anterior.
  3. Dado que contador (que ten o valor 2) é menor que a cantidade de notas (que é 3):
    1. Reloj1.IntervaloDelTemporizador establécese na diferenza de tempo entre a terceira nota e a segunda, é dicir, 12:00:04 menos 12:00:01 será 3 segundos.
    2. A variable contador increméntase nunha unidade, é dicir, agora xa ten o valor 3.
    3. Actívase a conta atrás do reloxo cambiando o valor de Reloj1.Habilitado a Cierto.
    4. Transcorridos 3 segundos, executarase o evento Reloj1.Temporizador e desactivarase  o temporizador (Reloj1) e chamarase outra vez a ReproducirGravacion.

3. A terceira vez que se chama a ReproducirGravacion, a variable contador ten o valor 3.

  1. Sonido1.Origen establécese ao terceiro elemento da lista, que é 6.mp3.
  2. Chámase a Sonido1.Reproducir para reproducir o son anterior.
  3. Dado que contador (que ten o valor 3) xa non é menor que a cantidade de notas (que é 3) non pasa nada máis, ou o que é o mesmo, a reprodución da gravación xa rematou.

Programación completa da App Xilófono mellorada

programacionxilofono_1

programacionxilofono_2

Reto:

Se o usuario toca unha nota e vaise a atender algo urxente; máis tarde volve e toca outra nota, as notas formarán parte da mesma canción, algo que probablemente non é o que tiña previsto. A idea é mellorar a app para que, transcorrido unha cantidade de tempo razoable (como un minuto ou dous) entre unha nota e a seguinte, considérese que a primeira nota xa non é útil e que se empezou a tocar unha nova canción.