iOS 7.1中使用WebAudio API会导致音频失真问题

9
在iOS 7.1上使用Web Audio API播放音频时,我一直听到嗡嗡声/嘈杂声/失真声。 这里有失真的例子,应该是正常的像这样
使用HTML5音频时,同一文件可以正常播放。在桌面上(Firefox、Chrome、Safari)都能正常工作。
编辑:
  • iOS模拟器版本iOS 7.1、8.1和8.2中的音频失真。嗡鸣声经常在我甚至没有播放任何东西之前就开始了。
  • 物理iPhone运行iOS 7.1,在Chrome和Safari中音频失真。
  • 在物理iPhone运行iOS 8.1的情况下,在Chrome和Safari中音频正常。
即:只有iOS 7.1有嗡鸣声问题。
Howler.js不是问题所在。使用纯JS仍然存在问题,如下所示:
var context;
var sound;
var extension = '.' + ( new Audio().canPlayType( 'audio/ogg' ) !== '' ? 'ogg' : 'mp3');


/** Test for WebAudio API support **/
try {
    // still needed for Safari
    window.AudioContext = window.AudioContext || window.webkitAudioContext;

    // create an AudioContext
    context = new AudioContext();
} catch(e) {
    // API not supported
    throw new Error( 'Web Audio API not supported.' );
}

function loadSound( url ) {
    var request = new XMLHttpRequest();
    request.open( 'GET', url, true );
    request.responseType = 'arraybuffer';

    request.onload = function() {
        // request.response is encoded... so decode it now
        context.decodeAudioData( request.response, function( buffer ) {
        sound = buffer;
        }, function( err ) {
            throw new Error( err );
        });
    }

    request.send();
}

function playSound(buffer) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.start(0);
}

loadSound( '/tests/Assets/Audio/En-us-hello' + extension );


$(document).ready(function(){ 

    $( '#clickme' ).click( function( event ) {
        playSound(sound);
    });


}); /* END .ready() */

这段代码的在线版本可以在此处找到:Web Audio API - Hello world


Google没有任何关于iOS 7.1上出现音频失真问题的结果。

是否有其他人遇到过这个问题?我应该向苹果提交错误报告吗?


是的,它发生在不同的文件中。这里有另一个更高级别的测试:https://bilingueanglais.com/tmp/howler/v2-howler-hello.html,Chrome没有问题,只有Safari有问题。 - Fabien Snauwaert
2
这看起来像是一个低级的Safari问题。从你录制的波形来看,每512个样本中大约有90个样本为零 - 就好像他们的块处理或解码出现了问题。 - cwilso
在拿到手机后编辑了问题。问题实际上只存在于iOS 7.1上,无论是Chrome还是Safari。 - Fabien Snauwaert
3
iOS 9.2仍存在此问题。 - Nikolay Tsenkov
1
同样的问题在这里(在iPhone 5(IOS 9.2)和两个不同的iPhone 6(IOS 9.2和IOS 9.2.1)上进行了测试)。无法在iPhone 5S(IOS 9.2.1)上重现该问题。模式似乎是第一次运行时它可以工作,并且在重新加载后始终可以工作。但是,如果我关闭/重新打开Safari,则声音总是在第一次失真,并在重新加载后工作。 - Patrick Fabrizius
显示剩余3条评论
2个回答

5

我认为问题是由于重置了audioContext.sampleRate属性导致的,这似乎是在浏览器/操作系统播放以不同采样率记录的内容后发生的。

我已经设计了以下解决方法,基本上是在设备当前回放采样率下静默播放一个短的wav文件:

"use strict";

var getData = function( context, filePath, callback ) {
    var source = context.createBufferSource(),
        request = new XMLHttpRequest();

    request.open( "GET", filePath, true );

    request.responseType = "arraybuffer";

    request.onload = function() {
        var audioData = request.response;

        context.decodeAudioData(
            audioData,
            function( buffer ) {
                source.buffer = buffer;

                callback( source );
            },
            function( e ) {
                console.log( "Error with decoding audio data" + e.err );
            }
        );
    };

    request.send();
};

module.exports = function() {
    var AudioContext = window.AudioContext || window.webkitAudioContext,
        context = new AudioContext();

    getData(
        context,
        "path/to/short/file.wav",
        function( bufferSource ) {
            var gain = context.createGain();
            gain.gain.value = 0;
            bufferSource.connect( gain );
            gain.connect( context.destination );
            bufferSource.start( 0 );
        }
    );
};

显然,如果一些设备的采样率不同,您需要检测并使用特定的文件来适配每个采样率。

5

看起来iOS6+ Safari默认采样率为48000。如果你在首次打开移动版Safari时输入开发者控制台,你将获得48000:

var ctx = new window.webkitAudioContext();
console.log(ctx.sampleRate);

进一步参考:https://forums.developer.apple.com/thread/20677 如果您在加载时关闭初始上下文: ctx.close(),则下一个创建的上下文将使用大多数其他浏览器使用的采样率(44100),并且声音将播放而不会失真。
感谢以下内容为我指明了正确的方向(以防上述内容在未来无法使用):https://github.com/Jam3/ios-safe-audio-context/blob/master/index.js 截至发布日期的函数:
function createAudioContext (desiredSampleRate) {
  var AudioCtor = window.AudioContext || window.webkitAudioContext

  desiredSampleRate = typeof desiredSampleRate === 'number'
    ? desiredSampleRate
    : 44100
  var context = new AudioCtor()

  // Check if hack is necessary. Only occurs in iOS6+ devices
  // and only when you first boot the iPhone, or play a audio/video
  // with a different sample rate
  if (/(iPhone|iPad)/i.test(navigator.userAgent) &&
      context.sampleRate !== desiredSampleRate) {
    var buffer = context.createBuffer(1, 1, desiredSampleRate)
    var dummy = context.createBufferSource()
    dummy.buffer = buffer
    dummy.connect(context.destination)
    dummy.start(0)
    dummy.disconnect()

    context.close() // dispose old context
    context = new AudioCtor()
  }

  return context
}

虽然这个链接可能回答了问题,但最好包含答案的主要部分并提供链接作为参考。 仅有链接的答案如果链接的页面发生更改则无效。-【来自审查】 - Teepeemm
根据原始链接中的更详细说明和代码片段进行编辑 - Yining Chen
很遗憾,这个答案在iOS 12上对我没用。 - frodo2975

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接