iOS 6 Web Audio API上没有声音

Ash*_*ain 39 javascript html5 mobile-safari ios6 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之间必然存在一些微妙的不兼容性.我错过了什么?

Ash*_*ain 48

编辑(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:touchendfor iOS 9+]之类的用户输入事件中播放一次,然后所有音频取消静音.之后,您可以随时播放音频(不必在用户输入事件中).

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

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

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


Sri*_*mar 5

您可以尝试使用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();
}
Run Code Online (Sandbox Code Playgroud)

随后,在播放音符时,不要打电话.noteOn(ac.currentTime),而是打电话.noteOn(ac.currentTime + 0.01).

请不要问我为什么要这么做.这就是目前的方式 - 即疯狂.


Sim*_*ver 5

我设法找到一个简单的解决方案,我肯定必须在其他地方记录下来 - 但有时我们不得不花费数小时为自己搞清楚这些事情......

因此,似乎很多教程(例如html5rocks上的这个教程)指示您执行以下步骤:

  • 创建一个实例,window.AudioContext如果不存在(在iOS上没有),则创建window.webkitAudioContext.

  • 创建一个XMLHttpRequest加载您的声音文件

  • load事件运行context.decodeAudioData(....)然后createBufferSource(),用解码数据填充它,最后source.start(0)播放声音.

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

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

这是可靠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
Run Code Online (Sandbox Code Playgroud)

我想这是一个常见的错误 - 我在3年后感到惊讶,似乎没有人指出这一点或发现它: - /