Casa de Restauración
Ministerio · Ushuaia
Radio La Voz
FM 94.5 MHz · Ushuaia
Radio La Voz
94.5 MHz
RADIO EN VIVO
Sitio web próximamente
Muy pronto podrás
acceder al contenido
Desde el fin del mundo
La Radio La Voz 94.5 ya transmite desde Ushuaia.
El sitio web completo con predicaciones, programación y contenido del
Ministerio Casa de Restauración estará disponible muy pronto.
Seguinos en nuestras redes
var playBtn = document.getElementById('player-play-btn');
var playIcon = document.getElementById('play-icon');
var pauseIcon = document.getElementById('pause-icon');
var loader = document.getElementById('player-loader');
var logoWrap = document.querySelector('.player-logo-wrap');
var muteBtn = document.getElementById('player-mute-btn');
var volHighIcon = document.getElementById('volume-high');
var volMuteIcon = document.getElementById('volume-mute');
var volumeSlider = document.getElementById('player-volume-slider');
var volumeFill = document.querySelector('.volume-fill');
var statusBadge = document.getElementById('status-badge');
var statusText = document.getElementById('status-text');
var isPlaying = false;
var isBuffering = true; // comienza en CONECTANDO desde el primer frame
var lastVolume = 0.8;
// Configurar volumen inicial
audio.volume = lastVolume;
// Lógica de reproducción
function togglePlay() {
if (isPlaying) {
audio.pause();
audio.src = '';
audio.load();
isPlaying = false;
isBuffering = false;
updateUI();
} else {
isBuffering = true;
updateUI();
audio.src = streamUrl;
audio.load();
var playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.then(function() {
isPlaying = true;
isBuffering = false;
updateUI();
}).catch(function(error) {
console.error("Error al iniciar reproducción:", error);
isPlaying = false;
isBuffering = false;
updateUI();
});
}
}
}
function updateUI() {
statusBadge.className = 'status-badge';
if (isBuffering) {
loader.classList.remove('hidden');
playIcon.classList.add('hidden');
pauseIcon.classList.add('hidden');
statusBadge.classList.add('connecting');
statusText.textContent = 'CONECTANDO...';
logoWrap.classList.remove('playing');
} else if (isPlaying) {
loader.classList.add('hidden');
playIcon.classList.add('hidden');
pauseIcon.classList.remove('hidden');
statusBadge.classList.add('live');
statusText.textContent = 'RADIO EN VIVO';
logoWrap.classList.add('playing');
} else {
loader.classList.add('hidden');
playIcon.classList.remove('hidden');
pauseIcon.classList.add('hidden');
statusBadge.classList.add('paused');
statusText.textContent = 'PAUSADO';
logoWrap.classList.remove('playing');
}
}
playBtn.addEventListener('click', togglePlay);
// Eventos del Elemento Audio
audio.addEventListener('waiting', function() {
isBuffering = true;
updateUI();
});
audio.addEventListener('playing', function() {
isPlaying = true;
isBuffering = false;
updateUI();
});
audio.addEventListener('stalled', function() {
// Intentar reconectar si la señal se detiene de forma inesperada
if (isPlaying && !audio.paused) {
isBuffering = true;
updateUI();
}
});
audio.addEventListener('error', function(e) {
console.error("Error de streaming:", e);
isPlaying = false;
isBuffering = false;
statusBadge.className = 'status-badge';
statusBadge.style.background = 'rgba(239, 68, 68, 0.15)';
statusBadge.style.borderColor = 'rgba(239, 68, 68, 0.4)';
statusBadge.style.color = '#ef4444';
statusText.textContent = 'ERROR DE CONEXIÓN';
loader.classList.add('hidden');
playIcon.classList.remove('hidden');
pauseIcon.classList.add('hidden');
logoWrap.classList.remove('playing');
});
// Controles de Volumen
function updateVolume(val) {
audio.volume = val;
volumeFill.style.width = (val * 100) + '%';
volumeSlider.value = val;
if (val === 0) {
volHighIcon.classList.add('hidden');
volMuteIcon.classList.remove('hidden');
} else {
volHighIcon.classList.remove('hidden');
volMuteIcon.classList.add('hidden');
}
}
volumeSlider.addEventListener('input', function(e) {
var val = parseFloat(e.target.value);
updateVolume(val);
if (val > 0) {
lastVolume = val;
}
});
muteBtn.addEventListener('click', function() {
if (audio.volume > 0) {
lastVolume = audio.volume;
updateVolume(0);
} else {
updateVolume(lastVolume);
}
});
// Lógica del Canvas del Visualizador
var canvas = document.getElementById('visualizer-canvas');
var ctx = canvas.getContext('2d');
var barCount = 18;
var currentHeights = [];
for (var i = 0; i < barCount; i++) {
currentHeights.push(0);
}
function resizeCanvas() {
var rect = canvas.parentNode.getBoundingClientRect();
canvas.width = rect.width * window.devicePixelRatio;
canvas.height = rect.height * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// Dibujo de barras con esquinas redondeadas
function drawBar(ctx, x, y, w, h, r) {
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
ctx.fill();
}
function drawSpectrum() {
var w = canvas.width / window.devicePixelRatio;
var h = canvas.height / window.devicePixelRatio;
ctx.clearRect(0, 0, w, h);
var gap = 4;
var barWidth = (w - (gap * (barCount - 1))) / barCount;
var time = Date.now() * 0.0035;
var ampVolume = audio.volume;
if (audio.muted) ampVolume = 0;
for (var i = 0; i < barCount; i++) {
var targetHeight = 2; // estado base o inactivo
if (isPlaying && !isBuffering) {
// Algoritmo matemático para simular un ecualizador en vivo muy orgánico
var val = Math.sin(i * 0.35 + time * 1.8) * 0.32 +
Math.cos(i * 0.65 - time * 2.5) * 0.28 +
Math.sin(i * 1.12 + time * 4.2) * 0.18 +
Math.sin(time * 0.5 + i * 0.8) * 0.12 + 0.1;
// Variaciones aleatorias finas
val += (Math.random() - 0.5) * 0.14;
// Resaltar frecuencias medias
var centerFactor = 1 - Math.abs(i - (barCount / 2)) / (barCount / 2);
val += centerFactor * 0.15;
// Limitar valores
val = Math.max(0.06, Math.min(0.95, val));
var scale = 0.25 + 0.75 * ampVolume;
targetHeight = val * h * scale;
} else if (isBuffering) {
// Animación suave de onda de carga al conectar
targetHeight = 4 + Math.sin(time * 4 + i * 0.5) * 2.5;
} else {
// Movimiento de standby muy sutil al estar pausado
targetHeight = 2 + Math.sin(time * 1.5 + i * 0.3) * 0.5;
}
// Easing de interpolación para transiciones fluidas a 60 FPS
currentHeights[i] += (targetHeight - currentHeights[i]) * 0.2;
var barH = currentHeights[i];
var barX = i * (barWidth + gap);
var barY = h - barH;
var grad = ctx.createLinearGradient(0, h, 0, 0);
grad.addColorStop(0, '#1d4ed8'); // Azul Ushuaia
grad.addColorStop(0.55, '#93c5fd'); // Celeste
grad.addColorStop(1, '#c9a84c'); // Oro Ministerio
ctx.fillStyle = grad;
drawBar(ctx, barX, barY, barWidth, barH, 2.5);
}
requestAnimationFrame(drawSpectrum);
}
// ── INICIAR VISUALIZADOR Y UI ──
drawSpectrum();
updateVolume(lastVolume);
updateUI(); // mostrará CONECTANDO (isBuffering=true desde declaración)
// ── LÓGICA DE AUTOPLAY DEFINITIVA ──
//
// Flujo:
// 1. Intentamos reproducir con audio muteado (permitido por browsers).
// 2. Si funciona → el evento 'playing' desmutea al volumen del usuario.
// 3. Si el browser lo bloquea → NO reseteamos a PAUSADO.
// En cambio, quedamos en CONECTANDO y esperamos el PRIMER clic/toque
// en CUALQUIER parte de la página para iniciar el stream.
// 4. Una vez que el stream arranca (por cualquiera de las dos vías),
// desregistramos todos los listeners secundarios.
var streamArmed = false; // el stream fue iniciado (intentado)
var streamPlaying = false; // el stream realmente suena
// El evento 'playing' es la única fuente de verdad para estado EN VIVO
audio.addEventListener('playing', function () {
if (streamPlaying) return;
streamPlaying = true;
audio.muted = false;
audio.volume = lastVolume;
isPlaying = true;
isBuffering = false;
updateUI();
updateVolume(lastVolume);
});
function launchStream() {
if (streamArmed) return;
streamArmed = true;
audio.muted = true;
audio.autoplay = true;
audio.src = streamUrl;
audio.load();
// No usamos el .catch() para resetear estado —
// si falla, el listener de clic/toque lo manejará.
audio.play().catch(function () { /* bloqueado, esperando gesto */ });
}
// Función que se ejecuta ante el PRIMER gesto del usuario en la página
function onFirstGesture() {
document.removeEventListener('click', onFirstGesture, true);
document.removeEventListener('touchstart', onFirstGesture, true);
if (!streamPlaying) {
// El stream no arrancó aún: lo iniciamos ahora que hay gesto
audio.muted = true;
audio.src = streamUrl;
audio.load();
audio.play().catch(function () {
// Si incluso con gesto falla, dejamos el Play manual disponible
isBuffering = false;
isPlaying = false;
updateUI();
});
}
}
// Armar listener de primer gesto (capture=true para atrapar cualquier clic)
document.addEventListener('click', onFirstGesture, true);
document.addEventListener('touchstart', onFirstGesture, true);
// Intentar autoplay muted inmediatamente
launchStream();
})();