viernes, 11 de mayo de 2012

Adecuación de la aplicación SMSMorseConverter


Como comentamos en el blog anterior, nuestros objetivos eran los siguientes:

- Adecuar la aplicación de traducción de mensajes a código Morse para que sea funcional para las personas sordo-ciegas.
-Realizar una batería de pruebas con diversas situaciones de uso de la aplicación y limpiar esos posibles errores, como hicimos con la aplicación AudioSense.

A la hora de adecuar la aplicación para los usuarios hacia los que va dirigida ésta, lo que estábamos buscando era una funcionalidad práctica y sencilla. Por ello, lo que pensamos para que pudiesen elegir los mensajes de una forma más sencilla fue que seleccionaran los mensajes a través de los botones de volumen (situados en los laterales de los dispositivos android), seleccionando los mensajes por numero (el 1 el que tiene la fecha más reciente) con un máximo de 10 mensajes (que se puede configurar a más o a menos). Para implementarlo lo que hicimos fue crear un KeyEvent que es la clase encargada de dar funcionalidad a los botones físicos.

Para configurar los botones creamos un método que regulase que hacer cuando pulsásemos los botones físicos y definimos acciones con ellos (clase Event) y los distintos botones. Para diferenciar que botón era pulsado creamos un switch con dos case y un default. En el primer case se situaba subir el volumen, y cuando lo reconocía ejecutaba el método que había dentro del case y el segundo era el de bajar el volumen. Si no se pulsaba ninguna de estas dos teclas los botones hacían lo que venga definido por el sistema operativo de Android.

int action = event.getAction();
        int keyCode = event.getKeyCode();
            switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_UP:
                if (action == KeyEvent.ACTION_UP) {
               …
case KeyEvent.KEYCODE_VOLUME_DOWN:
                if (action == KeyEvent.ACTION_DOWN) {
              

Para comprobar que funcionaba el código al pulsar los botones pusimos que fuese aumentando (botón de subir el volumen) o disminuyendo (botón de bajar el volumen) una variable, número de mensaje seleccionado, que al pulsar cualquier de los botones nos mostraba por pantalla esta, y así pudimos comprobar que los botones tenían una funcionalidad correcta.

Para implementar el programa, introducimos varias condiciones dentro de los case de cada botón ya que era necesario que el programa reconociese que no había mensajes en la bandeja de entrada (lo hicimos mediante la condición cur.moveToPosition(0)==false), o que nunca pasara del decimo mensaje (forzando que una variable no pudiese pasar de 10), ya que habíamos decidido que el máximo número de mensajes que pudiese traducir el usuario fuese 10. Otra de las condiciones que pusimos fue que no pudiese bajar del primer mensaje ya que no tendría sentido. En el caso de no haber mensajes vibra 500ms como hemos mencionado anteriormente.

Después de esto, se nos ocurrió implementar que cada vez que pasases un mensaje mediante los botones de volumen te tradujese el número de mensaje a código Morse mediante vibraciones para que el usuario supiese en que mensaje estaba y así saber más fácilmente que mensaje quiere traducir. Recordar siempre que los mensajes están ordenados por fecha de antigüedad siendo el primero el más reciente.

En la pantalla lo único que pusimos fue un botón que ocupase la pantalla entera, para que cuando decida qué mensaje quiere traducir el usuario solo tenga que tocar la pantalla y el dispositivo empezará a traducir a código Morse el mensaje seleccionado. Sabrá si es el mensaje a traducir pues como hemos dicho antes se le muestra por vibraciones el número de mensaje asociado al mensaje en el que se sitúa.

Después intentamos hacer varias mejoras sobre el código que habíamos hecho. La primera era añadir la hora a la fecha del mensaje ya que podía llegar a ser útil para diferenciar mejor los mensajes. Para hacerlo añadimos al método que habíamos usado anteriormente para traducir la fecha  a un formato reconocido (dd-mm-aaaa), la hora (dd-mm-aaaa H: hh:mm). Para que diese la hora correcta tuvimos que dar la zona horaria de España (GMT+1), pero como estamos actualmente en horario de verano la hora que nos señalaba del mensaje era una hora menos que la que era realmente por eso pusimos al final GMT+2.

SimpleDateFormat datePattern = new SimpleDateFormat ("yyyy-MM-dd HH:mm");;
datePattern.setTimeZone(TimeZone.getTimeZone("GMT+2:00"));

La otra mejora que pensamos que podía ser útil, fue que pudiese elegir la velocidad del Morse, es decir, que durasen más (pulsación larga) o menos (pulsación corta) los tiempos de punto o raya. Esto podía ser útil para personas que están aprendiendo Morse o para gente con menos sensibilidad en el tacto. Les damos la posibilidad del tiempo base normal (100ms) o tiempo base lento (200ms). Para ello lo que implementamos fue el método onLongClickListener, que lo que hace es que distingue entre una pulsación larga y una pulsación corta del botón de la pantalla antes mencionado. Para poder implementar esta segunda velocidad lo que hicimos fue crear otra clase que se llamase MorseCodeConverterDouble que tuviese como velocidad base 200ms.

boton.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {

También para que para el usuario le fuese más fácil descifrar el mensaje en vez de darle el formato de mensaje que anteriormente habíamos pensado lo que hicimos fue implementarlo de otra manera:
T: ‘nº teléfono’. F:’fecha’. H: ‘hora’. M:’mensaje’ß Cadena de caracteres a traducir a código Morse.

Al final de la aplicación nos encontramos con un problema con las vibraciones, ya que cuando le dábamos al botón atrás o al botón home (botón central) o sucedía cualquier otro evento del móvil externo a la aplicación, y  ésta estaba traduciéndose un mensaje, no paraba de vibrar. Para solucionarlo lo que hicimos fue implementar todas las vibraciones desde una única variable declarada al principio de la actividad, y definimos el método onPause y onStop para cancelar las vibraciones (parecido a la aplicación AudioSense).

También cuando se está traduciendo un mensaje y cambiamos el numero de mensaje porque no nos interesa el que habíamos seleccionado (pulsamos los botones de volumen), se cancelan las vibraciones asociadas al mensaje para traducir el número de mensaje siguiente o anterior dependiendo del botón que haya pulsado el usuario. Esto es muy útil para cancelar la traducción del mensaje antes seleccionado por si no hubiese seleccionado el usuario el mensaje deseado y así no tiene que esperar a finalizar la traducción completa.

Como habíamos implementado esta doble velocidad de Morse (larga o corta pulsación), cambiamos el tiempo que estaba vibrando si no había mensajes de 500ms a 1000ms, para no crear confusión al usuario. Por tanto si no hay mensajes en la bandeja de entrada del dispositivo, vibrará 1 segundo tanto al pulsar la pantalla como al pulsar el botón de volumen.

Probando la aplicación final, nos dimos cuenta de que si girábamos la pantalla se cancelaban las vibraciones, por lo tanto como pensábamos que no era de utilidad, cancelamos el giro de pantalla desde el manifest.xml de la aplicación como en la aplicación AudioSense.

 El resultado final de la aplicación, una vez pasada ésta una batería de posibles casos de uso de la aplicación dónde surgían errores, los cuales se han corregido y son los que antes hemos mencionado, es el siguiente:



Pronto subiremos un vídeo explicativo de nuestras aplicaciones: Idiotizador, AudioSense, Morse y SMSMorseConverter.

sábado, 5 de mayo de 2012

SMSMorseConverter

      Una vez terminada a tiempo la aplicación AudioSense (con todas las mejoras propuestas) decidimos hacer la aplicación de traducción de mensajes a código Morse (SMSMorseConverter). Se trata de una aplicación que traduce a las personas ciegas/sordo-ciegas  los mensajes de texto de la bandeja de entrada de su dispositivo móvil a código Morse.
 
  Los primeros objetivos propuestos para esta aplicación fueron los siguientes:

- Desarrollar una aplicación Android, que traduzca los mensajes de la bandeja de entrada a código Morse a través de vibraciones.

- Investigar cómo acceder a los mensajes y extraerlos de la bandeja de entrada.

-  Conseguir coger el texto adecuado de cada mensaje.

- Enseñar los mensajes por pantalla para que el usuario pueda seleccionar el que quiera.

- Traducir la cadena de caracteres elegida a código Morse.

Lo primero que hicimos fue crear un programa capaz de acceder a los mensajes y que nos pasara a un TextView los mensajes para poder traducirlos. En un primer momento intentamos acceder con los métodos de la clase Intent, pero no éramos capaces de acceder a los mensajes de esta forma.

      Tras buscar posibles soluciones encontramos una más sencilla, el uso de la clase Cursor, con la que se puede acceder a los mensajes y a otros servicios de Android a través de la instrucción:
        
         Uri uri = Uri.parse("donde quieras acceder");
         cur = getContentResolver().query(uri, null, null, null,null);
        
     Para acceder a mensajes lo que teníamos que hacer era añadir Uri.parse("content://sms/inbox”), y así  conseguimos acceder a la bandeja de entrada de los mensajes del dispositivo.

          Para extraer el cuerpo del mensaje y el número de teléfono del usuario que nos manda el mensaje tenemos que hacerlo mediante el método getString a la columna correcta donde se encontraba el dato que queríamos. Pero aquí nos encontramos con un problema ya que nos dimos cuenta que esa columna no es la misma para cada uno de los dispositivos Android, por eso en vez de poner una constante, obtuvimos mediante el método (getColumnIndex()), la columna donde se encontraba el número de teléfono y el cuerpo del mensaje (variable para cada dispositivo). Finalmente decidimos que la fecha también pues era un parámetro importante para el usuario.
  
            Una vez hecho esto, solo teníamos que pasar al TextView con setText() la cadena de caracteres, y presentarlo en la pantalla de nuestra aplicación.
  
           Ya habíamos conseguido acceder a los mensajes. Ahora teníamos que poder seleccionar un mensaje de la lista y traducirlo a vibraciones. Para que pudiésemos seleccionar un mensaje de la lista vimos necesario implementar un ListView, que te proporciona una lista de elementos que puedes seleccionar, ya que era más sencillo que un conjunto de text Views.
  
           Para crear un ListView era necesario también crear un ArrayAdapter, donde tiene que estar incluida la lista de elementos que va a mostrar el ListView. Para crear este ArrayAdapter tuvimos algunos problemas ya que no sabíamos que parámetros te pedían en el constructor. Una vez conseguido buscando a través de Internet, al ejecutarlo en el emulador de Eclipse, nos salía un NullPointerException, y no entendíamos el porqué. Nos dimos cuenta que la razón era porque el emulador no tenía ningún  mensaje, y por lo tanto no se creaba una lista de elementos, haciendo que fuese null la lista y el ListView no la pudiera representar.
 
            Una vez probada en el móvil, ya vimos que funcionaba perfectamente hasta el momento.
 
           El siguiente paso era definir qué hacer cuando seleccionabas un elemento de la lista. Lo único que queríamos que hiciese por ahora es que cuando “dásemos” pusiese seleccionado, para comprobar que funcionaba. Pensamos que teníamos que poner el mismo método que usábamos cuando pulsamos un botón, setOnClickListener(), pero nos dimos cuenta de que no era así, ya que era un elemento de una lista, no un botón, por tanto el método que había que usar era:
 
   lista.setOnItemClickListener(new OnItemClickListener() {
         public void onItemClick(AdapterView<?> a, View v, int position, long id) {
 
            Éste nos permite realizar cualquier acción al pulsar un elemento de la lista, la cual la tenemos que definir (en nuestro caso, traducción a código Morse de la cadena de caracteres deseada), y además podíamos distinguir entre posiciones con el parámetro position.
           
            Después de haber conseguido esto, solo nos quedaba traducir el mensaje a código Morse. Buscando encontramos que ya había hechos algunos código que traducían  a código Morse. El que más nos gustó fue el de Android developers, al cual le pasas un String como parámetro y te lo traduce a vibraciones.

            Ya habíamos terminado la aplicación, pero nos dimos cuenta que el traductor solo tenía las letras básicas (no tenía la ñ) y no tenía ningún signo como el guión, el punto… Por eso añadimos al código de Android developers algunos caracteres más: “ñ”, “.”, “,”, ”?”, “!”, “-“, “:”; cambiando el código inicial.                                                                                                                           
 
            Con ello decidimos también tratar las vocales con acentos, es decir, el código inicial las trataba como caracteres desconocidos y nosotros los que hicimos fue hacer que las vocales con acentos fuesen tratadas como vocales sin éstos.
 
            Además, nos encontramos con un error en el código, ya que si ponías un parámetro desconocido, en vez de no vibrar, vibraba como si fuese un punto (en código Morse) por cada carácter desconocido, es decir, como la “e” en código Morse, creando confusión. Esto se producía porque si no reconocía un carácter, dejaban un espacio (GAP), y lo añadían al array de los parámetros de vibración, sin darse cuenta que el GAP, en el patrón de vibración se ponía en los huecos de vibración del patrón y por lo tanto vibraba el tiempo que tendría que estar en “silencio”. Para solucionarlo lo que hicimos fue añadir al array de no reconocer caracteres un 0 en vez de un tiempo de GAP.
 
Para que la aplicación no forzase cierre cuando en el dispositivo no hubiese mensajes lo que hicimos es que cuando no hay mensajes te muestre un elemento de la lista que ponga “no hay mensajes”, y si lo pulsamos el dispositivo vibra durante 500ms, para que la persona sepa que no hay mensajes.
 
Los mensajes los mostrábamos con la siguiente estructura y es la cadena de caracteres que se traducía a código Morse:
 
"Mensaje del telefono: nº de teléfono. Con fecha: fecha y con contenido: cuerpo del mensaje
 
Se muestra a continuación la primera interfaz gráfica:
  
        Después de hacer este programa nos dimos cuenta de que no era del todo funcional para las personas sordo ciegas (a las que iba dirigida la aplicación), ya que tenían que seleccionar el mensaje tocando en una parte concreta de la pantalla y no sería útil para ellos, por eso pensamos otra manera de implementarlo seleccionando los mensajes de una forma diferente.
 
    Próximos objetivos:

- Adecuar la aplicación de traducción de mensajes a código Morse para que sea funcional para las personas sordo-ciegas.

- Realizar una batería de pruebas con diversas situaciones de uso de la aplicación y limpiar esos posibles errores, como hicimos con la aplicación AudioSense.

jueves, 26 de abril de 2012

Mejoras y resolución de problemas de la aplicación AudioSense


Los objetivos marcados para esta semana son los siguientes:
  • Mejorar el cálculo de las energías medias por barra para el ecualizador, distinguiendo entre voz y música.
  • Distinción entre voz y música para el cálculo del patrón de vibración.
  •  Mejorar el patrón de vibración en lo que se refiere a duración máxima del patrón de vibración.
  • Deshabilitar el giro de pantalla.
  • Solucionar el problema con el mensaje inicial del toogleButton.   
  • Deshabilitar las vibraciones en las diferentes opciones de cierre de la aplicación.
Para mejorar el aspecto del ecualizador gráfico hacemos que la relación no sea lineal, en lo que se refiere a muestras por barra. 

Cuando se trata de música usamos las frecuencias de las notas musicales de la cuarta hasta la séptima octava. Tuvimos que aumentar el número de barras de 16 a 28. Por tanto en el caso de música cada barra contiene la energía media de la frecuencia de la nota musical y de su sostenido, si existe.  

Cuando se trata de voz hacemos uso de la escala Bark, la cual nos da las frecuencias más importantes de la voz. Como no llegábamos a cubrir 28 valores para 28 barras y sabiendo que el espectro más importante de la voz se encuentra entre 0 y 2kHz, añadimos varias frecuencias que creíamos también importantes.

También decidimos mejorar el patrón de vibración, siendo diferente el cálculo en caso de ser voz que en el caso de ser música. Lo que hemos hecho es que si se trata de la voz sólo analizaremos el rango de frecuencias de 0 a 2000Hz ignorando el resto. Esto lo haremos simplemente sumando en la energía total  las 64 primeras muestras en el caso de que sea voz lo que grabamos. También la frecuencia central de máxima energía sólo se estudiará en el rango de 0 a 2kHz. En el caso de música, el cálculo de patrones sigue siendo el mismo al nombrado en anteriores blogs.

A la hora de pensar cómo mejorar los patrones de vibración, se nos ocurrió que podíamos mejorarlos si permitíamos al usuario que decidiese cual era el tiempo máximo que podía durar el patrón de vibración, es decir, que elija entre 1 segundo, 500ms, 200ms y 100ms. Esta opción depende de la apreciación táctil del usuario y la velocidad del hablante con el que desea comunicarse. Lo que hicimos fue definir 4 radioButtons y los unimos en un grupo para que sólo uno de ellos pueda estar activado. Los valores asociados a éstos son los inversos de los tiempos arriba mencionados, es decir, 10Hz, 5Hz, 2Hz o 1Hz. La selección de estos botones depende de lo dicho antes, por tanto si el usuario tiene buena apreciación táctil y la velocidad del hablante con el que desea comunicarse es normal debe elegir 10Hz y si al contrario tiene mala apreciación táctil debe elegir 1Hz y el hablante debe llevar un ritmo lento de habla para no perderse ninguna información.

Para hacer lo anteriormente explicado creamos 4 radio botones y los unimos en un grupo para que solo uno de ellos pueda estar activado. 

La elección de una de las 4 opciones, cambiará ciertos parámetros del código que son: la duración máxima del patrón de vibración y cada cuantas interrupciones del Timer se refrescan los patrones de vibración.

La interfaz gráfica final de la aplicación AudioSense es la siguiente:

 
Después  de todo esto, nos dedicamos a resolver ciertos problemas de la aplicación. Un problema que teníamos era que cuando girábamos la pantalla del móvil la aplicación dejaba de funcionar decidimos,  así que decidimos que la mejor solución era bloquear el giro de pantalla. Para ello en el fichero Manifest.xml de nuestra aplicación android, donde también se definen los permisos, lo que hicimos fue añadir lo siguiente:              android:screenOrientation="nosensor"

Teníamos otro problema con el mensaje/texto inicial asociado al toogleButton, puesto que al iniciar la aplicación en vez de poner Empezar ponía Desconectado, aunque en el resto de pulsaciones de botón los mensajes eran los correctos. Después de intentar encontrar una posible solución de configuración de botones, encontramos otra solución. Esta fue simular una doble pulsación  del toogleButton mediante la instrucción performClick() al inicio de la aplicación, consiguiendo así que mostrará el mensaje inicial correcto. 

Teníamos otro problema que era que las vibraciones no se terminaban siempre, sólo en el uso normal explicado en el manual de usuario. Es decir, que si pulsábamos el botón central, el botón de atrás o sucedía cualquier otro evento del dispositivo móvil ajeno a la aplicación la vibración de nuestra aplicación continuaba.

Esto era un grave problema ya que para  que dejara de vibrar y de grabar teníamos que entrar en las opciones del dispositivo y detener la aplicación desde allí. Lo que necesitábamos es que se finalizará la actividad en esos casos.

Realizando varias pruebas nos dimos cuenta de que al pulsar la tecla de atrás o el botón central o cualquier otro evento del móvil, se ejecuta el onPause y después se fuerza la ejecución del onStop. Por lo que lo que hacemos es definirlos y en el caso de que se esté grabando y vibrando en ese momento, es decir, la variable grabando esté a 1 ponemos la variable de grabando a 0 para que termine todo el proceso en paralelo de grabación y de vibración (al poner grabando a 0 se finaliza el proceso background de AsyncTask). Además en el onStop realizamos el método performClick() el cual hace que se pulse el botón de Terminar, para que cuando volvamos a entrar a la aplicación, ésta esté en su estado inicial.

                Para también arreglar el problema asociado a la pulsación única del botón Salir, como hemos comentado en el apartado de Listeners, forzamos que en su listener además de poner grabando a 0, se realice el onDestroy el cual cancela las vibración, termina la actividad actual mediante el método finish y borra todo espacio de memoria ocupado por el proceso de grabación mediante el método release().

Nuestros próximos objetivos serán:
  • Creación de una aplicación de traducción de mensajes a código Morse.

miércoles, 18 de abril de 2012

Patrón de vibración y ecualizador


Los objetivos marcados para esta semana eran los siguientes:
- Aumentar el ecualizador de 8 barras a 16 barras.
- Quitar el ruido ambiente (30dB).
- Eliminar la componente continua.
- Calcular los patrones de vibración, y asociar la actualización de éstos al Timer.
- Mejorar el aspecto del ecualizador añadiéndole colores según la altura de la barra.

Lo primero que hicimos fue aumentar el número de barras del ecualizador. Para ello lo que hicimos fue en vez de incluir en el cálculo de la energía media 16 muestras por cada barra, fue reducirlo a la media de energías de 8 muestras por barra y distribuir de manera diferente las barras en la pantalla, en vez de tener un ancho de 30 pixeles pasa a 20, y además comienza el dibujo más pegado al borde inferior de la pantalla.

                El siguiente objetivo que nos marcamos fue eliminar la continua, que para ello lo único que hicimos fue anular el término que ocupa la posición 0 del array de energías. Esto lo hicimos ya que la continua no aporta nada en nuestro estudio. También eliminamos de la representación del ecualizador el ruido ambiente restándole 30 dB a la energía media de cada barra ya calculada, si la barra tenía una altura menor de 30dB, automáticamente se ponía a 0.

                Para que se diferenciasen más las alturas de cada barra, multiplicamos por un factor mayor que la unidad la energía media de cada barra, en particular, un factor de 2,5. Observamos que para que el ecualizador aportará más información gráfica era necesario restarle más dB de ruido ambiente por lo que al final dejamos que el ruido ambiente era alrededor de 120dB, dada la alta sensibilidad del micrófono. 

                Lo siguiente que hicimos fue calcular el patrón de vibración. Lo hicimos mediante un método sencillo el cual debemos probar más su funcionalidad. El patrón de vibración se va a calcular cada 100ms, por lo que un patrón se repetirá durante 100ms siempre, ya que la modificación de los valores del patrón por ahora va asociado al mismo Timer que el refrescar el ecualizador gráfico. El cálculo se lleva a cabo con la frecuencia  a la que la energía es máxima y con la energía total. Para calcular el tiempo que durará el patrón lo hacemos con la frecuencia de mayor energía, haciendo una relación lineal. Imponemos que la duración mínima sea 10ms y la máxima 100ms, y lo relacionamos con nuestras posibles frecuencias (0-4000Hz) de máxima energía, siendo 10ms cuando la frecuencia sea 4000Hz y 100ms cuando la frecuencia se 0Hz. Para calcular el ciclo de trabajo de dicho patrón lo hacemos mediante la energía total en dB, imponiendo un máximo de 360 dB y un mínimo de 300 dB haciendo una relación lineal. Por ejemplo si la energía total es de 360dB, el ciclo de trabajo será del 100%  y si la energía total es de 300 dB o inferior será un ciclo de trabajo nulo, consiguiendo que el ruido del entorno no se traduzca a vibraciones, solo se traduzca la voz "cercana" al micrófono. 

                Uno de los problemas que nos encontramos en este momento era que cuando dábamos a parar la aplicación, y salíamos de ella, no paraba de vibrar el dispositivo, hasta que no se forzaba el cierre completo de la aplicación. Esto lo solucionamos añadiendo en el botón de salir una igualdad a null del objeto Vibrate.
                Una vez funcionaba todo lo anterior comenzamos a mejorar el aspecto del ecualizador. En un primer momento todas las barras eran del mismo color independientemente de la altura, lo que implementamos fue que las barras se dividiesen en cuadrados de 20 pixeles cada uno de un color que iba cambiando gradualmente con respecto al cuadrado anterior y posterior, así la barra según la altura a la que llegaba tenía unos colores u otros.  Este cambio gradual de cambio de color lo hicimos definiendo un array de constantes en los que poníamos los colores para después configurarlos a través de “Color.rgb(red, green, blue)”, y añadiéndolos a un objeto de la clase Paint. Nos surgieron una serie de problemas a la hora de implementar estas mejoras ya que configurábamos mal alguna variable, y nos dibujaba solo una barra, o incluso una barra en diagonal. Después de arreglar estos pequeños problemas el aspecto final de la aplicación es el siguiente. 


            
   
Hemos añadido un botón para que el usuario pueda seleccionar si lo que quiere “reconocer” es voz, sino será música pero queda implementarlo destacando en cada caso unas frecuencias diferentes del espectro.

Los siguientes objetivos que nos marcamos fueron:
- Mejorar el patrón de vibración.
- Hacer que la resolución del ecualizador no sea lineal, es decir, diferenciar si el sonido captado del entorno es voz o música, ya que habrá que destacar diferentes frecuencias de trabajo.
- Deshabilitar el giro de pantalla.


jueves, 12 de abril de 2012

Resolución de problemas


Para este “hito” nos plantemos los siguientes objetivos:

- “Dar la vuelta” al ecualizador gráfico.
- Corregir el error de la fft, explicado en el blog anterior.
- Comprobar el correcto funcionamiento de la fft (y un poco de todo el programa)
mediante la respuesta activa del ecualizador gráfico.

Comenzamos “dando la vuelta” al ecualizador. Para ello lo que hicimos fue calcular el número
de pixeles que tiene la pantalla del dispositivo android disponibles para pintar, es decir, sin
contar los pixeles dedicados a los botones. A partir de ese valor calculamos la nueva esquina
superior del cuadrado, restándole a éste la longitud del rectángulo a pintar (la energía media
de la banda de frecuencias) empezándose así a dibujar las barras del ecualizador desde arriba.
El resto de parámetros permanecieron iguales. Este primer objetivo nos costó poco resolverlo.

El siguiente paso fue más complicado ya que no entendíamos porque la primera fft que se
realizaba nos daba valores correctos (valores en el rango de lo esperado) pero las siguientes
fft, el valor real de la fft de cada muestra crecía exponencialmente con valores negativos hasta
al final desbordar el float. Hicimos diferentes pruebas para reconocer que era lo que causaba
el error. La prueba que nos hizo descubrir cuál era el error, fue realizar la fft repetidas veces al
mismo array de muestras grabadas, sin darnos cuenta de que el método de la fft nos devolvía
la fft en ese mismo array, y observamos que ocurría lo mismo arriba explicado. Por tanto,
deducimos que lo que pasaba en nuestro caso era que no cambiaba el valor del array de las
muestras grabadas inicialmente, el cual introducimos como parámetro al método, sino que
siempre era la mismo, ya que el código de realiza fft era con lenguaje C y utilizaba punteros,
y al volver a llamar al método de la fft se quedaba con el array anterior introducido como
parámetro el cual contienía ahora la fft, realizándose así la fft de la fft de la ftt…de la muestra
incial grabada, sin utilizar el resto de muestras lo que era nuestro propósito.

Para solucionarlo, lo que hicimos fue crear el array de muestras grabadas dentro del
propio “while” donde se realiza todo el cálculo de la fft, energías y frecuencias y cambiar el
archivo C para que en vez de usar punteros a arrays usara esos mismos arrays. Ahora si nos
daba valores coherentes pero la aplicación cerraba alrededor de los 10 segundos sin previo
aviso. Pensamos que era problema de tiempo de ejecución como por ejemplo que no le daba
tiempo al método a realizar todos los cálculos antes de coger la siguiente muestra. Probamos
diversas soluciones como: cambiar las dimensiones de los arrays, comentar los códigos
cuyo tiempo de ejecución pudiese ser mayor… Después de todas estas pruebas nos dimos
cuenta de que el problema de que se cerrase la aplicación (sin previo aviso) no era de falta de
tiempo sino de falta de memoria. El dispositivo al quedarse sin memoria disponible cerraba la
aplicación. Este exceso de “gasto” de memoria era porque en cada vuelta de while se creaba
un nuevo array de muestras. Investigamos distintos métodos para solucionar este apartado
pero al final lo que hicimos fue asignar el valor null al array al final del código del while y luego
liberar memoria para que así lo eliminase con la instrucción: “System.gc();”.

Una vez hecho esto, el programa funcionaba sin ningún problema por tiempo indefinido, y
pudimos comprobar mediante sonidos que introducíamos en el micrófono del dispositivo
android que todo el proceso (incluido la fft) era correcto, porque las barras del ecualizador
gráfico se movían coherentemente a los sonidos.



El aspecto de la aplicación es el siguiente:




Una vez conseguidos los objetivos arriba mencionados y que funcionará todo, nos propusimos los siguientes objetivos:
-          - Calcular los parámetros de vibración, para ya llevar a cabo la acción final de vibrar con ellos.
-          - Ver qué frecuencia de vibración puede reconocer nuestro sentido táctil.
-          - Mejorar las barras del ecualizador gráfico: quitar la continua, quitar el ruido(30 dB), escalar el resto de dB para que se aprecie mayor cambio de energías en las bandas, colores…


miércoles, 11 de abril de 2012

Ecualizador Gráfico y Timer


El siguiente paso que nos propusimos fue realizar el ecualizador gráfico. Estos fueron los objetivos de este paso:
-          - Aprender a dibujar en la pantalla de nuestro programa.
-          - Hacer que apareciese en la pantalla el ecualizador gráfico, que representa lo grabado en tiempo real.
-          - Construir un Timer para poder “refrescar” los valores  del ecualizador gráfico cada 50ms.

Para poder dibujar en la pantalla de nuestra aplicación necesitábamos aprender a usar la clase Canvas, en particular, el método onDraw() de Canvas. Para hacerlo creamos la clase VisualizerView (subclase de la clase View) y implementamos dentro de ésta el método onDraw(Canvas canvas). La implementación de éste método por ahora era muy sencilla, sólo intentamos pintar un cuadrado con el método drawRect(). El “último” paso para ver si habíamos aprendido a pintar un cuadrado en la pantalla, fue ejecutar el método onDraw()en el método onCreate()(el cual se ejecuta al arranque de la aplicación).

Una vez hecho todo esto, nos surgió un problema ya que intentábamos en una misma pantalla tanto poner los botones con la ayuda del eclipse (con el archivo main.xml) como pintar un cuadrado en ésta mediante la programación arriba descrita. Este problema lo que producía era un conflicto de “quien manda sobre la pantalla” y nos mandaba forzar cierre nada más intentar arrancar la aplicación.

Para solucionarlo lo que tuvimos que hacer fue crear toda la interfaz del programa sin usar la ayuda del eclipse, es decir, tuvimos que crear también los botones mediante programación sin ayuda del simulador, definiendo su tamaño, su situación en pantalla… Una vez hecho esto, el problema arriba mencionado fue resuelto.

Ya aparecido el cuadrado en pantalla, aprendimos a configurar diferentes parámetros de dibujo como: color (clase Paint), colocación en una situación específica de la pantalla, largo y ancho de un rectángulo para dibujar nuestras barras del ecualizador…
  
El siguiente paso era aprender a crear el Timer, para que poder refrescar el ecualizador gráfico cada 50ms. Para crear el Timer había que programar un método run() para que pasado el  periodo que hayamos configurado gracias al método Schedule() se ejecute el método run(). En ese método lo único que ejecutaremos fue  el  método updateVisualizer(), que lo único que hace es actualizar el dibujo. Este fue el código:
t = new Timer();
                           scanTask = new TimerTask() {
public void run() {
                                        mVisualizerView.updateVisualizer();
                                                   }
                           };
                           t.schedule(scanTask, 0, 50);

Al ejecutar el programa en este momento nos daba un error y nos obligaba a forzar cierre de nuevo. Buscando el error nos dimos cuenta de que el Timer se ejecutaba en un “hilo” paralelo al propio hilo donde se ejecutaba el resto del programa, y el error se debía a que el hilo propio no permitía que el hilo paralelo del Timer dibujase en “su” pantalla.

Para solucionar este problema vimos que era necesario crear un Handler, junto con un Runnable para que el Timer se ejecute en el hilo principal y así se pudiese actualizar la pantalla mediante éste, quedando el siguiente código:
t = new Timer();
scanTask = new TimerTask() {
public void run() {
             handler.post(new Runnable() {
                    public void run() {
                           mVisualizerView.updateVisualizer();                                     }
             });
      }};
t.schedule(scanTask, 0, 50);

Tras haber conseguido dibujar un cuadrado (y aprender a configurar más parámetros de dibujo) y que funcionase el Timer, probamos ambas funcionalidades dibujando un rectángulo que fuera aumentando cada segundo un pixel de largo, de esta manera comprobamos que funcionaban ambas a la perfección, y el programa estaba listo para “pintar” el ecualizador gráfico.

Empezamos a crear el ecualizador asociado al Timer, y el siguiente problema que nos encontramos fue que no sabíamos como dibujar de abajo hacia arriba ya que al crear un LinearLayout te obliga a empezar el siguiente objeto siempre debajo del anterior tomando como origen de coordenadas la esquina superior izquierda de la pantalla. La solución que propusimos de manera provisional fue crear el ecualizador al revés, para ello creamos ocho rectángulos (barras del ecualizador gráfico) que fueran variando con la energía media por rango de frecuencias.

Al intentar comprobar la funcionalidad de la fft (explicada en el blog anterior) mediante la visualización del ecualizador nos dimos cuenta que la fft no funcionaba bien, ya que después de dos o tres  segundos el valor de las muestras toman valores negativos y de gran valor, por lo tanto la energía daba valores muy grandes y llegaba un momento que las variables se desbordan y daban 0, e incluso los pixeles de la pantalla no soportaban esos valores, por lo que al pasar unos segundos ya no dibuja el ecualizador.

Después de todo esto, los objetivos que nos hemos marcado para la siguiente semana son:
- “Dar la vuelta” al ecualizador gráfico
- Solucionar el problema de la fft.
- Comprobar la funcionalidad de la fft por respuesta del ecualizador gráfico.

martes, 20 de marzo de 2012

SIGUIENTE PASO


Objetivos:
- Importar un código en lenguaje C a nuestra aplicación android, mediante Cygwin y la herramienta NDK-build.
-Realizar la FTT de la señal grabada, calcular la energía de cada frecuencia de ésta y obtener la banda de frecuencia que contiene la mayor energía.

Lo primero que hicimos fue descargarnos una herramienta que fuese capaz de construir una librería android “.so” a partir de un código en lenguaje C, que fue el NDK-build. Para poder generarla necesitamos también instalar un terminal, en este caso, el Cygwin.

Todo esto era necesario ya que el código en C tenía un método que realizaba la FFT de una señal a partir de sus muestras y era justo lo que necesitábamos. La otra opción era realizarlo mediante el filtrado de la práctica normal pero la FFT nos abría un abanico de funcionalidades.

Lo primero que tuvimos que hacer fue crear un directorio “jni” dentro de nuestro proyecto “AudioSense”. En este directorio incluimos tanto el archivo “.mk”, donde definimos el nombre de la librería “.so” a crear y la ruta del fichero C, como el fichero C, a partir del cual creamos la librería y el cual contiene el método de la FFT.

Desde Cygwin, situados dentro de la carpeta “jni” arriba nombrada, ejecutamos el NDK-build, el cual nos genera en ese proyecto la librería deseada.

Para un correcto funcionamiento, en el código, tuvimos que declarar el método como “native” además de cargar la librería.

El primer problema que nos encontramos es que al “llamar” a la método realfft()  compilaba pero no era reconocido en la ejecución.

Para solucionar esto tuvimos que añadir algunos prefijos al nombre del método  para fijar mejor su ubicación y que fuese “reconocido” (y también a los métodos llamados dentro de éste) y añadir varios argumentos  al propio método (JNIEnv y jobject).

Volvimos a realizar el proceso arriba explicado desde el Cygwin y el método ya era reconocido tanto en la compilación como en la ejecución.

Una vez hecho todo esto, obtuvimos la FFT de la señal  mediante el método realfft(), al cual le pasábamos como argumento 257 muestras de la señal grabada. Este método nos devuelve 128 valores de la FFT de la señal repartidos entre 0 y 4KHz (tanto su parte real como su parte imaginaria).

A partir de estos valores, calculamos la energía en cada frecuencia y luego obtuvimos, mediante una media ponderada, la “frecuencia central” de la banda que acumula mayor energía. Mediante el valor de esta “frecuencia central”  y la energía total calcularemos los parámetros de vibración.

Nuestro siguiente objetivo es un ecualizador gráfico para comprobar la funcionalidad de todo lo arriba explicado.