javascriptで音を出す

javascriptで波形を作ってバッファーに突っ込み音を出す際の注意事項。

まずjavascriptにはいろんな音の出し方があるんだよというのが、以下にうまくまとまっている。

つまみ食う

次にjavascriptで音を扱えるようになるとどんなことが出来るようになるの?というのは以下のサンプルを見るとわかりやすい。

強火で進め

これらを踏まえた上で、実装する上での注意点をまとめる。

Chrome: WebAudioAPI

以下をDeveloperToolsのコンソールにコピペして実行すれば音がなる。

(function(){
	var cnt = 0, length = 2048;
	var ctx = new webkitAudioContext();
	var node = ctx['createJavaScriptNode'](length, 1, 2);
	node['onaudioprocess'] = function(e){
		var data0 = e['outputBuffer']['getChannelData'](0), 
			data1 = e['outputBuffer']['getChannelData'](1);
		
		var val;
		for( var i=0; i<length; i++ ){
			val = Math.sin(cnt++/10)
			data0[i] = val;
			data1[i] = val;
		}
	}
	node['connect'](ctx['destination']);
})();
  • コールバック関数(onaudioprocess)が適当なタイミング呼ばれるので、javascript側では波形を生成することに専念できる。実装が楽。
  • onaudioprocessはなぜかalert関数で処理をブロックした状態でも呼ばれている。どうなっているのか謎。
  • 上記nodeを作ってしばらく放っていると、CPU使用率が20%台でずっと推移することがある(Win7,64bit/Chrome18)
  • sampleRateをセットできないため、デフォルトの48000に波形もあわせなければいけない。
  • 上記サンプルにはないが、周波数フィルタリング機能などもありちょっとしたシンセサイザーなどをサクッと作れそう。

FireFox: AudioDataAPI

以下をFirebugのコンソールにコピペして実行すれば音がなる。

(function(){
	var cnt = 0, 
		buffersize = 24000, 
		maxBuffersize=8192,
		currentWritePosition = 0, 
		totaltail, tail, written;
	var audio = new Audio();
	audio['mozSetup'](2, 48000);
	setInterval(function(){
		if( tail ){
			totaltail+=tail.length;
			written = audio['mozWriteAudio'](tail);
			currentWritePosition += written;
			tail = null
		}
		var available = audio['mozCurrentSampleOffset']() + buffersize - currentWritePosition;

		if( available>0 ){
			var readLength = available>=maxBuffersize ? maxBuffersize : available;
			var soundData = new Float32Array(readLength);
			for( i=0; i<readLength; i+=2 ){
				var v = Math.sin(cnt++/10);
				soundData[i] = v;
				soundData[i+1] = v;
			}
			written = audio['mozWriteAudio'](soundData);
			if( written < readLength ){
				tail = soundData.subarray(written);
			}
			currentWritePosition += written;
		}
		
	}, 50);
})();
  • WebAudioAPIと比べるとコードの量が多くなっているし、チューニングするべきパラメータの数が多い。
  • setTimeoutで好きなときにバッファーをセットできるのが良いのかもしれないが、どっちかというと不便。
  • CPU使用率問題や、sampleRate問題はないのでその点は素性が良い。

まとめ

WebAudioAPIもAudioDataAPIも発展途上で、クロスブラウザでの実装を考慮に入れるとお互いが足を引っ張り合ってしまう。早く仕様の統一される日が来てほしいが、FileAPIとかWebSocketの方が目を引く話題だし後回しにされそうな予感がする。うーん、切ないなぁ、、

IEは仕様が統一されてしばらくたってから実装するとか、たぶん石橋たたいて渡ってくるんだろう。