Ir al contenido
AppsSDAR

El sonido de aviso que dejó de sonar sin ningún error

4 min de lectura Frontend y UX

Tras recibir unos catorce mensajes seguidos en la bandeja, el reporte fue claro: el sonido de aviso había sonado una o dos veces, no catorce. No había errores en la consola, el código que lo disparaba se ejecutaba cada vez, y en las pruebas sueltas siempre sonaba. El problema solo aparecía con uso real y sostenido.

Por qué sintetizar el sonido

Para un aviso breve, la Web Audio API permite generar un tono con un oscilador y una envolvente de volumen en pocas líneas. No hay que descargar un archivo de audio, el sonido está disponible al instante y se puede ajustar su tono y duración desde el código. Es una buena elección para notificaciones.

La causa: un límite de contextos

Todo sonido de la Web Audio API vive dentro de un AudioContext. La implementación original creaba un contexto nuevo en cada aviso. Chrome limita la cantidad de contextos de audio por página a alrededor de seis. Los primeros avisos funcionan; a partir del límite, el constructor lanza una excepción.

Como la función de sonido estaba envuelta en un bloque que capturaba errores sin registrarlos, pensado para que un fallo de audio no afectara al resto de la app, la excepción desaparecía. El sonido dejaba de sonar y nada lo indicaba.

La corrección: un solo contexto

El contexto se crea una sola vez, a nivel de módulo, y se reutiliza para todos los avisos. Cada aviso crea su oscilador y su nodo de volumen, que son baratos y se descartan solos al terminar.

let audio = null; // uno solo para toda la página

function sonar() {
  const AC = window.AudioContext || window.webkitAudioContext;
  if (!audio) audio = new AC();
  if (audio.state === 'suspended') audio.resume();
  // oscilador + ganancia, start y stop
}

El detalle del estado suspendido

La llamada a resume() no es decorativa. Un contexto de audio puede quedar suspendido por dos razones. La primera es la política de reproducción automática: los navegadores no permiten producir sonido antes de que la persona interactúe con la página, y un contexto creado antes de esa interacción nace suspendido. La segunda es el segundo plano: al volver de otra pestaña, el contexto puede estar suspendido de nuevo. Reanudarlo antes de cada sonido cubre ambos casos.

En la práctica, conviene que el primer uso del audio ocurra después de algún clic de la persona en la app, por ejemplo al entrar a la bandeja, para que el contexto quede habilitado desde temprano.

Dos lecciones generales

Los recursos del navegador también se agotan

Se suele pensar en fugas de memoria, pero hay otros recursos con límites duros: contextos de audio, contextos WebGL, conexiones simultáneas por dominio, workers. Crear uno por evento en lugar de reutilizarlo funciona en la demo y falla con el uso real.

Capturar errores en silencio esconde bugs

Evitar que un fallo secundario rompa la app es correcto. Hacerlo sin dejar rastro convierte un error de una línea en una investigación. Aunque el fallo no deba mostrarse al usuario, debería registrarse al menos en la consola, o contarse, para que alguien pueda notar que ocurre.

Cómo probarlo

  • Disparar el sonido veinte veces seguidas y contar cuántas suenan.
  • Probar con la pestaña en segundo plano y al volver a ella.
  • Probar recargando la página sin hacer ningún clic antes del primer aviso.