Pantalla TV: dejar de cortar la voz que arranca despacio

Dos defectos del mismo cambio de hoy, que explican lo que se oía.

La detección de "el navegador bloqueó el audio" daba 1,9 s de plazo y, si en
ese tiempo no había empezado a hablar, cancelaba y repetía con otra voz. Pero
una voz remota tarda en arrancar porque se baja de internet: el plazo la
declaraba muerta cuando iba a hablar, le cortaba la frase y la repetía con la
voz del navegador. De ahí que se oyera media frase y luego otra distinta, a
veces de hombre y a veces de mujer. Ahora se le pregunta al sintetizador si
está trabajando antes de darla por fallida, y el plazo sube a 3,2 s.

Y la voz se elegía al llegar el turno, no al hablar. La lista de voces la
carga el navegador de forma asíncrona, así que en los primeros llamados tras
abrir la página venía vacía y se hablaba con la voz por defecto. Ahora se
resuelve en el momento de hablar, cuando ya está cargada.

Comprobado: con voz instantánea, lenta (2 s) y muy lenta (4 s) ya no hay
corte y siempre habla Sabina; con el audio bloqueado se sigue avisando para
que el cartel no quede pegado.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-12 08:23:08 -05:00
co-authored by Claude Opus 5
parent 8f94a8e012
commit b3a6be8f17
+22 -7
View File
@@ -663,26 +663,30 @@ function anunciarTurno(codigo, destino, paciente, onFin) {
let avisado = false;
const avisar = () => { if (!avisado) { avisado = true; onFin(); } };
const decir = (voz, esReintento) => {
const decir = (esReintento) => {
const utt = new SpeechSynthesisUtterance(texto);
utt.lang = 'es-CO';
if (voz) utt.voice = voz;
utt.rate = 0.95; utt.pitch = 1.05; utt.volume = 1;
let arranco = false;
utt.addEventListener('start', () => { arranco = true; });
utt.addEventListener('end', avisar);
utt.addEventListener('error', () => esReintento ? avisar() : decir(null, true));
utt.addEventListener('error', () => esReintento ? avisar() : decir(true));
// Que speak() no lance nada no significa que vaya a sonar: si el
// navegador bloquea el audio, o la voz no sirve, no pasa absolutamente
// nada y 'error' tampoco llega. Solo se nota porque 'start' no ocurre.
const margen = (esReintento ? 0 : BEEP_MS) + 1200;
const margen = (esReintento ? 0 : BEEP_MS) + 2500;
setTimeout(() => {
if (arranco) return;
// Antes de darla por fallida hay que preguntarle al sintetizador:
// una voz remota tarda en arrancar porque se baja de internet, y
// cancelarla aquí era cortarle la frase y repetirla con otra voz.
// De ahí que a veces se oyera media frase y luego otra distinta.
if (window.speechSynthesis.speaking || window.speechSynthesis.pending) return;
if (esReintento) { avisar(); return; }
window.speechSynthesis.cancel();
decir(null, true); // segunda oportunidad, con la voz del navegador
decir(true); // segunda oportunidad, con la voz del navegador
}, margen);
// Solo cuando hay algo en curso hace falta cancelar: Chrome ignora un
@@ -690,10 +694,21 @@ function anunciarTurno(codigo, destino, paciente, onFin) {
if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {
window.speechSynthesis.cancel();
}
setTimeout(() => window.speechSynthesis.speak(utt), esReintento ? 0 : BEEP_MS);
setTimeout(() => {
// La voz se elige aquí y no al crear el anuncio: el navegador carga
// la lista de forma asíncrona, y en los primeros llamados tras abrir
// la página todavía venía vacía. Entonces no se asignaba voz y
// hablaba la del navegador —de ahí que a veces sonara un hombre y a
// veces una mujer—. A esta altura ya está cargada.
if (!esReintento) {
const voz = getVozES();
if (voz) utt.voice = voz;
}
window.speechSynthesis.speak(utt);
}, esReintento ? 0 : BEEP_MS);
};
decir(getVozES(), false);
decir(false);
return true;
}