WebAudio で簡単シンセ

#WebAudioとは
ブラウザで音を扱えるAPI。音のデータの再生や加工のほか、オシレータの音源やフィルタをもっているので、シンセサイザみたいなものが簡単に作れる。
Chrome、Safari(iOSもOK)とFirefoxで動作。
ここではシンプルな構成のアナログシンセの音源を作ってみる。

#情報リソース

- [W3Cの仕様書最新ドラフト](http://webaudio.github.io/web-audio-api/)
- [仕様書日本語訳](http://g200kg.github.io/web-audio-api-ja/)(ちょっと古い)
- [日本語解説](http://www.g200kg.com/jp/docs/webaudio/) まずはこれをひと通り読むのがいい

#とりあえずデモ

OSC-LPF-EV+LFOの簡単な構成のアナログシンセ音源。
CONTボタンで連続音。TRIGボタンでEG付きの音、SEQでシーケンスを刻む。あとはスライダーをいろいろいじってみてください。

[実働デモ](http://wakufactory.jp/html5/webaudio/t1.html)

[全ソース(gist)](https://gist.github.com/wakufactory/1aba127a5d5773bc3c56) (要jQuery)
音を鳴らしている部分は実質30行ほどでできちゃう。あとはUIの処理。



#ポイント解説

##モジュール構成
OSCとLFOは同じオシレータモジュール。エンベロープとパラメータの値調整にゲインモジュールを使っている。

![Untitled.png](https://qiita-image-store.s3.amazonaws.com/0/74838/2ab21907-ea66-282b-c632-0f18936c25b8.png)


##初期化
WebAudioのコンテキストを取得。Safariはwebkitのプリフィックスが必要。

```javascript
var AC = window.AudioContext ||window.webkitAudioContext;
var ctx = new AC() ;
```

##モジュールの生成
オシレータやフィルタのモジュールがAudioNodeというオブジェクトになっている。createメソッドを呼び出して生成。パラメータの設定等はこのオブジェクトに対して行う。

```javascript
var vco0 = ctx.createOscillator() ;	//オシレータ
var f0 = ctx.createBiquadFilter() ;	//フィルタ
var e0 = ctx.createGain() ;			//エンベロープ用ゲイン
var g0 = ctx.createGain() ;			//出力用アンプ
```

##ノードの接続
アナログシンセのパッチングのように、モジュールを接続していく。オブジェクトにconnectすると音声データの接続、パラメータにconnectするとCV(電圧じゃないけど)として扱われる。
最後はctx.destinationに接続すると音が出る。

```javascript
vco0.connect(f0) ;	//オシレータからフィルタへ
f0.connect(e0) ;	//フィルタからエンベロープへ
e0.connect(a0) ;	//エンベロープからアナライザへ
a0.connect(g0) ;	//アナライザから出力アンプへ
l0.connect(l0o) ;	//LFOからオシレータのゲインへ
l0o.connect(vco0.frequency) ;	//オシレータゲインからオシレータの周波数へ
g0.connect(ctx.destination) ;	//出力アンプを最終出力へ
```

##発音開始
```javascript
vco0.start(0) ;
```
で音が鳴り始める。引数の0は今すぐ。WebAudoのコンテキスト生成時からの相対時間を指定できるので、正確なシーケンス処理にはこの時間指定で行う。
ここの例では、最初になりっぱなしにして、EGアンプのゲインのコントロールで発音をon/offしている。stop()で音を止めることができるが、オシレータモジュールは使い捨てで再びstart()することはできないので注意。
モバイルデバイスでは、このstart()は、ユーザアクション(ボタンのonclick等)の中で呼ばないと音が鳴らない。

##エンベロープ
モジュールのパラメータ(AudioParamオブジェクト)には、時間指定して値を変化させる機能がある。ここではAttack,Decayのみの単発音を鳴らすために、このオートメーション機能を使っている。

```javascript
var now=ctx.currentTime;	//WebAudioの持ってるタイマーの時間
var attack=0.01, decay=0.1, sustain=0, 
var maxvalue=1.0; // Attackの目標値を1.0
e0.gain.cancelScheduledValues(0);  // スケジュールを全て解除
e0.gain.setValueAtTime(0.0, now);  // 開始時間にgainを0に
e0.gain.linearRampToValueAtTime(maxvalue, now + attack); //AttackタイムかけてrootValueまで遷移  
e0.gain.linearRampToValueAtTime(sustain * maxvalue, now + attack + decay);  //decayタイムでsustain
```

WebAudioの持ってるタイマーの時間(秒単位)を指定して、パラメータの値を連続で変化させることができる。上の例では、attack秒かけてgainを0から1に変化させ、その後decay秒で0まで戻す指定をしているということになる。


##LFO
LFOは普通のオシレータの出力を、モジュールのパラメータに接続してモジュレーションを掛けている。
オシレータの出力値は-1から1の範囲で、パラメータの値は単純に加算されるだけなので、接続先のパラメータ種類によって値を調整する必要がある。そのためにgainモジュールを間にかませるている。

##シーケンス
このデモでは、シーケンス、といっても同じ音を等間隔で出すだけだが、は手抜きしてjavascriptのIntervalを使っている。Intervalは正確には刻まない場合が多いので、音楽のシーケンス制御には向いていいない。WebAudioの持っているタイマーで制御する必要があるだろう。

##アナライザ
主にビジュアライザ用途として、音声信号をデータで取り出せるAnalyserモジュールがある。波形と周波数をUint8Arrayに取り出せるので、canvasを使ってプロットしてるだけ。

```javascript
	var can = $('#c_wave').get(0).getContext('2d') ;
	can.strokeStyle="#fff" ;
	can.fillStyle="#486";
	var ad = new Uint8Array(512) ;
	setInterval(function() {
		a0.getByteTimeDomainData(ad) ;	//波形取得
		can.fillRect(0,0,512,256);
		can.beginPath();
		can.moveTo(0,128);
		for(i=0;i<256;i++) {
			can.lineTo(i,256-ad[i*2]);
		}
		can.stroke() ;
	},100);	
```

#WebAudioは楽しい

以上のように、簡単に音源モジュールを組み合わせて音をシンセサイズすることができる。簡単な効果音から、従来のシンセエミュレータではできなような複雑なパッチングまで、応用範囲は広いと思うので、みなさん遊んでみましょう。