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.
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.
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:
- 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.
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:
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:
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.
- Sonido1.Origen establécese ao primeiro elemento da lista notas, que é 1.mp3.
- Chámase a Sonido1.Reproducir e reprodúcese o son.
- 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.
- Sonido1.Origen establécese ao segundo elemento das lista notas, que é 3.mp3.
- Chámase a Sonido1.Reproducir e reprodúcese o son correspondente ao arquivo 3.mp3.
- 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.
- Sonido1.Origen establécese ao terceiro elemento das lista notas, que é 6.mp3.
- Chámase a Sonido1.Reproducir e reprodúcese o son correspondente ao arquivo 6.mp3.
- 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:
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:
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.
- Sonido1.Origen establécese ao primeiro elemento da lista, que é 1.mp3
- Chámase a Sonido1.Reproducir para reproducir o son anterior.
- Dado que contador (que ten o valor 1) é menor que a cantidade de notas (que é 3):
- 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.
- A variable contador increméntase nunha unidade, é dicir, agora xa ten o valor 2.
- Actívase a conta atrás do reloxo cambiando o valor de Reloj1.Habilitado a Cierto.
- 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.
- Sonido1.Origen establécese ao segundo elemento da lista, que é 2.mp3.
- Chámase a Sonido1.Reproducir para reproducir o son anterior.
- Dado que contador (que ten o valor 2) é menor que a cantidade de notas (que é 3):
- 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.
- A variable contador increméntase nunha unidade, é dicir, agora xa ten o valor 3.
- Actívase a conta atrás do reloxo cambiando o valor de Reloj1.Habilitado a Cierto.
- 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.
- Sonido1.Origen establécese ao terceiro elemento da lista, que é 6.mp3.
- Chámase a Sonido1.Reproducir para reproducir o son anterior.
- 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
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.