El problema de la sincronización sigue ahí, he hecho alguna prueba para intentar resolverlo pero no lo he conseguido (estas pruebas no tenían nada que ver con lo de los threads que comentaba el otro día).
Ahora dejaré el tema aparcado por un tiempo. El próximo lunes 17 es la prueba para el D.E.A. (Diploma de Estudios Avanzados). Hay que hacer una exposición oral de unos 10 a 15 minutos sobre los trabajos de investigación realizados en el curso. Toca preparar la presentación con el PowerPoint (o en mi caso con el OpenOffice Impress) y ensayarla de viva voz unas cuantas veces para asegurarse de que no queda muy corta ni muy larga, y de que se dice todo lo que se quiere decir sin hacer pausas ni que se trabe la lengua.
lunes, 10 de septiembre de 2007
martes, 4 de septiembre de 2007
Problema de sincronización
He estado investigando el problema que tengo con la sincronización de la voz y la animación. Para comprenderlo mejor estoy guardando en el fichero de log de la aplicación la mayor cantidad de información posible, y para este caso concreto he añadido los milisegundos que lleva en ejecución el programa para ver los tiempos en los que se reproduce el sonido y se pinta la imagen.
Después de algunas pruebas he averiguado que es lo que pasa, pero todavía no se la razón. No es que la voz y la imagen estén desincronizadas, pasan dos cosas:

La relación entre el número de fonemas y las veces que se pinta la malla no es equitativa ni constante, y es por eso que la animación no va fluida ni sincronizada. Por ejemplo, entre el ms. 6184 y el 6640 se generan 15 fonemas, y el último de ellos (correspondiente a la malla 6) se representa en pantalla 3 veces seguidas. Lo contrario ocurre en entre el ms. 6472 y el 7577. En este caso se pronuncian solo tres fonemas, pero solo se pinta el último de ellos 4 veces. Lo ideal sería que los refrescos de pantalla sucediesen aproximadamente a intervalos regulares con respecto a los fonemas generados.
La solución que se me ocurre puede ser bastante complicada y no es seguro que funcione, pasaría por ajustar de alguna forma la prioridad de ejecución de los threads que se encargan del OpenGL-ES y de la librería de Loquendo. Cosa complicada a priori por dos razones: Desconozco cómo está ese tema en Windows CE y en eVC, y también desconozco si ambas librerías los utilizan y si es posible acceder a ellos.
Después de algunas pruebas he averiguado que es lo que pasa, pero todavía no se la razón. No es que la voz y la imagen estén desincronizadas, pasan dos cosas:
- Se generan más fonemas de los que se representan. Esto no es problemático, ya comentaba en una entrada anterior que solo se representaban seis visemas (las vocales y la eme).
- Por lo anterior, solo uno de cada dos o tres (pongamos tres) fonemas se pintan en pantalla.
- Observando el log y lo que se ve en la pantalla, parece que se reparte mal el tiempo dedicado a cada una de las dos cosas, y así, a veces se pinta cuatro veces seguidas un mismo visema y no se pronuncia niguno nuevo (dando lugar a un chasquido o microcorte en la reproducción del sonido), y otras veces pasa justo lo contrario, el sonido se oye con fluidez pero la animación queda paralizada.

La relación entre el número de fonemas y las veces que se pinta la malla no es equitativa ni constante, y es por eso que la animación no va fluida ni sincronizada. Por ejemplo, entre el ms. 6184 y el 6640 se generan 15 fonemas, y el último de ellos (correspondiente a la malla 6) se representa en pantalla 3 veces seguidas. Lo contrario ocurre en entre el ms. 6472 y el 7577. En este caso se pronuncian solo tres fonemas, pero solo se pinta el último de ellos 4 veces. Lo ideal sería que los refrescos de pantalla sucediesen aproximadamente a intervalos regulares con respecto a los fonemas generados.
La solución que se me ocurre puede ser bastante complicada y no es seguro que funcione, pasaría por ajustar de alguna forma la prioridad de ejecución de los threads que se encargan del OpenGL-ES y de la librería de Loquendo. Cosa complicada a priori por dos razones: Desconozco cómo está ese tema en Windows CE y en eVC, y también desconozco si ambas librerías los utilizan y si es posible acceder a ellos.
miércoles, 29 de agosto de 2007
Animación+voz implementada y un problema
Ya he hecho una primera implementación del avatar moviendo la boca mientras dice algunas frases.
Para esta primera aproximación solo se muestran en la animación unos pocos visemas, que son los de las vocales y la letra 'm', que coinciden más o menos con la posición de la boca de las mallas del avatar que tengo ahora mismo. Si funciona bien, se pueden añadir más.
La forma de hacerlo es con la función de callback que comentaba en la entrada anterior. Cada vez que se pronuncia un fonema se indica su código IPA y de esa forma puedo saber cual es. He programado que se registren estos códigos en el fichero de log de la aplicación, y después he hecho que pronuncie palabras solo con la letra 'a', luego con la 'e', etc. y así he obtenido los de las cinco vocales. Curiosamente para cada una hay dos códigos distintos, aunque no me he fijado bien si unas veces sale uno cuando va acompañado de consonante, o cuando empieza o termina una palabra, etc..
Conocido el fonema ya se la malla que hay que representar, y lo indico en una variable para que en el próximo refresco de pantalla se pinte.
El problema que me he encontrado es que la sincronización entre la animación de la boca y el sonido no es precisamente buena, baste decir que a veces coincide lo que suena con lo que se ve en la pantalla de la PDA.
Para esta primera aproximación solo se muestran en la animación unos pocos visemas, que son los de las vocales y la letra 'm', que coinciden más o menos con la posición de la boca de las mallas del avatar que tengo ahora mismo. Si funciona bien, se pueden añadir más.
La forma de hacerlo es con la función de callback que comentaba en la entrada anterior. Cada vez que se pronuncia un fonema se indica su código IPA y de esa forma puedo saber cual es. He programado que se registren estos códigos en el fichero de log de la aplicación, y después he hecho que pronuncie palabras solo con la letra 'a', luego con la 'e', etc. y así he obtenido los de las cinco vocales. Curiosamente para cada una hay dos códigos distintos, aunque no me he fijado bien si unas veces sale uno cuando va acompañado de consonante, o cuando empieza o termina una palabra, etc..
Conocido el fonema ya se la malla que hay que representar, y lo indico en una variable para que en el próximo refresco de pantalla se pinte.
El problema que me he encontrado es que la sincronización entre la animación de la boca y el sonido no es precisamente buena, baste decir que a veces coincide lo que suena con lo que se ve en la pantalla de la PDA.
Etiquetas:
Animación,
Desarrollo,
Loquendo,
Síntesis de voz
martes, 21 de agosto de 2007
Callbacks
La librería de Loquendo permite definir una función de callback, esto es, una función a la que se llama automáticamente cada vez que hay algún evento para hacer algo con él.
Entre estos eventos están por ejemplo: Comienzo de la reproducción del sonido, finalización, generación de un fonema, buffer de salida lleno, y similares. Estos eventos tiene un identificador de forma que en la función de callback podemos saber cual se ha disparado para hacer con él (si procede) lo que queramos. Uno muy interesante es el de la generación de fonemas. Cada vez que se genera uno se pasa a la función su código IPA y de esta forma se puede saber la forma de la boca que habría que dibujar. ¡En la propia documentación de Loquendo indica que de esta manera se pueden programar avatares!
Entre estos eventos están por ejemplo: Comienzo de la reproducción del sonido, finalización, generación de un fonema, buffer de salida lleno, y similares. Estos eventos tiene un identificador de forma que en la función de callback podemos saber cual se ha disparado para hacer con él (si procede) lo que queramos. Uno muy interesante es el de la generación de fonemas. Cada vez que se genera uno se pasa a la función su código IPA y de esta forma se puede saber la forma de la boca que habría que dibujar. ¡En la propia documentación de Loquendo indica que de esta manera se pueden programar avatares!
sábado, 18 de agosto de 2007
Reproducción de voz bloqueante y no-bloqueante
He comenzado a incluir código para el TTS en el programa del avatar. De momento solo está la iniciación del sistema de síntesis de voz, y una frase que se dice al ejecutar la aplicación.
He estado mirando en el manual el tema de la reproducción del sonido bloqueante y no bloqueante. A mi la que me interesa es la segunda, que consiste simplemente en que la reproducción del sonido de la locución se hace en un thread separado del programa principal y de esa forma se pueden hacer otras cosas mientras.
Con las librerías de Loquendo hay dos formas de hacer esto. Una, la más simple, es usar un parámetro de la función que reproduce la voz, que hace que esta se ejecute en background hasta que termina. La otra forma consiste en hacer polling, es decir, se lanza la reproducción y a continuación se entra en un bucle en el que se va consultando si ha terminado o no.
Ahora tengo que ver cómo hago para que mientras suena la voz, se muestre la imagen adecuada del avatar con la boca en una u otra posición.
He estado mirando en el manual el tema de la reproducción del sonido bloqueante y no bloqueante. A mi la que me interesa es la segunda, que consiste simplemente en que la reproducción del sonido de la locución se hace en un thread separado del programa principal y de esa forma se pueden hacer otras cosas mientras.
Con las librerías de Loquendo hay dos formas de hacer esto. Una, la más simple, es usar un parámetro de la función que reproduce la voz, que hace que esta se ejecute en background hasta que termina. La otra forma consiste en hacer polling, es decir, se lanza la reproducción y a continuación se entra en un bucle en el que se va consultando si ha terminado o no.
Ahora tengo que ver cómo hago para que mientras suena la voz, se muestre la imagen adecuada del avatar con la boca en una u otra posición.
jueves, 16 de agosto de 2007
Problema con GLUT|ES. Solucionado
Me había quedado con un problema inquietante: Al compilar y ejecutar el avatar en el emulador del eVC, todo funcionaba perfectamente, pero al compilar para ejecutarlo sobre una PDA real, primero aparecían mensajes del linker, y cuando se conseguía generar el ejecutable no funcionaba como se suponía que debía hacerlo, es decir, el programa se abría pero no aparecía la imagen del avatar, aunque sí los menús.
Recordaba haber compilado el programa hace algunos meses para ser ejecutado en la PDA de verdad, y que había funcionado correctamente, así que como por suerte tenía alguna copia de seguridad las he utilizado para hacer unas pruebas. La más reciente era de justo el momento en que pasé de usar UG a GLUT|ES para los menús y las ventanas. La he compilado, y el resultado era el mismo que con el avatar, no se mostraba nada en la pantalla, aunque la aplicación se ejecutaba. He probado lo mismo con la copia más antigua, que era del momento justo anterior a que usara GLUT|ES, y al compilar el programa sí que ha funcionado.
Así que el problema parecía estar en esa librería. El día anterior probé varios ejemplos binarios descargados de la página de ZeusCMD para asegurarme de que funiconaba, y así era. Pero entonces he recordado los mensajes de advertencia del linker y todo lo que tuve que hacer para solucionarlos. Esos mensajes solo aparecían al compilar para ARM. He abierto los ejemplos de ZeusCMD y los he recompilado, copiándolos después a la PDA y ejecutándolos. Entonces se ha reproducido el error, la pantalla aparecía negra. Los ejemplos en formato binario que sí que funionaban ya venían compilados y linkados estáticamente, así que parece que el problema es la librería estática de GLUT|ES con la que se enlaza el ejecutable de mi programa al compilarlo.
De hecho, el mensaje de aviso que se muestra:
Al buscarlo en la ayuda del propio eVC, esto es lo que dice:
Es decir, para evitar que aparezca habría que recompilar desde los fuentes la librería de GLUT|ES, cosa nada fácil a priori.
He consultado la página de ZeusCMD, por si la forma de instalar GLUT|ES era incorrecta, y he encontrado un párrafo que había olvidado, que dice que al usar el binario o intentar recompilar los fuentes de la librería había personas que tenían problemas, y ofrece una versión recompilada de la susodicha librería estática. La he descargado y copiado en el lugar correspondiente, y una vez más he compilado el programa del avatar. Al ejecutarlo, ¡ha funcionado! Así que era eso, el binario de la librería estática de GLUT|ES para ARM no funciona bien con el Embedded Visual C++.
Después, solo por curiosidad, he deshecho los pasos que tuve que seguir para que no saliera el error del linker que he escrito antes, y he hecho de nuevo la prueba para asegurarme, confirmando el resultado:
Solucionado esto, espero poder ya de una vez integrar la librería de Loquendo en el programa del avatar.
Recordaba haber compilado el programa hace algunos meses para ser ejecutado en la PDA de verdad, y que había funcionado correctamente, así que como por suerte tenía alguna copia de seguridad las he utilizado para hacer unas pruebas. La más reciente era de justo el momento en que pasé de usar UG a GLUT|ES para los menús y las ventanas. La he compilado, y el resultado era el mismo que con el avatar, no se mostraba nada en la pantalla, aunque la aplicación se ejecutaba. He probado lo mismo con la copia más antigua, que era del momento justo anterior a que usara GLUT|ES, y al compilar el programa sí que ha funcionado.
Así que el problema parecía estar en esa librería. El día anterior probé varios ejemplos binarios descargados de la página de ZeusCMD para asegurarme de que funiconaba, y así era. Pero entonces he recordado los mensajes de advertencia del linker y todo lo que tuve que hacer para solucionarlos. Esos mensajes solo aparecían al compilar para ARM. He abierto los ejemplos de ZeusCMD y los he recompilado, copiándolos después a la PDA y ejecutándolos. Entonces se ha reproducido el error, la pantalla aparecía negra. Los ejemplos en formato binario que sí que funionaban ya venían compilados y linkados estáticamente, así que parece que el problema es la librería estática de GLUT|ES con la que se enlaza el ejecutable de mi programa al compilarlo.
De hecho, el mensaje de aviso que se muestra:
glutes_static.lib(seccook.obj) : warning LNK4078: multiple '.CRT' sections found with different attributes (40300040)
LINK : fatal error LNK1104: cannot open file 'LIBCMT.lib'
Al buscarlo en la ayuda del propio eVC, esto es lo que dice:
LINK found two or more sections that have the same name but different attributes.
Possible cause
An import library or exports file was created by a previous version of LINK or LIB.
Recreate the file and relink.
Es decir, para evitar que aparezca habría que recompilar desde los fuentes la librería de GLUT|ES, cosa nada fácil a priori.
He consultado la página de ZeusCMD, por si la forma de instalar GLUT|ES era incorrecta, y he encontrado un párrafo que había olvidado, que dice que al usar el binario o intentar recompilar los fuentes de la librería había personas que tenían problemas, y ofrece una versión recompilada de la susodicha librería estática. La he descargado y copiado en el lugar correspondiente, y una vez más he compilado el programa del avatar. Al ejecutarlo, ¡ha funcionado! Así que era eso, el binario de la librería estática de GLUT|ES para ARM no funciona bien con el Embedded Visual C++.
Después, solo por curiosidad, he deshecho los pasos que tuve que seguir para que no saliera el error del linker que he escrito antes, y he hecho de nuevo la prueba para asegurarme, confirmando el resultado:
- Con la librería glutes_static.lib para ARM original: Mensajes de error y warnings del linker -> Necesidad de modificar parámetros de compilación -> La aplicación no funciona como debería.
- Con la librería glutes_static.lib para ARM recompilada por ZeusCMD: No aparecen mensajes de error ni warnings -> La aplicación funciona correctamente.
Solucionado esto, espero poder ya de una vez integrar la librería de Loquendo en el programa del avatar.
Etiquetas:
Desarrollo,
Dispositivos móviles,
Embedded Visual C++
jueves, 9 de agosto de 2007
Un avance y un retroceso
El avance: Me ha contestado el servicio técnico de Loquendo y me ha enviado otra licencia para activar su producto. La he instalado y he ejecutado uno de los pogramas de demostración, y esta vez ha funcionado como se esperaba. A continuación he compilado el programa de prueba, lo he ejecutado en la PDA y también ha funcionado, y he podido escuchar por el altavoz el texto escrito en el programa.
Con la síntesis de voz funcionando, he añadido al programa del avatar el código de ejemplo y de nuevo lo he compilado.
El retroceso: Al ejecutarlo en la PDA, la pantalla se queda de color negro y no sale la cabeza del avatar. En cambio, al ejecutarlo en el emulador funciona perfectamente. Al principio del desarrollo recuerdo que hice una prueba sobre el dispositivo real, y funcionaba. ¿Qué ha cambiado desde entonces? Lo único que se me ocurre es que para el manejo de las ventanas pasé de usar UG a GLUT|ES, así que he revisado este último.
No he encontrado nada raro. Para confirmar que funciona correctamente me he bajado algún ejemplo compilado de las lecciones de la página ZeusCMD y los he ejecutado en la PDA, todos correctamente.
Así que el problema tiene que estar en el código. Toca revisarlo, y no se por qué funciona bien en el emulador y mal en la PDA real.
Con la síntesis de voz funcionando, he añadido al programa del avatar el código de ejemplo y de nuevo lo he compilado.
El retroceso: Al ejecutarlo en la PDA, la pantalla se queda de color negro y no sale la cabeza del avatar. En cambio, al ejecutarlo en el emulador funciona perfectamente. Al principio del desarrollo recuerdo que hice una prueba sobre el dispositivo real, y funcionaba. ¿Qué ha cambiado desde entonces? Lo único que se me ocurre es que para el manejo de las ventanas pasé de usar UG a GLUT|ES, así que he revisado este último.
No he encontrado nada raro. Para confirmar que funciona correctamente me he bajado algún ejemplo compilado de las lecciones de la página ZeusCMD y los he ejecutado en la PDA, todos correctamente.
Así que el problema tiene que estar en el código. Toca revisarlo, y no se por qué funciona bien en el emulador y mal en la PDA real.
