#WebAudioとは
ブラウザで音を扱えるAPI。音のデータの再生や加工のほか、オシレータの音源やフィルタをもっているので、シンセサイザみたいなものが簡単に作れる。
Chrome、Safari(iOSもOK)とFirefoxで動作。
ここではシンプルな構成のアナログシンセの音源を作ってみる。
#情報リソース
- W3Cの仕様書最新ドラフト
- 仕様書日本語訳(ちょっと古い)
- 日本語解説 まずはこれをひと通り読むのがいい
#とりあえずデモ
OSC-LPF-EV+LFOの簡単な構成のアナログシンセ音源。
CONTボタンで連続音。TRIGボタンでEG付きの音、SEQでシーケンスを刻む。あとはスライダーをいろいろいじってみてください。
全ソース(gist) (要jQuery)
音を鳴らしている部分は実質30行ほどでできちゃう。あとはUIの処理。
#ポイント解説
##モジュール構成
OSCとLFOは同じオシレータモジュール。エンベロープとパラメータの値調整にゲインモジュールを使っている。
##初期化
WebAudioのコンテキストを取得。Safariはwebkitのプリフィックスが必要。
var AC = window.AudioContext ||window.webkitAudioContext;
var ctx = new AC() ;
##モジュールの生成
オシレータやフィルタのモジュールがAudioNodeというオブジェクトになっている。createメソッドを呼び出して生成。パラメータの設定等はこのオブジェクトに対して行う。
var vco0 = ctx.createOscillator() ; //オシレータ
var f0 = ctx.createBiquadFilter() ; //フィルタ
var e0 = ctx.createGain() ; //エンベロープ用ゲイン
var g0 = ctx.createGain() ; //出力用アンプ
##ノードの接続
アナログシンセのパッチングのように、モジュールを接続していく。オブジェクトにconnectすると音声データの接続、パラメータにconnectするとCV(電圧じゃないけど)として扱われる。
最後はctx.destinationに接続すると音が出る。
vco0.connect(f0) ; //オシレータからフィルタへ
f0.connect(e0) ; //フィルタからエンベロープへ
e0.connect(a0) ; //エンベロープからアナライザへ
a0.connect(g0) ; //アナライザから出力アンプへ
l0.connect(l0o) ; //LFOからオシレータのゲインへ
l0o.connect(vco0.frequency) ; //オシレータゲインからオシレータの周波数へ
g0.connect(ctx.destination) ; //出力アンプを最終出力へ
##発音開始
vco0.start(0) ;
で音が鳴り始める。引数の0は今すぐ。WebAudoのコンテキスト生成時からの相対時間を指定できるので、正確なシーケンス処理にはこの時間指定で行う。
ここの例では、最初になりっぱなしにして、EGアンプのゲインのコントロールで発音をon/offしている。stop()で音を止めることができるが、オシレータモジュールは使い捨てで再びstart()することはできないので注意。
モバイルデバイスでは、このstart()は、ユーザアクション(ボタンのonclick等)の中で呼ばないと音が鳴らない。
##エンベロープ
モジュールのパラメータ(AudioParamオブジェクト)には、時間指定して値を変化させる機能がある。ここではAttack,Decayのみの単発音を鳴らすために、このオートメーション機能を使っている。
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を使ってプロットしてるだけ。
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は楽しい
以上のように、簡単に音源モジュールを組み合わせて音をシンセサイズすることができる。簡単な効果音から、従来のシンセエミュレータではできなような複雑なパッチングまで、応用範囲は広いと思うので、みなさん遊んでみましょう。