【问题标题】:No sound on iOS 6 Web Audio APIiOS 6 Web Audio API 没有声音
【发布时间】:2012-09-13 01:51:48
【问题描述】:

看到 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 音频 API,使用它,检测到没有 Vorbis 支持,因此回退到 AAC 音频,解码缓冲区然后播放它,没有抛出任何错误,但我什么也没听到。当然,我试着把音量调到最大:)

应该不存在编解码器问题,因为 iOS 6 可以正常播放 AAC - 您可以浏览到 one of the .m4a's the game plays,它可以正常播放直接从 Safari 访问的内容。

查看 iOS 6 上的 Web Audio API 示例:http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html - 其中一些有效,而另一些则无效。例如,Chrome Audio Visualizer 有效,但 Javascript Drone 无效。

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

【问题讨论】:

  • 可能与文件格式有关。我在 Safari 中播放一些 mp3 时遇到了问题。
  • 可能,但正如帖子所说,我可以直接从 Safari 播放其中一个 .m4a 声音。
  • 您具体遇到了哪些问题?
  • 在我看来,audioContext 的 currentTime 没有按应有的方式更新。只需要弄清楚为什么它在某些应用程序中而不是其他应用程序中...
  • @OskarEriksson - 游戏示例没有在 AudioContext 上使用 currentTime,所以我不知道为什么会破坏它。我还以为是别的东西。

标签: javascript html mobile-safari ios6 web-audio-api


【解决方案1】:

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

好吧,很抱歉回答我自己的赏金问题,但经过数小时的调试,我终于找到了答案。 iOS 6 上的 Safari 有效地从 Web Audio API 静音开始。它不会取消静音直到您尝试在用户输入事件中播放声音(创建缓冲区源,将其连接到目标,然后调用noteOn())。在此之后,它会取消静音并且音频播放不受限制,并且应该如此。这是 Web Audio API 如何在 iOS 6 上工作的一个未记录的方面(Apple's doc is here,希望他们尽快更新它并提及这一点!)

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

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

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

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

【讨论】:

  • 之前的两个答案不是都表明需要用户操作才能获取音频吗?
  • 我认为它们要么暗示您只能在那里创建上下文(不太正确),要么不会播放未在用户操作中触发的音频(也不太正确)
  • 在您尝试在用户输入事件中播放声音之前,它不会取消静音。我有一个反例 - srikumarks.github.com/demos/tala 。 (它是南印度塔拉斯的一种“节拍器”。)如果你在 Safari/iOS6/iPhone 中加载它并稍等片刻,节拍器将开始滴答作响,尽管图形无法正常工作在铬。不需要用户输入(除了键入 URL?)。
  • 我发现您需要在 iPad 而不是 iPhone 上的用户事件上播放声音。在 iPhone 上,它似乎无需任何用户输入即可自动播放。
  • 记录在案,只是很差:User Control of Downloads Over Cellular Networks
【解决方案2】:

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

  1. 在 Mobile Safari 设置/高级中启用“Webkit Inspector”。
  2. 使用 USB 数据线将设备连接到运行 Safari 6 的 Mac。
  3. 加载您的页面/游戏
  4. 转到菜单开发->[设备名称]->[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)

请不要问我为什么你必须这样做。这就是目前的情况——即疯狂。

【讨论】:

  • 感谢您的回答,非常有趣。不幸的是,它似乎不适用于我的第一个示例。考虑这个从头开始制作的演示,它在带有 iOS 6 的 iPad 2 上运行良好:scirra.com/labs/webaudio-ios6-working - 它只使用 noteOn(0) 并且不使用“在启动时创建增益节点”解决方法。这里一定有别的东西在起作用......
  • createGainNode() 技巧似乎对我有用 - 只要确保它在触摸事件的回调中被调用,并且需要触摸触发的声音限制似乎消失了。跨度>
  • 这是我一直在寻找的答案——很好的解决方案!
【解决方案3】:

我设法找到了一个简单的解决方案,我确信它肯定已经在其他地方记录了 - 但有时我们必须花费数小时自己解决这些问题......

看来很多教程(例如html5rocks 上的这个)指导您执行以下步骤:

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

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

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

正如其他人指出的那样,作为用户交互(点击或触摸启动)的结果,您必须创建 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 年后我很惊讶似乎没有人指出或发现这一点:-/

【讨论】:

  • 也适用于从事件处理程序调用的函数中。太棒了!
【解决方案4】:

所以,我想我已经想通了。

这是 Apple 要求用户进行操作才能允许播放声音的问题。事实证明,至少对我来说,你根本不应该创建音频上下文,除非用户需要它。在页面加载时创建上下文然后在用户操作上使用 createGainNode 或类似内容是不够的。

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

【讨论】:

  • 我不认为是这样 - 这个例子在启动时创建了音频上下文并且它工作正常:scirra.com/labs/webaudio-ios6-working 我也尝试在计时器上播放声音(如在,而不是在用户中)输入事件)并且它起作用了。
  • 是的,看起来工作正常。但是,原始游戏目前看起来有些错误。在 iPad 上输入时我得到了这个:“sbios6b:9 - 视口参数键“target-densitydpi”无法识别和忽略。” “c2runtime.js:5534 - SYNTAX_ERR:DOM 异常 12:指定了无效或非法的字符串。”
【解决方案5】:

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

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

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

【讨论】:

    【解决方案6】:

    我在 iOS 上遇到了 HTML5 音频 的音频限制,并通过以下方式解决了这个问题:

    1) 创建一个带有静音音频文件的音频元素,并通过触摸事件(例如“开始游戏”按钮)开始播放,然后立即暂停。

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

    3) 在任何事件上调用声音切换器函数(不必是触摸事件)。

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

    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/

      -这里是上述解决方案文章写完https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext987654322@

      之后的api更新

      -下面是我对第一篇文章的更新解决方案,使用了第二篇文章的更改。我遇到的问题是 iOS 7 Safari 抛出了一个奇怪的 not-enough-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;
      });
      

      【讨论】:

        【解决方案8】:

        更新:iOS 仍然需要用户输入才能播放声音 (No sound on 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,虽然我见过其他设备,所以它实际上可能与硬件无关)并且会神秘地停止播放某些声音(不是铃声或声音,出于某种原因,而是许多其他声音),它的音量滑块将消失。我发现调试非常困难,因为它通常(并非总是如此,有时你可以抓住它)只有在没有连接电源线时才会发生。

          在控制台中查找来自 VirtualAudio_Device 和各种编解码器的 ASSERTION FAILURE 消息。这可能与您的特定问题无关,但话又说回来,声音设备的一个区域中的错误可能与另一个区域有关。至少,如果没有其他帮助,这是一个需要调查的区域。

          【讨论】:

            【解决方案10】:

            该 API 在 iOS 6.1 上似乎已损坏,或者至少有一个重大更改,这意味着目前没有网站可以使用它。

            【讨论】:

              【解决方案11】:

              我无法使用所有简单的解决方案。特别是当我想多次播放声音时。

              所以我正在使用这个 js 库:http://pupunzi.open-lab.com/2013/03/13/making-html5-audio-actually-work-on-mobile

              【讨论】:

                【解决方案12】:

                好的,我喜欢 AshleysBrain 的回答,它帮助我解决了问题。但我找到了一个更通用的解决方案。

                在您必须从用户事件启动播放声音之前,现在他们强制您通过用户输入事件来执行此操作,(听起来很奇怪)我所做的只是在播放声音之前读取输入字段。

                所以

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

                playSoundFile 是你用来创建缓冲源的任何东西。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-10
                  • 2013-01-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-09-14
                  相关资源
                  最近更新 更多