FlashのSoundクラスではAAC形式のサウンドを扱えません(NetStreamでは扱えます)。これで何が困るかというと、extractメソッドによるPCMデータ取得ができないのです。Any Beatsではこの問題に随分と悩まされてきました。(普段M4Aを使ってる人たちから音楽ファイルが表示されないぜ的なクレームとか)
そこで、色々悩んだ結果、ブラウザのWeb Audio APIを使う方法を思いつきました。Web Audio APIをJSから叩くと、Chromeやsafari(Mac)ではAACを扱えます。ChromeではOGGも使えます。JSとAS3のデータの受け渡しが非常に重いのが難ですが。。。
以下抜粋。(JSのBase64部分はご自身で補完してください)
JS
var decodeIntervalID = 0;
var audioContext;
if (typeof AudioContext != "undefined") audioContext = new AudioContext();
else if (typeof webkitAudioContext != "undefined") audioContext = new webkitAudioContext();
if(audioContext){
decodeSound = function(base64){
if(decodeIntervalID > 0) clearInterval(decodeIntervalID);
var swf = getSWF("***");
if(!swf) return false;
var bytes = Base64Binary.decodeArrayBuffer(base64);
base64 = null;
var sendFunc = function(buffer) {
try{
var isStereo = buffer.numberOfChannels >= 2;
var leftArray = buffer.getChannelData(0);
var rightArray;
if(isStereo) rightArray = buffer.getChannelData(1);
var i = 0;
var len = leftArray.length;
var sampleRate = buffer.sampleRate > 44100 ? 44100 : buffer.sampleRate;
var downSamplingStep = -1;
var downSampling = 1;
if(buffer.sampleRate > 44100){
downSamplingStep = (1/((buffer.sampleRate-44100)/buffer.sampleRate));
downSampling = 44100 / buffer.sampleRate;
}
swf.decodeSoundStart(len);
var nextSkip = downSamplingStep;
var nextSkipInt = Math.floor(nextSkip);
decodeIntervalID = setInterval(function(){
if(i >= len){
clearInterval(decodeIntervalID);
swf.decodeSoundComplete(sampleRate, buffer.numberOfChannels);
decodeIntervalID = 0;
buffer = leftArray = rightArray = null;
return;
}
var resultBuffer = new ArrayBuffer(100000 * 4);
var result = new Float32Array(resultBuffer);
if(isStereo){
for(j = 0; i < len, j < 100000; i++){
if(i == nextSkipInt){
nextSkip += downSamplingStep;
nextSkipInt = Math.floor(nextSkip);
continue;
}
result[j++] = leftArray[i] * downSampling;
result[j++] = rightArray[i] * downSampling;
}
}
else{
for(j = 0; i < len, j < 100000; i++){
if(i == nextSkipInt){
nextSkip += downSamplingStep;
nextSkipInt = Math.floor(nextSkip);
continue;
}
result[j++] = leftArray[i] * downSampling;
}
}
swf.decodeSoundAdd(base64ArrayBuffer(resultBuffer));
}, 50);
}
catch(e){ swf.decodeSoundError(); }
};
if(audioContext.decodeAudioData) {
audioContext.decodeAudioData(bytes, sendFunc, function(e){
swf.decodeSoundError();
});
}
else{
try{
var b = audioContext.createBuffer(bytes, false);
sendFunc(b);
}
catch(e){ alert(e);swf.decodeSoundError(); }
}
bytes = null;
return true;
};
}
function getSWF(swf_name) {
if (navigator.appName.indexOf("Microsoft") != -1) {
return window[swf_name]
}
else {
return document[swf_name]
}
}
AS3
ExternalInterface.addCallback("decodeSoundStart", _decodeSoundStart);
ExternalInterface.addCallback("decodeSoundAdd", _decodeSoundAdd);
ExternalInterface.addCallback("decodeSoundComplete", _decodeSoundComplete);
ExternalInterface.addCallback("decodeSoundError", _decodeSoundError);
private var _pcmBytes:ByteArray;
private function _decodeSoundStart(len:uint):void {
if (_pcmBytes) _pcmBytes.clear();
else _pcmBytes = new ByteArray();
_pcmBytes.endian = Endian.LITTLE_ENDIAN;
}
private function _decodeSoundAdd(base64:String):void {
var i:int;
var bytes:ByteArray = Base64.decodeToByteArray(base64);
var len:uint = bytes.length / 4;
if (len > 0) {
bytes.endian = Endian.LITTLE_ENDIAN;
bytes.position = 0;
for (i = 0; i < len; i++) {
_pcmBytes.writeShort(bytes.readFloat() * 0x7fff);
}
}
}
private function _decodeSoundComplete(rate:uint, channels:uint):void {
_pcmBytes.position = 0;
var bits:uint = 16;
var wave:ByteArray = new ByteArray();
wave.endian = Endian.LITTLE_ENDIAN;
wave.writeUTFBytes("RIFF");
wave.writeInt( uint( _pcmBytes.length + 44 ) );
wave.writeUTFBytes("WAVE");
wave.writeUTFBytes("fmt ");
wave.writeInt( uint( 16 ) );
wave.writeShort( uint( 1 ) );
wave.writeShort( channels );
wave.writeInt( rate );
wave.writeInt( uint( rate * channels * ( bits >> 3 ) ) );
wave.writeShort( uint( channels * ( bits >> 3 ) ) );
wave.writeShort( bits );
wave.writeUTFBytes("data");
wave.writeInt( _pcmBytes.length );
wave.writeBytes( _pcmBytes );
wave.position = 0;
// use wave
}
private function _decodeSoundError():void
{
// error
}
このコードはAny Beatsで実際に使用しています。もっと効率の良い方法があれば教えて欲しいです><