WebKit Audio在电力循环后首次在iOS 6(iPhone 5)上失真

在iOS 6下,我一直在使用HTML5中的webkitAudioContext处理一个难以捉摸的audio失真问题。在其他情况下,可能会发生这种情况,但唯一可以获得100%repro的方法是在设备重新上电后首次访问我的页面。 看起来如果你在访问这个网页之前访问了任何有audiofunction的网页,问题就不会发生。

失真只发生在webkitAudioContext.decodeAudioData()产生的audio,然后通过webkitAudioContext.createBufferSource()播放。 webkitAudioContext.createMediaElementSource()的audio播放不会变形。

我是否缺less一些初始化步骤? 下面是我作为bug报告提交给苹果公司的完整代码和HTML(但没有收到回复):

<!DOCTYPE html> <html> <head> <script type="text/javascript"> var buffer = null; var context = null; var voice = null; function load_music(file) { context = new webkitAudioContext(); voice = context.createBufferSource(); var request = new XMLHttpRequest(); request.onload = function() { context.decodeAudioData(request.response, function(result) { buffer = result; document.getElementById("start").value = "Start"; }); }; var base = window.location.pathname; base = base.substring(0, base.lastIndexOf("/") + 1); request.open("GET", base + file, true); request.responseType = "arraybuffer"; request.send(null); } function start_music() { if (!buffer) { alert("Not ready yet"); return; } voice.buffer = buffer; voice.connect(context.destination); voice.noteOn(0); document.getElementById("compare").style.display = "block"; } </script> </head> <body onload="load_music('music.mp3')"> <p>This is a simple demo page to reproduce a <strong>webkitAudio</strong> problem occurring in Safari on iOS 6.1.4. This is a stripped down demo of a phenomenon discovered in our HTML5 game under development, using different assets.</p> <p><u>Steps to reproduce:</u></p> <ol> <li>Power cycle <strong>iPhone 5 with iOS 6.1.4</strong>.</li> <li>Launch Safari immediately, and visit this page.</li> <li>Wait for &quot;Loading...&quot; below to change to &quot;Start&quot;.</li> <li>Tap &quot;Start&quot;.</li> </ol> <p><u>Issue:</u></p> <p>Audio will be excessively distorted and play at wrong pitch. If another audio-enabled web site is visited before this one, or this site is reloaded, the audio will fix. The distortion only happens on the first visit after cold boot. <strong>To reproduce the bug, it is critical to power cycle before testing.</strong></p> <p>This bug has not been observed on any other iOS version (eg does not occur on iPad Mini or iPod 5 using iOS 6.1.3).</p> <input id="start" type="button" value="Loading..." onmousedown="start_music()" /> <span id="compare" style="display:none;"><p><a href="music.mp3">Direct link</a> to audio file, for comparison.</p></span> </body> </html> 

注:正文build议这只发生在iOS 6.1.4,但我的意思是说,这个问题只发生在这种情况下的电力循环。 我也遇到了6.1.3版本的iPad Mini上的问题,但是没有上电循环。

编辑:我已经尝试了几件事…推迟创build缓冲区源码没有区别。 使用不同的转码器来生成.mp3文件,它没有什么区别。 播放一次性静音作为第一个声音没有任何区别,因为每次decodeAudioData声音的失真都会持续下去,直到页面重新载入。 如果createMediaElementSource和createBufferSource源在同一个页面中混合,则只有createBufferSourceaudio(使用decodeAudioData)会变形。 当我检查request.response.byteLength在失败的情况下和非失败的情况下,他们是一样的,这表明XMLHttpRequest没有返回不正确的数据,但我认为数据的损坏会损坏MP3头,并呈现无论如何文件无法播放。

在故障状态和非故障状态之间有一个明显的差别。 只读值context.sampleRate将在故障状态下为48000,在非故障状态下为44​​100。 (然而,失败状态听起来比非失败状态低。)唯一发生在我身上的是如果在应该报告44100的浏览器上检测到48000,那么我通过JavaScript刷新页面,但这是严重的userAgent筛选并不是很有前途的证据,这让我感到紧张。

我一直有类似的问题,即使在iOS 9.2。

即使没有<video>标签,在冷启动后首次播放页面上的audio时,播放也会失真。 重新加载后,它工作正常。

最初的AudioContext似乎默认为48 kHz,这是发生失真的地方(即使我们的audio采样率为48 kHz)。 当播放工作正常时, AudioContext的采样率为44.1 kHz。

我find了一个解决方法:可以在播放初始声音后重新创buildAudioContext 。 新创build的AudioContext似乎具有正确的采样率。 去做这个:

 // inside the click/touch handler var playInitSound = function playInitSound() { var source = context.createBufferSource(); source.buffer = context.createBuffer(1, 1, 48000); source.connect(context.destination); if (source.start) { source.start(0); } else { source.noteOn(0); } }; playInit(); if (context.sampleRate === 48000) { context = new AudioContext(); playInit(); } 

我发现与HTML5video有关的错误,并认为我发现了问题的根源。
我注意到,如果您使用<video>标签播放video,则会将context.sampleRate值设置为video的audio编码。 看起来好像iOS Safari有一个全局的sampleRate,它用于一切。 要看到这一点,请尝试以下操作:

 // Play a video with audio encoded at 44100 Hz video.play(); // This will console log 44100 var ctx = new webkitAudioContext(); console.log(ctx.sampleRate); // Play a video with audio encoded at 48000 Hz video2.play(); // This will console log 48000 var ctx = new webkitAudioContext(); console.log(ctx.sampleRate); 

此全局采样率似乎在页面加载中持续存在,并在选项卡和浏览器实例之间共享。 所以,在另一个选项卡中播放YouTubevideo可能会破坏所有已解码的audio。

当audio以一个采样率解码并在另一个采样率上播放时,audio会失真。

  1. 解码audio并存储缓冲区
  2. 做一些改变采样率的方法,如播放video或audio文件
  3. 播放缓冲区(失真)

我不知道为什么冷开始后会发生这种情况。 如果我不得不猜测,那就是Safari在您尝试使用它之前不会初始化这个全局采样率。

问题仍然在iOS 7上,所以我不认为一个修复即将到来。 与此同时,我们被困在黑客手中,比如检查一个更改的采样率。

一个NPM包在线来解决这个问题:

https://github.com/Jam3/ios-safe-audio-context

 npm install ios-safe-audio-context