Нет звука в iOS 6 API веб-аудио

Я был очень рад видеть, что iOS 6 поддерживает API-интерфейс Web Audio, так как мы создаем игры на HTML5. Тем не менее, я не могу заставить iOS 6 воспроизводить звук вообще, используя API-интерфейс Web Audio с примерами, которые отлично работают в настольном Chrome.

Вот игра HTML5 с сенсорным управлением и воспроизведением звука через API Web Audio (если он есть - если нет, то он вернется к аудио HTML5):

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

Изменить: @Srikumar предложил некоторые обходные пути. Я применил их в версии ниже. Это все еще не работает!

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

Все отлично воспроизводится на настольном Chrome, но iOS 6 вообще не издает звука. У меня проблемы с отладкой, потому что я занимаюсь только разработкой для Windows, а iOS 6 заменил режим отладки на удаленный веб-инспектор, который, по-видимому, недоступен в Safari для Windows. Используя несколько предупреждений, я обнаружил, что он правильно идентифицирует API-интерфейс Web Audio, использует его, обнаруживает отсутствие поддержки Vorbis, поэтому возвращается к аудио AAC, декодирует буфер и затем воспроизводит его, и никаких ошибок не выдается, но я ничего не слышу. И, конечно же, я попытался увеличить громкость до максимума:)

Не должно быть проблем с кодеками, потому что iOS 6 может играть в AAC очень хорошо - вы можете просмотреть одну из игр.m4a, в которую она играет, и она играет отлично, если вы играете прямо из Safari.

Посмотрите примеры API Web Audio здесь на iOS 6: http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html - некоторые из них работают, а другие нет. Например, Chrome Audio Visualizer работает, а Javascript Drone - нет.

Должна быть некоторая тонкая несовместимость между Web Audio на iOS 6 и настольным Chrome. Что мне не хватает?

12 ответов

Решение

Изменить (ноябрь 2015 г.): iOS 9 больше не позволяет запускать звук в touchstart событие, которое нарушает решение ниже. Однако это работает в touchend событие. Оригинальный ответ для iOS 6 оставлен без изменений ниже, но для поддержки iOS 9 убедитесь, что вы используете touchend,

Ну, извините, что отвечаю на мой собственный вопрос о вознаграждении, но после нескольких часов отладки я наконец нашел ответ. Safari на iOS 6 эффективно запускается с отключенным API Web Audio. Он не включит звук, пока вы не попытаетесь воспроизвести звук в событии пользовательского ввода (создайте источник буфера, подключите его к месту назначения и вызовите noteOn()). После этого включается звук, и звук воспроизводится неограниченно и так, как должно. Это недокументированный аспект того, как API Web Audio работает на iOS 6 ( здесь документ от Apple, надеюсь, они обновят его с упоминанием об этом в ближайшее время!)

Пользователь может много касаться экрана, занимаясь игрой. Но это останется приглушенным. Вы должны играть внутри события пользовательского ввода, как touchstart [редактировать: touchend для iOS 9+], один раз, затем все звук включается. После этого вы можете воспроизводить звук в любое время (не обязательно входить в событие ввода пользователя).

Обратите внимание, что это отличается от ограничений на звук HTML5: обычно вы можете только запустить звук в событии пользовательского ввода и воспроизводить только один звук за раз; Web Audio API полностью отключает звук после первого ввода play-in-user, так что вы можете воспроизводить звуки в любое время, а затем смешивать их полифонически, обрабатывать классные эффекты и т. д.

Это означает, что многие игры, уже находящиеся в сети и использующие API-интерфейс Web Audio, никогда не будут воспроизводить звук, поскольку они не создают заметку в событии касания. Вы должны настроить его так, чтобы ждать первого события пользовательского ввода.

Есть несколько способов обойти это: не проигрывайте свою заглавную музыку, пока пользователь не коснется экрана; иметь начальный экран "touch to enable audio" и воспроизводить звук, а затем начинать игру, когда они касаются; и т.д. Надеюсь, что это поможет кому-то еще с той же проблемой сэкономить время, пытаясь отладить его!

Вы можете попробовать отладить его с помощью веб-инспектора в Safari 6 на Mac.

  1. Включите "Инспектор Webkit" в настройках Mobile Safari / Дополнительно.
  2. Подключите устройство к Mac с Safari 6 с помощью USB-кабеля.
  3. Загрузите свою страницу / игру
  4. Зайдите в меню Develop->[devicename]->[pageurl]

Это не работает для меня из коробки, но с несколькими попытками это может помочь сузить проблему.

Очевидно, есть и то, что аудио может быть запущено только действием пользователя. Я не уверен, что это правда, потому что какой-то код, который работает на iOS6 на iPhone4, не воспроизводит звук на iPad (также iOS6).

Обновление: Некоторый успех с веб-аудио на iPhone4+iOS6. Обнаружено, что "currentTime" некоторое время застревает на 0, как только вы создаете новый аудио-контекст на iOS6. Для того, чтобы заставить его двигаться, сначала нужно выполнить фиктивный вызов API (например, createGainNode() и откажитесь от результата). Звуки воспроизводятся только тогда, когда currentTime начинает работать, но планирование звуков точно в currentTime, похоже, не работает. Они должны быть немного в будущее (например: 10 мс). Вы можете использовать следующее createAudioContext функция ожидания, пока контекст не будет готов шуметь. Действия пользователя не требуются для iPhone, но пока нет такого успеха на 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();
}

Впоследствии, когда играете ноту, не звоните .noteOn(ac.currentTime), но сделать .noteOn(ac.currentTime + 0.01) вместо.

Пожалуйста, не спрашивайте меня, почему вы должны делать все это. Это так, как сейчас - то есть безумие.

Мне удалось найти простое решение, которое, я уверен, должно быть задокументировано в другом месте, но иногда нам приходится часами разбираться с этими вещами для себя...

Таким образом, кажется, что многие учебные пособия (такие как этот на html5rocks) инструктируют вас сделать следующие шаги:

  • Создать экземпляр window.AudioContext и если этого не существует (чего нет на iOS), то создайте window.webkitAudioContext,

  • Создать XMLHttpRequest загрузить ваш звуковой файл

  • На load запуск события context.decodeAudioData(....) а потом createBufferSource(), заполняя его декодированными данными, и, наконец, source.start(0) играть звук.

Как уже отмечали другие, вы должны создать AudioContext (который, между прочим, вы должны хранить и использовать на протяжении всей жизни страницы) в результате взаимодействия с пользователем (щелчок или запуск прикосновением).

ОДНАКО: для того, чтобы iOS "разблокировала" свои аудио возможности, вы ДОЛЖНЫ иметь аудио данные при создании AudioContext, Если вы загружаете данные асинхронно, проигрывать нечего. Недостаточно просто создать AudioContext внутри click событие.

Вот два решения для надежного воспроизведения iOS:

  • 1) Вы должны загрузить хотя бы один звуковой файл, прежде чем даже инициализировать AudioContext, а затем сразу выполнить все вышеуказанные шаги для этого звукового файла в рамках одного взаимодействия с пользователем (например, щелчком мыши).

  • ИЛИ 2) Создайте звук динамически в памяти и воспроизведите его.

Вот как я сделал этот второй вариант:

ПОМНИТЕ - ДОЛЖЕН БЫТЬ в пределах click / touch событие для 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

Я думаю, что это распространенная ошибка - и я удивлен через 3 года, что никто, кажется, не указал на это или обнаружил это:-/

Так что, думаю, я понял это.

Это проблема Apple, требующая действий пользователя, прежде чем можно будет разрешить воспроизведение звука. Оказывается, по крайней мере для меня, что вы не должны создавать аудио-контекст вообще, за исключением случаев, когда его вызывает пользователь. Недостаточно создать контекст при загрузке страницы, а затем использовать createGainNode или подобное для действий пользователя.

В вашем случае я бы создал контекст, когда пользователь нажимает кнопку "Нажмите, чтобы начать".

обновление для решения 2015: привет всем, если вы здесь работаете над проблемой веб-аудио с ios6+, я нашел эти ссылки в качестве справки.

Это хорошая статья с решением для кода: http://matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/

- есть обновление API после того, как вышеприведенная статья о решении была написана https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext

-приведено мое обновленное решение для первой статьи с использованием изменений из второй статьи. Проблема, с которой я столкнулся, заключалась в том, что Safari на iOS 7 выдавал странную ошибку "not-too-args". это исправило это:

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

Обновление: iOS все еще требует пользовательского ввода для воспроизведения звука ( Нет звука в iOS 6 API Web Audio)

Ранее я застрял с веб-аудио в сети iOS. И что еще хуже, он должен работать на Android и других настольных платформ. Этот пост - один из тех постов, которые я прочитал и не нашел немедленных ответов.

Пока я не нашел howler.js.

Это решение для кроссплатформенного веб-аудио решения:

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

Отвечая на оригинальный вопрос, я могу подтвердить некоторые проблемы с форматами файлов на iPhone 4S/iOS 6 и MacOSX. Если файл MP3 "не годится" для Safari, декодирование идет плохо, и при вызове AudioContext.createBuffer(array, bool) выдается ошибка.

Странная вещь связана с ошибкой: "SYNTAX_ERR, DOM Exception 12", на что указывают другие. Это заставляет меня думать, что это ошибка....

То же поведение также в MacOS, с Safari 6.0 (7536.25).

Я столкнулся с аудио-ограничениями в HTML5 Audio на iOS и обошел проблему:

1) Создание аудиоэлемента с беззвучным аудиофайлом и его воспроизведение вначале с помощью сенсорного события (например, кнопка "начать игру"), а затем без остановки его приостановка.

2) Создание функции переключения звука, которая переключает Audio Src, а затем воспроизводит элемент Audio после короткого времени ожидания.

3) Вызов функции переключения звука при любых событиях (не должно быть сенсорным событием).

Это работает, потому что аудиоэлемент при первом касании включается без звука, а звуковой файл без звука остается неизменным, поэтому источник можно переключать на лету.

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

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

Похоже, API-интерфейс поврежден в iOS 6.1 или, по крайней мере, имеет серьезное изменение, означающее, что ни один сайт в настоящее время не работает с ним.

Это не фактический ответ, просто направление, чтобы посмотреть, если вещи все еще не работают. iOS6 имеет проблемы со звуком на некоторых устройствах (особенно 64-гигабайтных 4-х, выпущенных в течение определенного периода, хотя я видел другие, так что на самом деле это может быть не связано с аппаратным обеспечением) и таинственным образом прекратит воспроизводить некоторые виды звуков (не рингтоны или голос, для некоторых причина, но много других звуков), и это ползунки громкости исчезнут. Я обнаружил, что его трудно отладить, так как обычно (хотя и не всегда, иногда вы можете его поймать) происходит только тогда, когда он не подключен через шнур питания.

Найдите в консоли сообщения ASSERTION FAILURE от VirtualAudio_Device и с различными кодеками. Это может не иметь никакого отношения к вашей конкретной проблеме, но опять же, ошибка в одной области звукового устройства может быть связана с другой. Как минимум, это область для расследования, если больше ничего не помогает.

У меня проблемы с использованием всех простых решений. Особенно, когда я хочу воспроизвести звук несколько раз.

Поэтому я использую эту библиотеку js: http://pupunzi.open-lab.com/2013/03/13/making-html5-audio-actually-work-on-mobile

Хорошо, мне нравится ответ AshleysBrain, он помог мне решить проблему. Но я нашел немного более общее решение.

До того, как вам нужно было инициировать воспроизведение звука из пользовательского события, теперь они заставляют вас делать это через событие пользовательского ввода (звучит странно). Я только что прочитал поле ввода перед тем, как воспроизвести звук.

Так

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

playSoundFile - это то, что вы используете для создания исходного буфера.

Другие вопросы по тегам