Javascript iOS 6 Web Audio API 上没有声音
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12517000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
No sound on iOS 6 Web Audio API
提问by AshleysBrain
I was really excited to see iOS 6 supports the Web Audio API, since we make HTML5 games. However, I cannot get iOS 6 to play any sound at all using the Web Audio API with examples that work fine in desktop Chrome.
我很高兴看到 iOS 6 支持 Web Audio API,因为我们制作 HTML5 游戏。但是,我无法使用 Web Audio API 和在桌面 Chrome 中正常工作的示例让 iOS 6 播放任何声音。
Here is a HTML5 game with touch controls and playing audio via the Web Audio API (if present - if not it will fall back to HTML5 audio):
这是一个带有触摸控制和通过 Web Audio API 播放音频的 HTML5 游戏(如果存在 - 如果没有,它将回退到 HTML5 音频):
http://www.scirra.com/labs/sbios6b/
http://www.scirra.com/labs/sbios6b/
Edit: @Srikumar suggested some workarounds. I applied them at the version below. It still does not work!
编辑:@Srikumar 提出了一些解决方法。我在下面的版本中应用了它们。它仍然不起作用!
http://www.scirra.com/labs/sbios6f/
http://www.scirra.com/labs/sbios6f/
Everything plays just fine on desktop Chrome, but iOS 6 emits no sound at all. I'm having trouble debugging it because I only do Windows development, and iOS 6 replaced the debug mode with remote web inspector, which apparently is not available on Safari for Windows. Using a few alerts I did find it correctly identifies the Web Audio API, uses it, detects no Vorbis support so falls back to AAC audio, decodes a buffer and then plays it, and no errors are thrown, but I hear nothing. And, of course, I tried turning the volume up to max :)
在桌面 Chrome 上一切正常,但 iOS 6 根本没有声音。我在调试它时遇到了麻烦,因为我只进行 Windows 开发,而 iOS 6 用远程 Web 检查器替换了调试模式,这显然在 Windows 的 Safari 上不可用。使用一些警报,我确实发现它正确识别了 Web Audio API,使用它,检测到没有 Vorbis 支持,因此回退到 AAC 音频,解码缓冲区然后播放它,没有抛出错误,但我什么也没听到。而且,当然,我尝试将音量调到最大:)
There should not be a codec problem, because iOS 6 can play AAC just fine - you can browse to one of the .m4a's the game playsand it plays fine visited direct from Safari.
应该没有编解码器问题,因为 iOS 6 可以很好地播放 AAC - 您可以浏览到.m4a 的游戏之一,并且可以直接从 Safari 访问它。
Looking at the Web Audio API examples here on iOS 6: http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html- some of them work, and others don't. For example, the Chrome Audio Visualizerworks, but Javascript Dronedoesn't.
在 iOS 6 上查看此处的 Web Audio API 示例:http: //chromium.googlecode.com/svn/trunk/samples/audio/samples.html- 其中一些有效,而另一些则无效。例如,Chrome Audio Visualizer可以工作,但Javascript Drone不行。
There must be some subtle incompatibility between Web Audio on iOS 6 and desktop Chrome. What am I missing?
iOS 6 上的 Web Audio 和桌面 Chrome 之间肯定存在一些微妙的不兼容。我错过了什么?
回答by AshleysBrain
Edit (November 2015):iOS 9 no longer allows audio to start in a touchstart
event, which breaks the solution below. However it works in a touchend
event. The original answer for iOS 6 is left intact below, but for iOS 9 support make sure you use touchend
.
编辑(2015 年 11 月):iOS 9 不再允许在touchstart
事件中启动音频,这打破了下面的解决方案。但是它在一个touchend
事件中起作用。iOS 6 的原始答案在下面保持不变,但对于 iOS 9 支持,请确保您使用touchend
.
Well, sorry to answer my own bounty question, but after hours of debugging I finally found the answer. Safari on iOS 6 effectively starts with the Web Audio API muted. It will not unmute until you attempt to play a sound in a user input event(create a buffer source, connect it to destination, and call noteOn()
). After this, it unmutes and audio plays unrestricted and as it ought to. This is an undocumented aspect of how the Web Audio API works on iOS 6 (Apple's doc is here, hopefully they update it with a mention of this soon!)
好吧,很抱歉回答我自己的悬赏问题,但经过数小时的调试,我终于找到了答案。iOS 6 上的 Safari 有效地从 Web Audio API 静音开始。直到您尝试在用户输入事件中播放声音(创建缓冲区源,将其连接到目标,然后调用noteOn()
),它才会取消静音。在此之后,它会取消静音并且音频播放不受限制并且应该如此。这是 Web Audio API 如何在 iOS 6 上工作的一个未记录的方面(Apple 的文档在这里,希望他们尽快更新它并提及这一点!)
The user can be touching the screen a lot, engaged in the game. But it will remain muted. You haveto play inside a user input event like touchstart
[edit: touchend
for iOS 9+], once, then all audio unmutes. After that you can play audio at any time (doesn't have to be in a user input event).
用户可以多次触摸屏幕,参与游戏。但它会保持静音。您必须在像touchstart
[edit: touchend
for iOS 9+]这样的用户输入事件中播放一次,然后所有音频都取消静音。之后您可以随时播放音频(不必在用户输入事件中)。
Note this is different to the restrictions on HTML5 audio: typically you can only start audio at all in a user input event, and only play one sound at a time; the Web Audio API fully unmutes after the first play-in-user-input, so that you can play sounds at any time, and then you can mix them polyphonically, process cool effects, etc.
请注意,这与 HTML5 音频的限制不同:通常您只能在用户输入事件中启动音频,并且一次只能播放一种声音;Web Audio API 在第一次播放用户输入后完全取消静音,这样您就可以随时播放声音,然后您可以将它们进行和弦混合,处理很酷的效果等。
This means many games already on the web using the Web Audio API will never play audio, because they do not happen to issue a noteOn in a touch event. You have to adjust it to wait for the first user input event.
这意味着已经在网络上使用 Web Audio API 的许多游戏永远不会播放音频,因为它们不会在触摸事件中发出 noteOn。您必须调整它以等待第一个用户输入事件。
There are a few ways to work around this: don't play your title music until the user touches the screen; have an initial 'touch to enable audio' screen and play a sound then begin the game when they touch; etc. Hopefully this will help anyone else having the same problem save some time trying to debug it!
有几种方法可以解决这个问题:在用户触摸屏幕之前不要播放标题音乐;有一个初始的“触摸以启用音频”屏幕并播放声音,然后在他们触摸时开始游戏;等等。希望这将帮助其他有同样问题的人节省一些时间来调试它!
回答by Srikumar
You can try to debug it using the Web Inspector on Safari 6 on a mac.
您可以尝试在 Mac 上的 Safari 6 上使用 Web Inspector 对其进行调试。
- Enable "Webkit Inspector" in Mobile Safari settings/advanced.
- Connect device to a Mac running Safari 6 using a USB cable.
- Load your page/game
- Go to menu Develop->[devicename]->[pageurl]
- 在 Mobile Safari 设置/高级中启用“Webkit Inspector”。
- 使用 USB 电缆将设备连接到运行 Safari 6 的 Mac。
- 加载您的页面/游戏
- 转到菜单开发->[设备名称]->[pageurl]
It doesn't work out of the box for me, but with a few tries it can help narrow down the problem.
它对我来说不是开箱即用的,但通过几次尝试,它可以帮助缩小问题的范围。
Apparently there is also the thing that audio can only be triggered by a user action. I'm not sure this is true 'cos some code that works on iOS6 on iPhone4 doesn't play any sound on an iPad (also iOS6).
显然还有一个问题,即音频只能由用户操作触发。我不确定这是真的,因为一些在 iPhone4 上的 iOS6 上运行的代码在 iPad(还有 iOS6)上没有播放任何声音。
Update: Some success with web audio on iPhone4+iOS6. Found that the "currentTime" remains stuck at 0 for a while as soon as you create a new audio context on iOS6. In order to get it moving, you first need to perform a dummy API call (like createGainNode()
and discard the result). Sounds play only when currentTime starts to run, but scheduling sounds exactly at currentTime doesn't seem to work. They need to be a little bit into the future (ex: 10ms). You can use the following createAudioContext
function to wait until the context is ready to make noise. User action doesn't seem to be required on iPhone, but no such success on iPad just yet.
更新:在 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();
}
Subsequently, when playing a note, don't call .noteOn(ac.currentTime)
, but do .noteOn(ac.currentTime + 0.01)
instead.
随后,在弹奏音符时,不要调用.noteOn(ac.currentTime)
,而是调用.noteOn(ac.currentTime + 0.01)
。
Please don't ask me whyyou have to do all that. That's just the way it is at the moment - i.e. crazy.
请不要问我为什么你必须这样做。这就是目前的情况——即疯狂。
回答by Simon_Weaver
I managed to figure out a simple solution which I'm sure must have been documented elsewhere - but sometimes we have to spend hours figuring these things out for ourselves...
我设法想出了一个简单的解决方案,我确信它肯定在其他地方记录过——但有时我们不得不花几个小时为自己弄清楚这些事情......
So it seems many tutorials (such as this one on html5rocks) instruct you to do the following steps :
因此,似乎很多教程(例如html5rocks上的这个教程)都指导您执行以下步骤:
Create an instance of
window.AudioContext
and if that doesn't exist (which it doesn't on iOS) then createwindow.webkitAudioContext
.Create an
XMLHttpRequest
to load your sound fileOn the
load
event runcontext.decodeAudioData(....)
and thencreateBufferSource()
, filling it with the decoded data, and finallysource.start(0)
to play the sound.
创建一个实例,
window.AudioContext
如果它不存在(在 iOS 上不存在),则创建window.webkitAudioContext
.创建一个
XMLHttpRequest
加载你的声音文件在
load
事件 runcontext.decodeAudioData(....)
然后createBufferSource()
,用解码的数据填充它,最后source.start(0)
播放声音。
As others have pointed out you must create the AudioContext
(which incidentally you must store and use for the lifetime of the page) as a result of a user interaction (click or touchstart).
正如其他人指出的那样,您必须创建AudioContext
(顺便说一句,您必须在页面的生命周期内存储和使用)作为用户交互(单击或触摸启动)的结果。
HOWEVER :For iOS to 'unlock' its audio capabilities you MUSThave audio data available when you create the AudioContext
. If you load the data asynchronously there's nothing for it to play. It is not sufficient to merely create the AudioContext
inside a click
event.
但是:对于 iOS 要“解锁”其音频功能,您 必须在创建AudioContext
. 如果你异步加载数据,它就没有什么可玩的。仅仅AudioContext
在click
事件内部创建是不够的。
Here's two solutions for reliable iOS playback:
以下是可靠的 iOS 播放的两种解决方案:
1) You must load at least one sound file before you even initialize the AudioContext, and then run all the above steps for that sound file immediately within a single user interaction (eg click).
OR 2) Create a sound dynamically in memory and play it.
1) 在初始化 AudioContext 之前,您必须至少加载一个声音文件,然后在单个用户交互(例如单击)中立即为该声音文件运行上述所有步骤。
或 2) 在内存中动态创建声音并播放。
This is how I did that second option:
这就是我做第二个选项的方式:
REMEMBER - MUST BE within click
/ touch
event for iOS:
记住 - 必须在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
I imagine this is a common mistake - and I'm surprised after 3 years that nobody seems to have pointed this out or discovered it :-/
我想这是一个常见的错误 - 三年后我很惊讶似乎没有人指出或发现它:-/
回答by Oskar Eriksson
So, I think I've figured it out.
所以,我想我已经想通了。
It's a issue of Apple requiring an user action before sound can be allowed to play. It turns out, at least for me, that you shouldn't create the audio context at all except when the user calls for it. It's not enough to create the context when the page loads and then use createGainNode or similar on an user action.
这是 Apple 的一个问题,要求用户执行操作才能允许播放声音。事实证明,至少对我而言,除非用户要求,否则根本不应该创建音频上下文。在页面加载时创建上下文,然后在用户操作上使用 createGainNode 或类似方法是不够的。
In your case I'd create the context when the user clicks the "Touch to begin" button.
在您的情况下,我会在用户单击“触摸开始”按钮时创建上下文。
回答by Hyman Wild
I have come across the audio restrictions with HTML5 Audioon iOS and worked around the problem by:
我在 iOS 上遇到了HTML5 音频的音频限制,并通过以下方式解决了这个问题:
1) Creating an Audio Element with a silent audio file and playing it initially with a touch event (e.g. 'begin game' button) and then immidietly pausing it.
1) 创建一个带有无声音频文件的音频元素,最初使用触摸事件(例如“开始游戏”按钮)播放它,然后立即暂停它。
2) Building a sound-switcher function which switches the Audio src and then plays the Audio element after a short timeout.
2) 构建一个声音切换器功能,该功能切换音频源,然后在短暂超时后播放音频元素。
3) Calling the sound-switcher function on any events (doesn't need to be a touch event).
3)在任何事件上调用声音切换器功能(不需要是触摸事件)。
This works because the Audio Element is un-muted on first touch, with the silent audio file, and remains un-muted, so the source can be switched on-the-fly.
这是有效的,因为音频元素在第一次触摸时未静音,带有静音音频文件,并且保持未静音,因此可以即时切换源。
switchSound: (id) ->
@soundSwitch.pause()
@soundSwitch.src = @sounds[id]._src
clearTimeout @switchSoundDelay
@switchSoundDelay = setTimeout =>
# @soundSwitch.volume = 1
@soundSwitch.play()
,50
回答by mat
updated for 2015 solution:hey all, if you are here working on a web audio problem with ios6+ I've found these links as help.
针对 2015 年解决方案进行了更新:大家好,如果您在这里处理 ios6+ 的网络音频问题,我发现这些链接可以作为帮助。
-this is a good article with code solution: http://matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/
- 这是一篇带有代码解决方案的好文章:http: //matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/
-here is an update to the api after the above ^ solution article was written https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
- 在写完上述 ^ 解决方案文章后,这里是 api 的更新https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
-below is my updated solution to the first article, using the changes from the second article. The issue I was having was iOS 7 safari throwing a strange not-enough-args error. this fixed it:
- 以下是我对第一篇文章的更新解决方案,使用了第二篇文章中的更改。我遇到的问题是 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;
});
回答by ytbryan
Update: iOS still requires a user input to play sound (No sound on iOS 6 Web Audio API)
更新:iOS 仍然需要用户输入才能播放声音(iOS 6 Web Audio API 上没有声音)
I was previously stuck with web audio on iOS web. And to make things worse, it needs to work on android and other desktop platform. This post is one of those posts that I read and found no immediate answers.
我之前一直在使用 iOS 网络上的网络音频。更糟糕的是,它需要在 android 和其他桌面平台上运行。这篇文章是我阅读并没有立即找到答案的文章之一。
Until I found howler.js.
直到我找到了howler.js。
This is the solution for cross-platform web audio solution:
这是跨平台网络音频解决方案的解决方案:
<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>
回答by Enrico MRK
Answering to the originalquestion, I can confirm some troubles with file formats on iPhone 4S/iOS 6 and MacOSX. If an MP3 file is "not good" for Safari, the decoding goes bad and calling AudioContext.createBuffer(array, bool) gives you and error.
回答原始问题,我可以确认 iPhone 4S/iOS 6 和 MacOSX 上的文件格式存在一些问题。如果 Safari 的 MP3 文件“不好”,则解码会出错并且调用 AudioContext.createBuffer(array, bool) 会给您和错误。
The strange thing is about the error: "SYNTAX_ERR, DOM Exception 12", as pointed out by others. This makes me think it is a bug....
奇怪的是关于错误:“SYNTAX_ERR,DOM Exception 12”,正如其他人指出的那样。这让我觉得这是一个错误......
Same behavior also on MacOS, with Safari 6.0 (7536.25).
在 MacOS 上也有相同的行为,使用 Safari 6.0 (7536.25)。
回答by Mason Cloud
This isn't an actual answer, just a direction to look if things still aren't working. iOS6 has audio issues on somedevices (particularly the 64gb 4s manufactured during a particular period, though I've seen others so it may not actually be hardware related) and will mysteriously stop playing some kinds of sounds (not ringtones or voice, for some reason, but many other sounds), and it's volume sliders will vanish. I've found it notoriously difficult to debug as it will usually (thought not always, sometimes you can catch it) happen only when not connected with a power cord.
这不是一个实际的答案,只是一个方向,看看事情是否仍然不起作用。iOS6 在某些设备上有音频问题(特别是在特定时期生产的 64gb 4s,虽然我见过其他的,所以它实际上可能与硬件无关)并且会神秘地停止播放某些类型的声音(不是铃声或语音,对于某些原因,但许多其他声音),它的音量滑块将消失。我发现调试非常困难,因为它通常(认为并非总是如此,有时您可以抓住它)仅在未连接电源线时才会发生。
Look in the console for ASSERTION FAILURE messages from the VirtualAudio_Device and with various codecs. This may have nothing whatsoever to do with your particular issue, but then again, a bug in one area of the sound device may be related to another. At minimum, it's an area to investigate if nothing else is helping.
在控制台中查看来自 VirtualAudio_Device 和各种编解码器的断言失败消息。这可能与您的特定问题没有任何关系,但话又说回来,声音设备一个区域的错误可能与另一个相关。至少,如果没有其他帮助,这是一个需要调查的领域。
回答by Mark
The API appears to be broken on iOS 6.1, or at least, has a breaking change that means no sites currently work with it.
该 API 在 iOS 6.1 上似乎已损坏,或者至少有一个重大更改,这意味着当前没有网站使用它。