草庐IT

ios - WebKit 音频在 iOS 6 (iPhone 5) 上第一次在电源循环后失真

coder 2023-07-28 原文

我一直在与 iOS 6 下 HTML5 中使用 webkitAudioContext 的难以捉摸的音频失真错误作斗争。在其他情况下可能会发生这种情况,但我能获得 100% 重现的唯一方法是在电源循环后第一次访问我的页面时设备。似乎如果您在访问此页面之前访问任何支持音频的页面,问题就不会发生。

失真仅发生在由 webkitAudioContext.decodeAudioData() 生成然后通过 webkitAudioContext.createBufferSource() 播放的音频上。 webkitAudioContext.createMediaElementSource() 的音频播放将不会失真。

我是否遗漏了一些初始化步骤?这是我作为错误报告提交给 Apple 的完整代码和 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 (e.g. 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>

注意:正文表明这仅发生在 iOS 6.1.4 上,但我的意思是说该问题仅在这种情况下的电源循环时发生。我在 6.1.3 下的 iPad Mini 上也遇到过这个问题,但在电源循环时没有。

编辑:我尝试过的一些事情...推迟缓冲区源的创建没有任何区别。使用不同的转码器生成它播放的 .mp3 文件没有区别。作为第一个声音播放一次性静音没有任何区别,因为每个 decodeAudioData 声音都会继续失真,直到页面重新加载。如果 createMediaElementSource 和 createBufferSource 源在同一页面中混合,则只有 createBufferSource 音频(使用 decodeAudioData)会失真。当我在失败案例和非失败案例中检查 request.response.byteLength 时,它们是相同的,这表明 XMLHttpRequest 没有返回不正确的数据,尽管我认为数据损坏会损坏 MP3 header 并呈现文件无论如何都无法播放。

故障情况和非故障情况之间存在一个明显的区别。只读值 context.sampleRate 在失败状态下为 48000,在非失败状态下为 44100。 (然而,失败状态听起来比非失败状态音调低。)我唯一想到的是一个 hack,如果在应该报告 44100 的浏览器上检测到 48000,我通过 JavaScript 刷新页面,但这是严重的 userAgent筛选而不是 future 的证据,这让我很紧张。

最佳答案

即使在 iOS 9.2 上,我也遇到过类似的问题。

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

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

我确实找到了解决方法:可以重新创建 AudioContext播放初始声音后。新创建的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();
}

关于ios - WebKit 音频在 iOS 6 (iPhone 5) 上第一次在电源循环后失真,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17892345/

有关ios - WebKit 音频在 iOS 6 (iPhone 5) 上第一次在电源循环后失真的更多相关文章

  1. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  2. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  3. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  4. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  5. ruby-on-rails - Capybara-webkit 引发 Capybara::Driver::Webkit::WebkitInvalidResponseError - 2

    我在rspec中收到来自webkit驱动程序的以下消息:Capybara::Driver::Webkit::WebkitInvalidResponseError:UnabletoloadURL:http://127.0.0.1:44923/posts几天前它成功了。问题出在save_page方法上。有什么问题吗? 最佳答案 当我的页面出现错误时,我收到过类似的错误消息。您应该通过在测试模式下启动服务器(railss-etest)并自行访问页面来手动检查情况是否如此。 关于ruby-on-

  6. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  7. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  8. ruby - 我怎样才能只写一次 "Text"并同时检查 path_info 是否包含 'A' ? - 2

    -if!request.path_info.include?'A'%{:id=>'A'}"Text"-else"Text"“文本”写了两次。我怎样才能只写一次并同时检查path_info是否包含“A”? 最佳答案 有两种方法可以做到这一点。使用部分,或使用content_forblock:如果“文本”较长,或者是一个重要的子树,您可以将其提取到一个部分。这会使您的代码变干一点。在给出的示例中,这似乎有点矫枉过正。在这种情况下更好的方法是使用content_forblock,如下所示:-if!request.path_info.inc

  9. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  10. ruby-on-rails - 使用 Ruby 正确处理 Stripe 错误和异常以实现一次性收费 - 2

    我查看了Stripedocumentationonerrors,但我仍然无法正确处理/重定向这些错误。基本上无论发生什么,我都希望他们返回到edit操作(通过edit_profile_path)并向他们显示一条消息(无论成功与否)。我在edit操作上有一个表单,它可以POST到update操作。使用有效的信用卡可以正常工作(费用在Stripe仪表板中)。我正在使用Stripe.js。classExtrasController5000,#amountincents:currency=>"usd",:card=>token,:description=>current_user.email)

随机推荐