真正的原因是,现在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){
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);
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(){
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();
}