javascript html5 mobile-safari ios6 web-audio

javascript - Sin sonido en iOS 6 Web Audio API



html5 mobile-safari (12)

Estaba realmente emocionado de ver que iOS 6 es compatible con Web Audio API, ya que hacemos juegos HTML5. Sin embargo, no puedo hacer que iOS 6 reproduzca ningún sonido con la API de Web Audio con ejemplos que funcionan bien en Chrome para computadoras de escritorio.

Aquí hay un juego HTML5 con controles táctiles y reproducción de audio a través de la API de Web Audio (si está presente; si no, recurrirá al audio HTML5):

http://www.scirra.com/labs/sbios6b/

Editar: @Srikumar sugirió algunas soluciones. Los apliqué en la versión de abajo. ¡Todavía no funciona!

http://www.scirra.com/labs/sbios6f/

Todo funciona bien en el escritorio de Chrome, pero iOS 6 no emite ningún sonido. Tengo problemas para depurarlo porque solo desarrollo Windows, y iOS 6 reemplazó el modo de depuración con el inspector web remoto, que aparentemente no está disponible en Safari para Windows. Usando algunas alertas encontré que identifica correctamente la API de Web Audio, la usa, no detecta compatibilidad con Vorbis, por lo que regresa al audio de AAC, decodifica un búfer y luego lo reproduce, y no se producen errores, pero no escucho nada. Y, por supuesto, intenté subir el volumen al máximo :)

No debería haber un problema con el códec, porque iOS 6 puede jugar AAC correctamente. Puedes navegar a uno de los juegos de .m4a y jugar directamente desde Safari.

Mirando los ejemplos de Web Audio API aquí en iOS 6: http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html - algunos funcionan y otros no. Por ejemplo, Chrome Audio Visualizer funciona, pero Javascript Drone no funciona.

Debe haber alguna incompatibilidad sutil entre Web Audio en iOS 6 y Chrome de escritorio. ¿Qué me estoy perdiendo?


Actualización: iOS aún requiere una entrada de usuario para reproducir el sonido ( Sin sonido en iOS 6 Web Audio API )

Antes estaba atrapado con audio web en iOS web. Y para empeorar las cosas, necesita funcionar en Android y otras plataformas de escritorio. Esta publicación es una de esas publicaciones que leí y no encontré respuestas inmediatas.

Hasta que encontré howler.js .

Esta es la solución para la solución de audio web multiplataforma:

<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.3/howler.min.js"></script> <script> var sound = new Howl({ src: [''yay3.mp3''] }); sound.play(); </script>


De acuerdo, me gusta la respuesta de AshleysBrain, me ayudó a resolver el problema. Pero encontré una solución un poco más general.

Antes de que tuvieras que iniciar el sonido de reproducción de un evento de usuario, ahora te obligan a hacerlo a través de un evento de entrada de usuario (suena extraño) Lo que hice fue leer un campo de entrada antes de reproducir el sonido.

Asi que

$(''#start-lesson'').click(function() { return startThisLesson(); }); startThisLesson = function() { var value; value = $(''#key-pad-value'')[0].value; playSoundFile(yourBuffer); }

playSoundFile es lo que usas para crear la fuente de buffer.


Entonces, creo que lo he descubierto.

Es un problema de Apple que requiere una acción del usuario antes de poder reproducir el sonido. Resulta, al menos para mí, que no debes crear el contexto de audio, excepto cuando el usuario lo solicite. No es suficiente crear el contexto cuando la página se carga y luego usar createGainNode o similar en una acción del usuario.

En su caso, crearía el contexto cuando el usuario haga clic en el botón "Tocar para comenzar".


Esta no es una respuesta real, solo una dirección para ver si las cosas aún no funcionan. iOS6 tiene problemas de audio en algunos dispositivos (especialmente los 64 gb 4s fabricados durante un período determinado, aunque he visto otros por lo que puede no estar relacionado con el hardware) y misteriosamente dejará de reproducir algunos tipos de sonidos (no tonos o voz, para algunos razón, pero muchos otros sonidos), y sus controles deslizantes de volumen desaparecerán. He encontrado que es notoriamente difícil de depurar, ya que normalmente (aunque no siempre, a veces puedes verlo) solo ocurre cuando no está conectado con un cable de alimentación.

Mire en la consola los mensajes de FALLO DE ASERCIÓN desde el dispositivo VirtualAudio y con varios códecs. Esto puede no tener nada que ver con su problema en particular, pero una vez más, un error en un área del dispositivo de sonido puede estar relacionado con otro. Como mínimo, es un área para investigar si nada más está ayudando.


La API parece estar rota en iOS 6.1, o al menos, tiene un cambio radical que significa que actualmente no hay sitios que funcionen con ella.


Me encontré con las restricciones de audio con HTML5 Audio en iOS y resolví el problema de la siguiente manera:

1) Crear un elemento de audio con un archivo de audio silencioso y reproducirlo inicialmente con un evento táctil (por ejemplo, el botón ''comenzar juego'') y luego pausarlo inmeditamente.

2) Construir una función de conmutación de sonido que cambie el sonido de audio y luego reproduzca el elemento de audio después de un corto tiempo de espera.

3) Llamar a la función de cambio de sonido en cualquier evento (no necesita ser un evento táctil).

Esto funciona porque el elemento de audio no se silencia en el primer toque, con el archivo de audio silencioso, y permanece sin silenciar, por lo que la fuente se puede encender sobre la marcha.

switchSound: (id) -> @soundSwitch.pause() @soundSwitch.src = @sounds[id]._src clearTimeout @switchSoundDelay @switchSoundDelay = setTimeout => # @soundSwitch.volume = 1 @soundSwitch.play() ,50


Me las arreglé para encontrar una solución simple que estoy seguro debe haber sido documentada en otra parte, pero a veces tenemos que pasar horas averiguando estas cosas por nosotros mismos ...

Por lo tanto, parece que muchos tutoriales (como este en html5rocks ) le indican que haga los siguientes pasos:

  • Cree una instancia de window.AudioContext y, si eso no existe (que no funciona en iOS), cree window.webkitAudioContext .

  • Crea un XMLHttpRequest para cargar tu archivo de sonido

  • En el evento de load , ejecute context.decodeAudioData(....) y luego createBufferSource() , llenándolo con los datos decodificados, y finalmente source.start(0) para reproducir el sonido.

Como han señalado otros, debe crear AudioContext (que, por cierto, debe almacenar y usar durante toda la vida de la página) como resultado de la interacción del usuario (haga clic o toque).

SIN EMBARGO: Para iOS para ''desbloquear'' sus capacidades de audio DEBE tener datos de audio disponibles cuando crea el AudioContext . Si carga los datos de manera asincrónica, no hay nada para que se reproduzca. No es suficiente simplemente crear el AudioContext dentro de un evento de click .

Aquí hay dos soluciones para la reproducción confiable de iOS:

  • 1) Debe cargar al menos un archivo de sonido incluso antes de inicializar el AudioContext, y luego ejecutar todos los pasos anteriores para ese archivo de sonido inmediatamente dentro de una única interacción del usuario (por ejemplo, hacer clic).

  • O 2) Cree un sonido dinámicamente en la memoria y reprodúzcalo.

Así es como hice esa segunda opción:

RECUERDE: DEBE ESTAR dentro del evento de click / touch para iOS:

window.AudioContext = window.AudioContext || window.webkitAudioContext; var context = new window.AudioContext(); // you should null check window.AudioContext for old browsers to not blow up // create a dummy sound - and play it immediately in same ''thread'' var oscillator = context.createOscillator(); oscillator.frequency.value = 400; oscillator.connect(context.destination); oscillator.start(0); oscillator.stop(.5); // you can set this to zero, but I left it here for testing. // audio context is now ''unlocked'' and ready to load and play sounds asynchronously // YOU MUST STORE ''context'' for future usage. DON''T recreate more AudioContexts

Me imagino que este es un error común, y me sorprende después de 3 años que nadie parece haberlo señalado o descubierto: - /


Puede intentar depurarlo utilizando Web Inspector en Safari 6 en un mac.

  1. Habilite "Webkit Inspector" en Mobile Safari settings / advanced.
  2. Conecte el dispositivo a una Mac con Safari 6 usando un cable USB.
  3. Cargue su página / juego
  4. Ir al menú Desarrollar -> [nombre de dispositivo] -> [pageurl]

No funciona de la caja para mí, pero con algunos intentos puede ayudar a reducir el problema.

Aparentemente, también existe la posibilidad de que el audio solo pueda ser activado por una acción del usuario. No estoy seguro de que esto sea cierto porque algunos códigos que funcionan en iOS6 en iPhone4 no reproducen ningún sonido en un iPad (también iOS6).

Actualización : cierto éxito con audio web en iPhone4 + iOS6. Descubrió que el "tiempo actual" permanece bloqueado en 0 por un tiempo tan pronto como crea un nuevo contexto de audio en iOS6. Para que se mueva, primero debe realizar una llamada ficticia a API (como createGainNode() y descartar el resultado). Los sonidos se reproducen solo cuando CurrentTime comienza a ejecutarse, pero la programación suena exactamente en el momento actual. Parece que no funciona. Necesitan estar un poco en el futuro (ej .: 10ms). Puede usar la siguiente función createAudioContext para esperar hasta que el contexto esté listo para hacer ruido. La acción del usuario no parece ser necesaria en el iPhone, pero aún no ha tenido tanto éxito en el iPad.

function createAudioContext(callback, errback) { var ac = new webkitAudioContext(); ac.createGainNode(); // .. and discard it. This gets // the clock running at some point. var count = 0; function wait() { if (ac.currentTime === 0) { // Not ready yet. ++count; if (count > 600) { errback(''timeout''); } else { setTimeout(wait, 100); } } else { // Ready. Pass on the valid audio context. callback(ac); } } wait(); }

Posteriormente, al reproducir una nota, no llame a .noteOn(ac.currentTime) , sino que haga .noteOn(ac.currentTime + 0.01) lugar.

Por favor, no me preguntes por qué tienes que hacer todo eso. Así es en este momento, es decir, loco.


Respondiendo a la pregunta original , puedo confirmar algunos problemas con los formatos de archivo en iPhone 4S / iOS 6 y MacOSX. Si un archivo MP3 "no es bueno" para Safari, la decodificación falla y la invocación de AudioContext.createBuffer (array, bool) le da un error.

Lo extraño es sobre el error: "SYNTAX_ERR, DOM Exception 12", como lo señalaron otros. Esto me hace pensar que es un error ...

Mismo comportamiento también en MacOS, con Safari 6.0 (7536.25).



Edición (noviembre de 2015): iOS 9 ya no permite que el audio se inicie en un evento de inicio touchstart , lo que rompe la solución a continuación. Sin embargo, funciona en un evento touchend . La respuesta original para iOS 6 se deja intacta a continuación, pero para iOS 9, asegúrese de usar touchend .

Bueno, lamento responder mi propia pregunta sobre recompensas, pero después de horas de depurar finalmente encontré la respuesta. Safari en iOS 6 comienza de manera efectiva con la API de Web Audio silenciada. No se activará hasta que intente reproducir un sonido en un evento de entrada de usuario (cree una fuente de búfer, conéctela al destino y llame a noteOn() ). Después de esto, se activa y el audio se reproduce sin restricciones y como debería. Este es un aspecto no documentado de cómo funciona la API de Web Audio en iOS 6 ( el documento de Apple está aquí , ¡espero que lo actualicen con una mención de esto pronto!)

El usuario puede tocar la pantalla mucho, participando en el juego. Pero permanecerá silenciado. Tienes que jugar dentro de un evento de entrada de usuario como touchstart [editar: touchend para iOS 9+], una vez, luego todo el audio se activa. Después de eso, puede reproducir audio en cualquier momento (no tiene que estar en un evento de entrada de usuario).

Tenga en cuenta que esto es diferente a las restricciones de audio HTML5: por lo general, solo puede iniciar el audio en un evento de entrada de usuario y solo reproducir un sonido a la vez; Web Audio API se activa completamente después de la primera entrada de play-in-user, para que pueda reproducir sonidos en cualquier momento, y luego puede mezclarlos polifónicamente, procesar efectos geniales, etc.

Esto significa que muchos juegos que ya están en la web y que usan la API de Web Audio nunca reproducirán audio, ya que no emiten una nota en un evento táctil. Tienes que ajustarlo para esperar el primer evento de entrada del usuario.

Hay algunas formas de evitar esto: no reproduzca su música de título hasta que el usuario toque la pantalla; tener una pantalla inicial de "toque para habilitar el audio" y reproducir un sonido, luego comenzar el juego cuando toquen; etc. ¡Espero que esto ayude a cualquier otra persona que tenga el mismo problema, ahorre algo de tiempo intentando depurarlo!


actualizado para la solución de 2015: oye todo, si estás trabajando en un problema de audio web con iOS6, he encontrado estos enlaces como ayuda.

-este es un buen artículo con solución de código: http://matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/

-hay una actualización de la API después de que se escribió el artículo de la solución anterior https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext

-bajo está mi solución actualizada al primer artículo, usando los cambios del segundo artículo. El problema que estaba teniendo era iOS 7 safari arrojando un error extraño no suficiente. esto lo solucionó:

define(function() { try { window.AudioContext = window.AudioContext || window.webkitAudioContext; window.audioContext = new window.AudioContext(); } catch (e) { console.log("No Web Audio API support"); } /* * WebAudioAPISoundManager Constructor */ var WebAudioAPISoundManager = function (context) { this.context = context; this.bufferList = {}; this.playingSounds = {}; }; /* * WebAudioAPISoundManager Prototype */ WebAudioAPISoundManager.prototype = { addSound: function (url) { // Load buffer asynchronously var request = new XMLHttpRequest(); request.open("GET", url, true); request.responseType = "arraybuffer"; var self = this; request.onload = function () { // Asynchronously decode the audio file data in request.response self.context.decodeAudioData( request.response, function (buffer) { if (!buffer) { alert(''error decoding file data: '' + url); return; } self.bufferList[url] = buffer; }); }; request.onerror = function () { alert(''BufferLoader: XHR error''); }; request.send(); }, stopSoundWithUrl: function(url) { if(this.playingSounds.hasOwnProperty(url)){ for(var i in this.playingSounds[url]){ if(this.playingSounds[url].hasOwnProperty(i)) { this.playingSounds[url][i].stop(0); } } } } }; /* * WebAudioAPISound Constructor */ var WebAudioAPISound = function (url, options) { this.settings = { loop: false }; for(var i in options){ if(options.hasOwnProperty(i)) { this.settings[i] = options[i]; } } this.url = ''/src/www/assets/audio/'' + url + ''.mp3''; this.volume = 1; window.webAudioAPISoundManager = window.webAudioAPISoundManager || new WebAudioAPISoundManager(window.audioContext); this.manager = window.webAudioAPISoundManager; this.manager.addSound(this.url); // this.buffer = this.manager.bufferList[this.url]; }; /* * WebAudioAPISound Prototype */ WebAudioAPISound.prototype = { play: function () { var buffer = this.manager.bufferList[this.url]; //Only play if it''s loaded yet if (typeof buffer !== "undefined") { var source = this.makeSource(buffer); source.loop = this.settings.loop; source.start(0); if(!this.manager.playingSounds.hasOwnProperty(this.url)) { this.manager.playingSounds[this.url] = []; } this.manager.playingSounds[this.url].push(source); } }, stop: function () { this.manager.stopSoundWithUrl(this.url); }, getVolume: function () { return this.translateVolume(this.volume, true); }, //Expect to receive in range 0-100 setVolume: function (volume) { this.volume = this.translateVolume(volume); }, translateVolume: function(volume, inverse){ return inverse ? volume * 100 : volume / 100; }, makeSource: function (buffer) { var source = this.manager.context.createBufferSource(); var gainNode = this.manager.context.createGain(); source.connect(gainNode); gainNode.gain.value = this.volume; source.buffer = buffer; // source.connect(gainNode); gainNode.connect(this.manager.context.destination); return source; } }; return WebAudioAPISound; });