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

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

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

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

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

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

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

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

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

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

Ответ 1

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

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

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

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

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

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

Ответ 2

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

  • Включить "Webkit Inspector" в настройках/расширенных настройках Mobile Safari.
  • Подключите устройство к Mac, работающему под управлением Safari 6, используя USB-кабель.
  • Загрузите свою страницу/игру
  • Перейти в меню Разработка → [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), а <<24 > .

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

Ответ 3

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

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

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

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

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

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

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

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

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

    /li >
  • ИЛИ 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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

Я столкнулся с ограничениями звука с помощью HTML5 Audio на iOS и работал над проблемой:

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

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

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

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

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

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

Ответ 7

обновление для 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

- ниже мое обновленное решение для первой статьи, используя изменения из второй статьи. Проблема, с которой я столкнулся, - это сафари iOS 7, которые бросают странную ошибку недостаточно-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 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;
});

Ответ 8

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

Я раньше застрял в веб-аудио на веб-сайте 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>

Ответ 9

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

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

Ответ 10

API, по-видимому, сломан на iOS 6.1 или, по крайней мере, имеет прерывистое изменение, что означает, что в настоящее время он не работает с сайтами.

Ответ 12

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

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

Итак,

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

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