web-dev-qa-db-fra.com

Pas de son sur l'API Web Audio iOS 6

J'étais vraiment enthousiaste à l'idée de voir iOS 6 prendre en charge l'API Web Audio, car nous créons des jeux HTML5. Cependant, je ne parviens pas à faire jouer le son à iOS 6 à l'aide de l'API Web Audio avec des exemples fonctionnant correctement sous Chrome.

Voici un jeu HTML5 avec commandes tactiles et lecture de l'audio via l'API Web Audio (le cas échéant - si ce n'est pas le cas, l'audio HTML5 est utilisé):

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

Edit: @Srikumar a suggéré quelques solutions de contournement. Je les ai appliquées à la version ci-dessous. Cela ne fonctionne toujours pas!

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

Tout fonctionne parfaitement sur le bureau Chrome, mais iOS 6 n’émet aucun son. Je ne parviens pas à le déboguer car je ne développe que Windows et iOS 6 a remplacé le mode débogage par l'inspecteur Web distant, qui n'est apparemment pas disponible sur Safari pour Windows. En utilisant quelques alertes, j'ai trouvé que cela identifiait correctement l'API Web Audio, l'utilisait, ne détectait aucune prise en charge de Vorbis, alors retombait en audio AAC, décodait une mémoire tampon, puis la lisait. Et bien sûr, j'ai essayé de monter le volume au maximum :)

Il ne devrait pas y avoir de problème de codec, car iOS 6 peut très bien jouer à AAC - vous pouvez naviguer jusqu'à l'un des jeux en cours de lecture du jeu. M4a } et il joue très bien, visité directement depuis Safari. 

En regardant les exemples de l'API Web Audio ici sur iOS 6: http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html } - certains fonctionnent, d'autres pas. . Par exemple, le Chrome Audio Visualizer fonctionne, mais Javascript Drone ne fonctionne pas.

Il doit exister une incompatibilité subtile entre Web Audio sur iOS 6 et le bureau Chrome. Qu'est-ce que je rate?

38
AshleysBrain

Edit (novembre 2015): iOS 9 n'autorise plus l'audio à démarrer dans un événement touchstart, ce qui casse la solution ci-dessous. Cependant, cela fonctionne dans un événement touchend. La réponse d'origine pour iOS 6 est laissée intacte ci-dessous, mais pour iOS 9, assurez-vous d'utiliser touchend.

Désolée de répondre à ma propre question sur les primes, mais après des heures de débogage, j'ai finalement trouvé la réponse. Safari sur iOS 6 démarre effectivement avec l’API Web Audio en sourdine. Il ne restera pas en sourdine tant que vous n'avez pas tenté de reproduire un son lors d'un événement d'entrée utilisateur (créez une source de mémoire tampon, connectez-la à la destination et appelez noteOn()). Après cela, le son est rétabli et la lecture audio est illimitée et comme il se doit. Il s'agit d'un aspect non documenté du fonctionnement de l'API Web Audio sur iOS 6 ( le document d'Apple est ici , espérons qu'ils le mettent à jour avec une mention de cela bientôt!)

L'utilisateur peut toucher beaucoup l'écran, être impliqué dans le jeu. Mais il restera en sourdine. Vous avez à jouer dans un événement d'entrée utilisateur tel que touchstart [edit: touchend pour iOS 9+], une fois, puis tout le son est désactivé. Après cela, vous pouvez lire de l'audio à tout moment (il n'est pas nécessaire que ce soit dans un événement d'entrée utilisateur).

Notez que ceci diffère des restrictions sur l'audio HTML5: en règle générale, vous ne pouvez démarrer l'audio que dans un événement d'entrée utilisateur et ne lire qu'un son à la fois; L'API Web Audio est complètement désactivée après la première entrée utilisateur, de sorte que vous puissiez jouer des sons à tout moment, puis les mélanger de manière polyphonique, traiter des effets sympas, etc.

Cela signifie que de nombreux jeux déjà sur le Web utilisant l'API Web Audio ne liront jamais l'audio, car ils ne peuvent pas émettre de noteOn lors d'un événement tactile. Vous devez l’ajuster pour attendre le premier événement de saisie utilisateur.

Il existe plusieurs façons de contourner ce problème: ne jouez pas votre musique de titre tant que l'utilisateur n'a pas touché l'écran; avoir un écran tactile initial pour activer l'audio et jouer un son puis commencer le jeu quand ils touchent; etc. J'espère que cela aidera tous ceux qui ont le même problème à gagner du temps à essayer de le déboguer!

48
AshleysBrain

J'ai réussi à trouver une solution simple qui, j'en suis sûr, doit avoir été documentée ailleurs - mais nous devons parfois passer des heures à résoudre ces problèmes par nous-mêmes ...

Il semble donc que de nombreux tutoriels (comme celui-ci sur html5rocks ) vous demandent de suivre les étapes suivantes:

  • Créez une instance de window.AudioContext et si elle n’existe pas (ce qui n’existe pas sous iOS), créez window.webkitAudioContext.

  • Créez une XMLHttpRequest pour charger votre fichier son

  • Sur l'événement load, exécutez context.decodeAudioData(....), puis createBufferSource(), en le remplissant avec les données décodées, et enfin source.start(0) pour lire le son.

Comme d'autres l'ont fait remarquer, vous devez créer la AudioContext (que vous devrez d'ailleurs stocker et utiliser pour la durée de vie de la page) à la suite d'une interaction de l'utilisateur (clic ou touchstart).

CEPENDANT: Pour que iOS «déverrouille» ses fonctionnalités audio, VOUS DEVEZ disposer de données audio lorsque vous créez la AudioContext. Si vous chargez les données de manière asynchrone, rien ne se lit. Il ne suffit pas de créer simplement la AudioContext dans un événement click.

Voici deux solutions pour une lecture iOS fiable:

  • 1) Vous devez charger au moins un fichier audio avant même d’initialiser AudioContext, puis exécuter toutes les étapes ci-dessus pour ce fichier audio immédiatement dans le cadre d’une interaction utilisateur unique (par exemple, un clic).

  • OU 2) Créez un son dynamiquement en mémoire et jouez-le.

Voici comment j'ai fait cette deuxième option:

N'OUBLIEZ PAS - DOIT ÊTRE DANS L'événement click/touch pour 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

J'imagine que c'est une erreur commune - et je suis surpris, après 3 ans, que personne ne semble l'avoir signalé ou découvert: - /

5
Simon_Weaver

Vous pouvez essayer de le déboguer en utilisant Web Inspector sur Safari 6 sur un mac. 

  1. Activez "Webkit Inspector" dans Mobile Safari settings/advanced.
  2. Connectez le périphérique à un Mac exécutant Safari 6 à l'aide d'un câble USB.
  3. Chargez votre page/jeu
  4. Allez dans le menu Développer -> [nom de périphérique] -> [page]

Cela ne fonctionne pas pour moi, mais en quelques essais, cela peut aider à réduire le problème.

Apparemment, le son ne peut être déclenché que par une action de l'utilisateur. Je ne suis pas sûr que cela soit vrai car un code qui fonctionne sur iOS6 sur iPhone4 ne reproduit aucun son sur un iPad (également iOS6).

Mise à jour : Quelques succès avec l'audio Web sur iPhone4 + iOS6. Constaté que le "currentTime" reste bloqué à 0 pendant un certain temps dès que vous créez un nouveau contexte audio sur iOS6. Pour le faire bouger, vous devez d'abord effectuer un appel d'API factice (tel que createGainNode() et ignorer le résultat). Les sons ne sont lus que lorsque currentTime commence à s'exécuter, mais la planification des sons sonores exactement à currentTime ne semble pas fonctionner. Ils ont besoin d’être un peu dans l’avenir (ex: 10ms). Vous pouvez utiliser la fonction createAudioContext suivante pour attendre que le contexte soit prêt à générer du bruit. L'action de l'utilisateur ne semble pas être nécessaire sur iPhone, mais pas encore pour le moment sur 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();
}

Par la suite, lorsque vous jouez une note, n'appelez pas .noteOn(ac.currentTime), mais faites plutôt .noteOn(ac.currentTime + 0.01).

S'il vous plaît ne me demandez pas pourquoi vous devez faire tout cela. C’est comme ça en ce moment - c’est fou.

5
Srikumar

Donc, je pense que je l'ai compris. 

C'est un problème d'Apple nécessitant une action de l'utilisateur avant que le son puisse être autorisé à jouer. Au moins pour moi, il s’avère que vous ne devez pas du tout créer le contexte audio, sauf lorsque l’utilisateur l’appelle. Il ne suffit pas de créer le contexte lors du chargement de la page, puis d'utiliser createGainNode ou similaire pour une action de l'utilisateur. 

Dans votre cas, je créerais le contexte lorsque l'utilisateur clique sur le bouton "Toucher pour commencer".

3
Oskar Eriksson

J'ai rencontré les restrictions audio avec HTML5 Audio sur iOS et j'ai contourné le problème en:

1) Créer un élément audio avec un fichier audio silencieux et le lire initialement avec un événement tactile (par exemple, le bouton «commencer le jeu»), puis le suspendre immédiatement.

2) Construire une fonction de sélecteur de son qui commute la source audio puis lit l'élément audio après un court délai.

3) Appel de la fonction de sélecteur de son pour tous les événements (ne doit pas nécessairement être un événement tactile).

Cela fonctionne car l'élément audio n'est pas mis en sourdine au premier contact, avec le fichier audio silencieux, et reste non-mis en sourdine, de sorte que la source puisse être activée à la volée.

switchSound: (id) ->
        @soundSwitch.pause()
        @soundSwitch.src = @sounds[id]._src

        clearTimeout @switchSoundDelay
        @switchSoundDelay = setTimeout =>
            # @soundSwitch.volume = 1
            @soundSwitch.play()
        ,50 
1
Jack Wild

Mise à jour: iOS nécessite toujours une entrée de l'utilisateur pour lire le son ( Pas de son sous iOS 6 Web Audio API )

J'étais auparavant coincé avec l'audio Web sur le Web iOS. Et pour aggraver les choses, il doit fonctionner sur Android et une autre plate-forme de bureau. Cet article fait partie de ceux que j'ai lus et pour lesquels je n'ai trouvé aucune réponse immédiate.

Jusqu'à ce que j'ai trouvé howler.js

Voici la solution pour la solution audio Web multiplateforme: 

<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>
1
ytbryan

Répondant à la question originale , je peux confirmer certains problèmes liés aux formats de fichier sur iPhone 4S/iOS 6 et MacOSX. Si un fichier MP3 n'est "pas bon" pour Safari, le décodage va mal et appeler AudioContext.createBuffer (array, bool) vous donnera une erreur.

La chose étrange est à propos de l'erreur: "SYNTAX_ERR, DOM Exception 12", comme l'ont souligné d'autres. Cela me fait penser que c'est un bug ....

Même comportement également sur MacOS, avec Safari 6.0 (7536.25).

1
Enrico MRK

mis à jour pour 2015 solution: Salut à tous, si vous travaillez ici sur un problème d'audio Web avec ios6 +, j'ai trouvé ces liens comme aide.

Ceci est un bon article avec solution de code: http://matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/

-Il y a une mise à jour de l'API après la rédaction de l'article ^ de solution ci-dessus https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext

-below est ma solution mise à jour pour le premier article, en utilisant les modifications du deuxième article. Le problème que je rencontrais était le safari iOS 7 en lançant une étrange erreur pas assez d'arguments. cela l'a corrigé:

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;
});
1
mat

J'ai du mal à utiliser toutes les solutions simples. Surtout quand je veux jouer un son plusieurs fois.

J'utilise donc cette bibliothèque js: http://pupunzi.open-lab.com/2013/03/13/making-html5-audio-actually-work-on-mobile

0
Matthias M

Ce n'est pas une réponse réelle, mais une direction à regarder si les choses ne fonctionnent toujours pas. iOS6 a des problèmes audio sur certains périphériques (en particulier les 64 Go conçus au cours d’une période donnée, bien que j’en ai vu d’autres, de sorte qu’il n’est peut-être pas lié au matériel) et arrêtera mystérieusement de jouer certains types de sons (pas de sonneries voix, pour une raison quelconque, mais de nombreux autres sons), et ses curseurs de volume disparaîtront. Je trouve qu'il est notoirement difficile de déboguer car cela ne se produira généralement (ne pensant pas toujours, parfois vous pouvez l'obtenir) uniquement lorsqu'il n'est pas connecté à un cordon d'alimentation.

Recherchez dans la console les messages ASSERTION FAILURE provenant de VirtualAudio_Device et avec divers codecs. Cela n'a peut-être rien à voir avec votre problème particulier, mais là encore, un bogue dans une zone du périphérique audio peut être lié à un autre. Au minimum, c'est un domaine dans lequel enquêter si rien d'autre ne l'aide.

0
Mason Cloud

L'API semble endommagée sur iOS 6.1 ou, du moins, a subi une modification importante, ce qui signifie qu'aucun site ne la gère actuellement.

0
Mark

Ok, j'aime bien AshleysBrain, cela m'a aidé à résoudre le problème. Mais j'ai trouvé une solution un peu plus générale.

Avant de commencer à jouer le son d'un événement utilisateur, ils vous obligent maintenant à le faire via un événement d'entrée utilisateur (cela semble étrange). Ce que j'ai fait était de lire un champ d'entrée avant de jouer le son.

Alors

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

playSoundFile est ce que vous utilisez pour créer la source tampon.

0
bobbdelsol