繁体   English   中英

iOS 6 Web Audio API上没有声音

[英]No sound on iOS 6 Web Audio API

我很高兴看到iOS 6支持Web Audio API,因为我们制作HTML5游戏。 但是,我无法让iOS 6使用Web Audio API播放任何声音,其中的示例在桌面Chrome中运行良好。

这是一款带有触摸控件和通过Web Audio API播放音频的HTML5游戏(如果存在 - 如果不存在,它将回退到HTML5音频):

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

编辑:@Srikumar提出了一些解决方法。 我在下面的版本中应用它们。 它仍然无法正常工作!

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

桌面Chrome上的所有功能都可以正常播放,但iOS 6根本不会发出任何声音。 我无法调试它,因为我只进行Windows开发,iOS 6用远程Web检查器取代了调试模式,这显然在Safari for Windows上不可用。 使用一些警报,我确实发现它正确识别Web Audio API,使用它,检测不到Vorbis支持,因此回退到AAC音频,解码缓冲区然后播放它,并且没有抛出任何错误,但我什么都没听到。 当然,我尝试将音量调高到最大值:)

应该没有编解码器问题,因为iOS 6可以很好地播放AAC - 您可以浏览.m4a中的一个游戏,并且直接从Safari访问它。

在iOS 6上查看Web Audio API示例: http//chromium.googlecode.com/svn/trunk/samples/audio/samples.html - 其中一些有效,有些则无效。 例如, Chrome Audio Visualizer可以正常工作,但Javascript Drone却没有。

iOS 6和桌面Chrome上的Web Audio之间必然存在一些微妙的不兼容性。 我错过了什么?

编辑(2015年11月): iOS 9不再允许音频在touchstart事件中启动,这会破坏下面的解决方案。 然而,它适用于touchend事件。 iOS 6的原始答案在下面保留不变,但对于iOS 9支持,请确保使用touchend

好吧,很抱歉回答我自己的赏金问题,但经过几个小时的调试后我终于找到了答案。 iOS 6上的Safari有效地启动了Web Audio API静音。 在您尝试在用户输入事件中播放声音 (创建缓冲源,将其连接到目标,并调用noteOn()之前,它不会取消静音。 在此之后,它取消静音,音频播放不受限制,应该如此。 这是关于Web Audio API如何在iOS 6上运行的一个未记录的方面( Apple的doc就在这里 ,希望他们很快就会更新它!)

用户可以大量触摸屏幕,参与游戏。 但它仍将保持低调。 必须在一个用户输入事件中玩,比如touchstart [edit: touchend for iOS 9+],一次,然后所有音频touchend静音。 之后,您可以随时播放音频(不必在用户输入事件中)。

请注意,这与HTML5音频的限制不同:通常,您只能在用户输入事件中启动音频,并且一次只能播放一个声音; 在第一次播放用户输入后,Web Audio API完全取消静音,这样您就可以随时播放声音,然后您可以复用它们,处理炫酷效果等。

这意味着许多已经在网络上使用Web Audio API的游戏将永远不会播放音频,因为它们不会在触摸事件中发出noteOn。 您必须调整它以等待第一个用户输入事件。

有几种方法可以解决这个问题:在用户触摸屏幕之前不要播放标题音乐; 有一个初始的'触摸启用音频'屏幕并播放声音然后在他们触摸时开始游戏; 希望这可以帮助其他任何有同样问题的人节省一些时间来尝试调试它!

您可以尝试使用Mac上Safari 6上的Web Inspector对其进行调试。

  1. 在Mobile Safari设置/高级中启用“Webkit Inspector”。
  2. 使用USB线将设备连接到运行Safari 6的Mac。
  3. 加载您的页面/游戏
  4. 转到菜单开发 - > [devicename] - > [pageurl]

它对我来说不是开箱即用的,但只需几次尝试就可以帮助缩小问题范围。

显然,音频只能由用户动作触发。 我不确定这是真的'因为在iPhone4上适用于iOS6的一些代码在iPad(也是iOS6)上没有播放任何声音。

更新 :iPhone4 + iOS6上的网络音频取得了一些成功。 在iOS6上创建新的音频上下文后,发现“currentTime”暂时停留在0。 为了让它移动,首先需要执行一个虚拟API调用(如createGainNode()并丢弃结果)。 声音仅在currentTime开始运行时播放,但是在currentTime处的调度声音似乎不起作用。 他们需要进入未来(例如:10ms)。 您可以使用以下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)中播放声音。

正如其他人指出的那样,由于用户交互(click或touchstart),您必须创建AudioContext (顺便AudioContext ,您必须在页面的生命周期内存储和使用)。

但是:要让iOS“解锁”其音频功能,您必须在创建AudioContext时提供音频数据。 如果以异步方式加载数据,则无需播放。 仅仅在click事件中创建AudioContext是不够的。

这是可靠iOS播放的两种解决方案:

  • 1)您必须在初始化AudioContext之前加载至少一个声音文件,然后在单个用户交互中立即运行该声音文件的所有上述步骤(例如,单击)。

  • 或2)在内存中动态创建声音并播放。

这就是我做第二个选项的方式:

记住 - 必须在iOS的click / touch事件中:

 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或类似内容是不够的。

在您的情况下,当用户单击“触摸开始”按钮时,我将创建上下文。

回答原来的问题,我可以在iPhone 4S / iOS 6和MacOSX上确认文件格式的一些问题。 如果一个MP3文件对于Safari来说“不好”,解码就会变坏并且调用AudioContext.createBuffer(array,bool)会给你带来错误。

奇怪的是关于错误:“SYNTAX_ERR,DOM Exception 12”,正如其他人所指出的那样。 这让我觉得这是一个bug ....

使用Safari 6.0(7536.25)在MacOS上也有相同的行为。

我在iOS上遇到了HTML5音频的音频限制,并解决了以下问题:

1)使用静音音频文件创建音频元素并最初使用触摸事件(例如“开始游戏”按钮)播放,然后立即暂停。

2)构建声音切换器功能,切换音频src,然后在短暂超时后播放音频元素。

3)在任何事件上调用声音切换器功能(不需要是触摸事件)。

这是因为音频元素在第一次触摸时使用静音音频文件取消静音,并保持未静音,因此可以即时切换音源。

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

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

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

-below是我对第一篇文章的更新解决方案,使用了第二篇文章中的更改。 我遇到的问题是iOS 7 safari抛出一个奇怪的不够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 Web Audio API上没有声音

我以前在iOS网站上遇到了网络音频。 更糟糕的是,它需要在Android和其他桌面平台上工作。 这篇文章是我读过的那些帖子之一,没有立即得到答案。

直到我找到howler.js

这是跨平台Web音频解决方案的解决方案:

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

这不是一个真正的答案,只是一个方向,看看事情是否仍然有效。 iOS6在某些设备上存在音频问题(特别是在特定时期制造的64gb 4s,虽然我见过其他设备因此实际上可能与硬件无关)并会神秘地停止播放某些类型的声音(不是铃声或语音,对于某些设备原因,但许多其他声音),它的音量滑块将消失。 我发现很难调试,因为通常情况下(通常不会总是这样,有时你可以抓住它)只有在没有连接电源线时才会发生。

在控制台中查看来自VirtualAudio_Device的ASSERTION FAILURE消息以及各种编解码器。 这可能与您的特定问题没有任何关系,但是再一次,声音设备的一个区域中的错误可能与另一个区域相关。 至少,如果没有其他任何帮助,这是一个调查的领域。

该API似乎在iOS 6.1上被破坏,或者至少有一个突破性的变化,这意味着没有网站目前使用它。

我使用所有简单的解决方案都遇到了麻烦。 特别是,当我想多次播放声音时。

所以我正在使用这个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是用于创建缓冲区源的任何内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM