本当の理由は、createBufferとdecodeAudioDataの両方にバグがあり、通常再生するはずのファイルに対して奇妙な漠然としたDOM例外12をスローすることです。しかし、これは新しく進化しているテクノロジーであり、私たちに起こった小さな奇跡以来、現在のWebオーディオAPIにも感謝する必要があります。
それらは、ストリーミングオーディオフォーマットの合理的なデコーダーが開始すべきヘッダー境界でのストリーム同期を欠いています。そしてmp3または多くのaac/adtsファイルはストリーミングファイルフォーマットです。ストリーミングとは、どこにでも切り取ったり、何かを追加したりできることを意味します(画像アートワークでもさまざまなタグ)デコーダーは未知のデータを気にする必要はありません。デコーダーは、自分が知っていてデコードできるヘッダーが見つかるまでシークする必要があります。
最も近いフレームヘッダーの開始を探し、このオフセットからのデータのみを渡すこの一時的なソリューションをまとめました。
mp3またはmp2はすべて、この理由だけで存在するoxFFF同期ワード上の0XFFEとaac(adts)を使用して、すべてのオーディオフレーム(約200バイトごと)のヘッダーを開始します。したがって、両方とも0xFFEで同期します。これは、以前に再生されなかったファイルを再生するために現在使用しているコードです。
私が嫌うのは、arrayBufferには、slice()が返すまったく新しい配列コピーではなく、異なるオフセットから異なるビューを返す、型指定された子のようなsubarray()がないことです。webaudio apiだけがtypedarraysを入力として受け入れたが、残念ながら、arraybufferを作成する唯一の方法は巨大なslice()コピーのようです。ありがたいことに、通常は1つか2つのシークだけが必要です。
WebAudioApiにファイルについてうるさくないように強制する
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();
}