Javascriptでドレミを再生

[「JavaScript で Beep 音を鳴らす方法」](http://qiita.com/isseium/items/12b215b6eab26acd2afe)を見て、Waveファイルを動的に作成してドレミを鳴らしてみました。

|IE9|IE10~|Chrome|FireFox|Safari|iOS<BR>(Safari)|
|:--:|:--:|:--:|:--:|:--:|:--:|
|×|×|○|○|○<BR>(遅い)|○<BR>(遅い)|

## 使い方

```
play(node, sec)
```

指定音階(ドレミ)を再生する

引数

* node
音階を指定(ド=48,ド#=49…)

* sec
長さを指定(秒)

```
stop()
```

停止する

## ソース

```js:波形を作って音を出すJavascript
<script type="text/javascript">
var audio;

//音階を再生
//node ノード(ド=48,ド#=49…)
//sec 再生秒数
function play(node,sec)
{
	stop();
    if(node>=96)return;

	Hz=11025;//サンプリングレート

	//Waveデータ
	var bytes=new Uint8Array(Math.floor(Hz*sec)+100);

	//Waveヘッダー作成
	var header="524946460000000057415645666D74201000000001000100112B0000112B0000010008006461746100000000";
	for(fp = 0; fp < header.length/2; fp++){
		bytes[fp]=parseInt(header.substr(fp*2,2),16);
	}

	//音階(ドド#レ…シ)の周波数(1オクターブ下がると1/2倍)
	var freqs=[4180, 4428, 4708, 4968, 5264, 5592, 5884, 6300, 6676, 6988, 7476, 7848];

	//1サンプルあたりの位相計算
	octave = Math.floor(node/12); //オクターブ
	freq = freqs[node%12] / (1<<(7-octave)); //周波数
	phase = 6.28 / (Hz / freq);

	//波形作成
	for (t = 0; t < Math.floor(Hz*sec); t++){
		bytes[fp++] = Math.floor(Math.sin(phase*t)*127)+128;
	}

	//データ補正
	setLittleEndian(bytes,4,fp-8);	//ファイルサイズ
	setLittleEndian(bytes,24,Hz);	//サンプリングレート
	setLittleEndian(bytes,40,fp-44);//波形サイズ

	//BASE64変換してオーディオ作成
	str="";
	for (i=0;i<fp;i++){str+=String.fromCharCode(bytes[i]);}
	audio=new Audio("data:audio/wav;base64,"+btoa(str));
	audio.play();
}

function stop()
{
	if (audio&&!audio.ended){audio.pause();audio.currentTime=0;}
}

function setLittleEndian(bytes,p,data)
{
	bytes[p] = (data & 0xFF);
	bytes[p+1] = ((data >> 8) & 0xFF);
	bytes[p+2] = ((data >> 16) & 0xFF);
	bytes[p+3] = ((data >> 24) & 0xFF);
}
</script>
```

```html:音階再生ボタン
<input type="button" value="ド" onclick="javascript:play(48,1)">
<input type="button" value="レ" onclick="javascript:play(50,1)">
<input type="button" value="ミ" onclick="javascript:play(52,1)">
<input type="button" value="ファ" onclick="javascript:play(53,1)">
<input type="button" value="ソ" onclick="javascript:play(55,1)">
<input type="button" value="ラ" onclick="javascript:play(57,1)">
<input type="button" value="シ" onclick="javascript:play(59,1)">
<input type="button" value="ド" onclick="javascript:play(60,1)">
<input type="button" value="停止" onclick="javascript:stop()">
```

<!--サンプル-->

<input type="button" id=C><input type="button" id=D><input type="button" id=E><input type="button" id=F><input type="button" id=G><input type="button" id=A><input type="button" id=B><input type="button" id=CC><input type="button" id=STOP>

<!--以下のブックマークレッド実行でお試し実行できます。音は大きいので気をつけて下さい。-->

<script type="text/javascript">
C.value='ド';
C.onclick=function(){play(48,1);};
D.value='レ';
D.onclick=function(){play(50,1);};
E.value='ミ';
E.onclick=function(){play(52,1);};
F.value='ファ';
F.onclick=function(){play(53,1);};
G.value='ソ';
G.onclick=function(){play(55,1);};
A.value='ラ';
A.onclick=function(){play(57,1);};
B.value='シ';
B.onclick=function(){play(59,1);};
CC.value='ド';
CC.onclick=function(){play(60,1);};
STOP.value='停止';
STOP.onclick=function(){stop();};
</script>

<div name='(function(){var audio;function play(node,sec){stop();if(node&gt;=96)return;Hz=11025;var bytes=new Uint8Array(Math.floor(Hz*sec)+100);var header="524946460000000057415645666D74201000000001000100112B0000112B0000010008006461746100000000";for(fp = 0; fp &lt; header.length/2; fp++){bytes[fp]=parseInt(header.substr(fp*2,2),16);}var freqs=[4180, 4428, 4708, 4968, 5264, 5592, 5884, 6300, 6676, 6988, 7476, 7848];octave = Math.floor(node/12);freq = freqs[node%12] / (1&lt;&lt;(7-octave));phase = 6.28 / (Hz / freq);for (t = 0; t &lt; Math.floor(Hz*sec); t++){bytes[fp++] = Math.floor(Math.sin(phase*t)*127)+128;}setLittleEndian(bytes,4,fp-8);setLittleEndian(bytes,24,Hz);setLittleEndian(bytes,40,fp-44);str="";for (i=0;i&lt;fp;i++){str+=String.fromCharCode(bytes[i]);}audio=new Audio("data:audio/wav;base64,"+btoa(str));audio.play();}function stop(){if (audio&&!audio.ended){audio.pause();audio.currentTime=0;}}function setLittleEndian(bytes,p,data){bytes[p] = (data & 0xFF);bytes[p+1] = ((data &gt;&gt; 8) & 0xFF);bytes[p+2] = ((data &gt;&gt; 16) & 0xFF);bytes[p+3] = ((data &gt;&gt; 24) & 0xFF);}C.value=&#39;ド&#39;;C.onclick=function(){play(48,1);};D.value=&#39;レ&#39;;D.onclick=function(){play(50,1);};E.value=&#39;ミ&#39;;E.onclick=function(){play(52,1);};F.value=&#39;ファ&#39;;F.onclick=function(){play(53,1);};G.value=&#39;ソ&#39;;G.onclick=function(){play(55,1);};A.value=&#39;ラ&#39;;A.onclick=function(){play(57,1);};B.value=&#39;シ&#39;;B.onclick=function(){play(59,1);};CC.value=&#39;ド&#39;;CC.onclick=function(){play(60,1);};STOP.value=&#39;停止&#39;;STOP.onclick=function(){stop();};})();' class='javascript' />

##参考
[JavaScript で Beep 音を鳴らす方法](http://qiita.com/isseium/items/12b215b6eab26acd2afe)
[音声処理：WAVEファイルのフォーマット](http://www.asahi-net.or.jp/~cv9t-sum/knowledge40.htm#WAVE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)