fix: voz TTS no sonaba en display_global por voz online y falta de activación
- getVozES() prefiere voces locales (offline) sobre Microsoft Online que fallan silenciosamente sin internet o con latencia - cancel() antes de speak() para limpiar cola atascada - keepalive sin check !speaking, cada 10s - sonidoActivo persiste en localStorage; banner rojo si no activado - AudioContext se resume si suspended antes de reproducir Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
4b054c5afc
commit
cbcc0a9d8d
@@ -373,6 +373,10 @@ $_hasVideo = (bool)$_tvVideo;
|
||||
<div class="dot"></div>
|
||||
</div>
|
||||
|
||||
<div id="sonido-banner" onclick="activarSonido()" style="display:none;position:fixed;bottom:0;left:0;right:0;z-index:9999;background:rgba(239,68,68,.92);color:#fff;padding:.6rem 1.2rem;font-size:1rem;font-weight:600;cursor:pointer;align-items:center;justify-content:center;gap:.6rem">
|
||||
<i class="fas fa-volume-mute"></i> Haz clic aquí para activar el sonido
|
||||
</div>
|
||||
|
||||
<div id="ann-overlay">
|
||||
<div class="ann-bg"></div>
|
||||
<div class="ann-card">
|
||||
@@ -466,21 +470,30 @@ let audioCtx = null, sonidoActivo = false;
|
||||
|
||||
function activarSonido() {
|
||||
try {
|
||||
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
if (!audioCtx) {
|
||||
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
} else if (audioCtx.state === 'suspended') {
|
||||
audioCtx.resume();
|
||||
}
|
||||
const g = audioCtx.createGain(); g.gain.setValueAtTime(0.001, audioCtx.currentTime);
|
||||
const o = audioCtx.createOscillator(); o.connect(g); g.connect(audioCtx.destination);
|
||||
o.start(); o.stop(audioCtx.currentTime + 0.05);
|
||||
} catch (_) {}
|
||||
sonidoActivo = true;
|
||||
localStorage.setItem('turneroSonido', '1');
|
||||
const btn = document.getElementById('btn-sonido');
|
||||
if (btn) { btn.innerHTML = '<i class="fas fa-volume-up"></i>'; btn.classList.add('on'); }
|
||||
const banner = document.getElementById('sonido-banner');
|
||||
if (banner) banner.style.display = 'none';
|
||||
setTimeout(playBeep, 100);
|
||||
}
|
||||
document.addEventListener('click', () => { if (!sonidoActivo) activarSonido(); }, { once: true });
|
||||
document.addEventListener('touchstart', () => { if (!sonidoActivo) activarSonido(); }, { once: true });
|
||||
// Sin once:true para que cualquier clic reactive si la página recargó
|
||||
document.addEventListener('click', () => { if (!sonidoActivo) activarSonido(); });
|
||||
document.addEventListener('touchstart', () => { if (!sonidoActivo) activarSonido(); });
|
||||
|
||||
function playBeep() {
|
||||
if (!sonidoActivo || !audioCtx) return;
|
||||
if (audioCtx.state === 'suspended') { audioCtx.resume().then(playBeep); return; }
|
||||
try {
|
||||
const osc = audioCtx.createOscillator();
|
||||
const g = audioCtx.createGain();
|
||||
@@ -496,13 +509,21 @@ function playBeep() {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Selección de voz española — cargada una vez, reutilizada en cada anuncio
|
||||
// Selección de voz española — preferir locales (offline) sobre online para evitar fallos de red
|
||||
let _vozES = null;
|
||||
function getVozES() {
|
||||
if (_vozES) return _vozES;
|
||||
const voices = window.speechSynthesis.getVoices();
|
||||
// Preferencia: es-CO → es-419 → es-MX → es-US → es-ES → cualquier es-*
|
||||
for (const lang of ['es-CO','es-419','es-MX','es-US','es-ES']) {
|
||||
// 1) Voz local (localService=true) en orden de preferencia
|
||||
for (const lang of ['es-CO','es-MX','es-US','es-ES']) {
|
||||
const v = voices.find(v => v.lang === lang && v.localService);
|
||||
if (v) { _vozES = v; return v; }
|
||||
}
|
||||
// 2) Cualquier voz local en español
|
||||
const vLocal = voices.find(v => v.lang.startsWith('es') && v.localService);
|
||||
if (vLocal) { _vozES = vLocal; return vLocal; }
|
||||
// 3) Fallback: voz online (puede fallar sin internet)
|
||||
for (const lang of ['es-CO','es-MX','es-US','es-ES']) {
|
||||
const v = voices.find(v => v.lang === lang);
|
||||
if (v) { _vozES = v; return v; }
|
||||
}
|
||||
@@ -512,13 +533,16 @@ function getVozES() {
|
||||
}
|
||||
if ('speechSynthesis' in window) {
|
||||
window.speechSynthesis.onvoiceschanged = () => { _vozES = null; getVozES(); };
|
||||
// Keepalive: Chrome pausa speechSynthesis tras ~15 min sin interacción del usuario
|
||||
// Keepalive: Chrome pausa speechSynthesis tras ~15 min — correr siempre, no solo cuando !speaking
|
||||
setInterval(() => {
|
||||
if (!window.speechSynthesis.speaking) {
|
||||
window.speechSynthesis.pause();
|
||||
window.speechSynthesis.resume();
|
||||
}
|
||||
}, 14000);
|
||||
window.speechSynthesis.pause();
|
||||
window.speechSynthesis.resume();
|
||||
}, 10000);
|
||||
}
|
||||
// Auto-restore sonido si estaba activo antes de un reload
|
||||
if (localStorage.getItem('turneroSonido') === '1') {
|
||||
const banner = document.getElementById('sonido-banner');
|
||||
if (banner) banner.style.display = 'flex';
|
||||
}
|
||||
|
||||
function anunciarTurno(codigo, destino, paciente) {
|
||||
@@ -534,7 +558,7 @@ function anunciarTurno(codigo, destino, paciente) {
|
||||
const voz = getVozES();
|
||||
if (voz) utt.voice = voz;
|
||||
utt.rate = 0.85; utt.pitch = 1.05; utt.volume = 1;
|
||||
setTimeout(() => window.speechSynthesis.speak(utt), 800);
|
||||
setTimeout(() => { window.speechSynthesis.cancel(); window.speechSynthesis.speak(utt); }, 300);
|
||||
return utt;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user