LoginSignup
24
21

More than 5 years have passed since last update.

WebAudio で簡単シンセ

Last updated at Posted at 2015-06-10

WebAudioとは

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

情報リソース

とりあえずデモ

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

実働デモ

全ソース(gist) (要jQuery)
音を鳴らしている部分は実質30行ほどでできちゃう。あとはUIの処理。

ポイント解説

モジュール構成

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

Untitled.png

初期化

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は楽しい

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

24
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
21