解码音频数据返回 null 错误

18

我来这里是希望你们SO上的可爱人们能够帮助我解决一个问题。

具体来说,每当我尝试使用webkitAudioContext的decodeAudioData方法时,它总是触发一个空错误的错误处理程序。这是我目前正在使用的代码:

var soundArray;
var context = new webkitAudioContext();
function loadSound(soundName) {
    var request = new XMLHttpRequest();
    request.open('GET',soundName);
    request.responseType = 'arraybuffer';
    request.onload = function() {
            context.decodeAudioData(this.response, function(buf) {
                sounds[soundName] = buf;
            },function(err) { console.log("err(decodeAudioData): "+err); });
    }
    request.send();
}
在这一点上,它不断地向控制台记录错误消息,说“err(decodeAudioData)= null”,主要是因为这是我决定记录它的方式。无论如何,有什么想法可能正在发生这种情况吗?
我正在使用Chrome Canary,v20.0.1121.0尝试使某些东西工作。但是,显然,它不起作用!那么,你有什么想法我可能能做什么?如果需要任何新信息,请告诉我,我会更新必要的内容。

它是否适用于其他音频来源?根据我的经验,不是所有的音频文件都可以被API解码。你可能希望参考new.crbug.com。 - ebidel
1
我已经尝试使用OGG、MP3和WAV格式的音频文件,但都没有成功播放。我曾经读过一篇Chrome浏览器的错误报告,称可能是因为Chrome不喜欢播放只有一两秒钟长的音频文件,但我正在运行第二个项目,构建一个基于Web的音频播放器,使用拖放功能,而完整的3-4分钟歌曲也无法播放。我会尽力提交一个错误报告,感谢您的建议! - ozzmotik
这个问题已经被解决了,所以请忽略这个问题。 - Scott Stensland
你能提供一下修复的细节吗,Scott? - ejectamenta
2个回答

26

真正的原因是,现在createBuffer和decodeAudioData两者都存在一个Bug,会为它们本应播放的文件抛出奇怪模糊的DOM异常12。

但我们应该意识到这是新的且不断发展的技术,即使是当前的Web音频API也应该感恩,因为它对我们来说是一个小奇迹。

它们在头部边界上缺少流同步,任何合理的流式音频格式解码器都应该从这里开始。mp3或许多AAC/ADTS文件都是流式文件格式。流式意味着您可以在任何地方剪切它们或插入附加任何内容(各种标签甚至图像艺术作品),解码器不应关心未知数据。解码器只需寻找直到找到他知道并可以解码的标题。

我拼凑了这个临时解决方案,它会寻找最近的帧标题开始,并仅传递从此偏移量开始的数据。

mp3或mp2每个音频帧(大约200字节)都以0XFFE开头,而aac(adts)则以oxFFF同步字开头,就是为了这个目的而存在的。因此,两者都将在0xFFE上进行同步。

以下是我目前用于播放以前未播放的文件的代码。

我讨厌的是arrayBuffer没有像它的数据类型子类那样的subarray(),可以返回从不同偏移量的不同视图,而不是slice()返回的整个新数组副本。如果Web音频API接受类型化数组作为输入的唯一方法是通过巨大的slice()复制来创建arraybuffer。幸运的是,通常只需要一两次查找。

强制Web音频API对文件不挑剔

node={};
node.url='usual_mp3_with_tags_or_album_artwork.mp3';

function syncStream(node){ // should be done by api itself. and hopefully will.
    var buf8 = new Uint8Array(node.buf); 
    buf8.indexOf = Array.prototype.indexOf;
    var i=node.sync, b=buf8;
    while(1) {
        node.retry++;
        i=b.indexOf(0xFF,i); if(i==-1 || (b[i+1] & 0xE0 == 0xE0 )) break;
        i++;
    }
    if(i!=-1) {
        var tmp=node.buf.slice(i); //carefull there it returns copy
        delete(node.buf); node.buf=null;
        node.buf=tmp;
        node.sync=i;
        return true;
    }
    return false;
}

function decode(node) {
    try{
        context.decodeAudioData(node.buf,
        function(decoded){
            node.source  = context.createBufferSource();
            node.source.connect(context.destination);
            node.source.buffer=decoded; 
            node.source.noteOn(0);
        },
        function(){ // only on error attempt to sync on frame boundary
            if(syncStream(node)) decode(node);
        });
    } catch(e) {
        log('decode exception',e.message);
    }
}

function playSound(node) { 
    node.xhr = new XMLHttpRequest();
    node.xhr.onload=function(){  
        node.buf=node.xhr.response;
        node.sync=0;
        node.retry=0;
        decode(node);
    }
    node.xhr.open("GET", node.url, true); 
    node.xhr.responseType = "arraybuffer"; 
    node.xhr.send();
}

看到了这个。非常感谢Ladislav。显然,这个bug在iOS 8.3中仍然存在。 - Mathias
我正在使用麦克风作为ArrayBuffer的源。有时候decodeAudioData可以正常工作,有时候不行。这是一个间歇性的问题。在@Mathias的解释之后,我将寻找最近的起始标头。 - Raffael Bechara Rameh

5

我之前在Chrome 19中使用webkitAudioContext,今天升级到了Chrome 20后出现了与你相同的问题。

我尝试了另一个MP3文件,它可以正常播放。两个文件之间唯一的区别就是错误的MP3文件中嵌入了封面。

我移除了这个封面,现在可以正常播放了。


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