Cómo está hecho

FocusLec también tiene una intención didáctica, de forma que sirva para ver cómo se puede plantear una aplicación de este tamaño.

El punto de partida

Para resaltar la palabra que suena hace falta que el motor de voz avise de cuál está pronunciando. En iOS el sistema documenta ese aviso como el mecanismo previsto para hacer exactamente esto. En Android es opcional para el motor instalado: el método del framework está definido como vacío por defecto y la documentación aclara que solo se invoca si el motor suministra información temporal. No hay ninguna clave que permita consultarlo por adelantado.

Arquitectura

Diagrama de contexto.
Tres capas. El motor de sincronización llega al plugin solo a través de TtsEngine.

Por qué hay una interfaz propia delante del plugin

Cuando empezó el proyecto, el plugin de voz de la comunidad emitía el aviso de rango solo en Android. Meter una interfaz propia por delante dejaba la puerta abierta a cambiar de plugin, o a escribir uno, sin tocar nada más.

La interfaz TtsEngine y sus implementaciones. La tercera, el simulador, permite probar el motor de sincronización sin ningún dispositivo.

El modelo de documento

Cada bloque lleva un identificador estable: su ruta estructural más una huella de 32 bits, así demo/p4#113xk17. Una posición de lectura es el par (bloque, ordinal de palabra) y nunca un número global de palabra ni un desplazamiento en píxeles. La diferencia se ve al reabrir un fichero que ha cambiado: un índice global se desplaza entero con que alguien añada un párrafo al principio, mientras que el par aguanta mientras el bloque siga reconociéndose.

En el diagrama hay dos campos que parecen redundantes: textoMostrado y textoNormalizado. Lo que se pinta en pantalla y lo que se manda al motor de voz no son la misma cadena. Hay guiones blandos que desaparecen, espacios que se colapsan, caracteres de anchura cero que sobran y ligaduras tipográficas que ocupan un carácter y se pronuncian como dos. Los avisos del motor vienen medidos sobre la segunda. Los rectángulos que hay que iluminar están en la primera. El resaltado depende de mantener la correspondencia entre las dos, en unidades UTF-16.

Modelo de documento. La posición de lectura cuelga del documento y se expresa siempre en bloque y ordinal.

El recorrido de una palabra, de la voz al recuadro

La frase “Escucha, enfoca, entiende.” recorre la aplicación en catorce pasos, del fichero a la voz y de vuelta a la pantalla. Los botones avanzan y retroceden paso a paso. Verla a pantalla completa.
Secuencia de la sincronización.
Estados del reproductor. Las tres transiciones: cambiar la velocidad, navegar y reanudar, pasan todas por Preparando, porque las tres obligan a detener la locución y volver a encolar el texto restante.

Algunas consideraciones interesantes

Prueba de concepto de sincronización

Texto fijo en el código, sin importación ni ajustes. Una batería automática de 21 lecturas por plataforma (siete textos de prueba a tres velocidades) comprobó que el aviso de palabra llega en Android y en iOS. Comparar el texto recibido con el esperado daba fallos falsos; se pasó a comparar posiciones y la precisión mínima subió de 0,733 a 1.

Modelo de documento y primera pantalla

Bloques con identificador estable, la pantalla de lectura y la correspondencia entre el texto que se pinta y el que se pronuncia.

Importación de documentos

FocusLec está pensada, sobre todo, para documentos EPUB. No obstante, también puede leer documentos en formato TXT, PDF o Word.

Visor para libros enteros

Con un EPUB de 103.925 palabras, pintar la página costaba 4.525 ms y creaba 207.868 elementos. Con la lista virtualizada son 10 ms y 728 elementos.

Sincronización sobre documento real

El texto se envía al motor en trozos de frases completas, bajo el tope de 4000 caracteres de Android. Una sonda decide en cada arranque si el aparato puede marcar por palabra o solo por frase. Cuando un aviso no cuadra con el texto, la posición se recoloca buscando la palabra cerca.

Controles de reproducción

Leer, pausa, seguir, navegación por frase y velocidad. Ninguna plataforma cambia la velocidad de una locución ya empezada: hay que detenerla y volver a encolar desde la palabra en curso.

Tipografía, contraste y posición guardada

Tipografías, tamaños, espaciados y temas de contraste. Las preferencias y el punto de lectura de cada documento se guardan en el dispositivo. El documento no se guarda: ni Android ni iOS mantienen el permiso sobre un fichero elegido con el selector.

Accesibilidad

Contraste medido, un tema en blanco y negro, pruebas con los tamaños de texto máximos del sistema y recorridos con TalkBack y VoiceOver. Si hay un lector de pantalla en marcha, la aplicación avisa de que las dos voces comparten altavoz.

Documentos de Word

Un DOCX es un ZIP con XML dentro, igual que un EPUB. Se aprovecharon el descompresor y el analizador que ya había.

Palabras con barra

Las formas desdobladas como “los/las niños/niñas” se leían con la barra (“los barra las”) o encadenando las dos formas. Al oído, una frase corriente se vuelve difícil de seguir. Ahora se elige si se lee todo o solo una de las formas. El texto en pantalla no cambia.

Leer despacio sin estirar la voz

Al bajar la velocidad, el motor estira cada letra y deja el hueco entre palabras en cero: 54 ms por letra a 1× y 86 a 0,2×. Medido en un iPad y en un Android, pedir 0,2× solo alarga la lectura entre un 70 % y el doble. Por debajo de 0,7× la voz ya no se estira y lo que crece son las pausas. En iOS la velocidad tuvo que ir dentro del marcado SSML: con SSML, el parámetro normal de velocidad se ignora, como dice la documentación de Apple y confirmó el iPad.

Seis idiomas

La aplicación lee documentos en español, inglés, francés, italiano, portugués (de Portugal o de Brasil) y alemán, y su interfaz, incluidas las ventanas de información, está en esos seis idiomas. Leer en otro idioma no es solo cambiar de voz: cada uno trae sus abreviaturas, sus comillas y sus signos. En francés se escribe un espacio fino antes de “?” y “:”, y la escritura inclusiva con punto medio se lee como una sola palabra. En alemán, “3.” es un ordinal y no un final de frase, y hay que reconocer las comillas „así“ y el signo de género de “Lehrer*innen”. Se probó en un iPad y en un Android que la voz avisa de todas las palabras en los cinco idiomas nuevos.

Casos de uso

Qué es un caso de uso

Un caso de uso describe lo que una persona hace con la aplicación para conseguir algo concreto. Es más específico que una épica: de cada uno salen las historias de usuario y las tareas que se estiman y se planifican en cada iteración. En un proyecto ágil sirven para acordar qué hay que construir antes de construirlo y para comprobar después que lo construido lo cumple. Estos son algunos de los de FocusLec.

CU-01
Seguir un texto largo

Una persona abre un documento de estudio y lo sigue con la vista y el oído, con la tipografía, el espaciado y la velocidad que le resultan cómodos.

CU-02
Letra grande y contraste

Una persona amplía mucho el texto, sube el contraste y deja que la marca la lleve por el renglón mientras escucha.

CU-03
Aprender otro idioma

Una persona abre un texto en la lengua que estudia, baja la velocidad y sigue la pronunciación palabra por palabra.

CU-05
Personalización

Una persona ajusta tipografía, tamaño, interlineado, espaciado, contraste, velocidad y aspecto del resaltado. La aplicación lo recuerda para la próxima vez.

CU-06
Documento no compatible

Una persona abre un fichero que no se puede procesar y recibe una explicación de por qué y de qué puede hacer, sin que la aplicación se cierre.

Sincronizar el resaltado de texto con el audio

El camino del aviso de palabra. Mientras no se conoce la frecuencia real de la voz, el recuadro se pinta al llegar el aviso; en cuanto se conoce, cada aviso se programa para el instante del audio en que suena su palabra.

En Android el motor dice en qué instante del audio cae cada palabra, así que el recuadro se puede adelantar lo que se pida. En iOS y en el navegador el aviso llega cuando la palabra ya suena. Durante un tiempo, el ajuste de adelanto no hacía nada en esas plataformas. Se resolvió aprendiendo el ritmo de la voz sobre la marcha: cada aviso dice cuánto duró la palabra anterior y, por sus letras, cuánto durará la siguiente.

Tecnologías y gestión del código

El código se gestiona con Git siguiendo Git Flow.