WEBブラウザ上で生成したメロディを再生する方法(WEB Audio API)
introduction
特定のフォーマットのファイルを準備せず,音を動的に生成して再生する
- 通常WEBブラウザ上で音を鳴らす時は,音楽ファイル(mp3やmidiなど)を準備して,それを再生する場合が殆ど.
- DTMのようなものをWEBブラウザ上で作りたい場合,すべての音階の音楽ファイルを用意するか,音を動的に生成できるようにするか.
javascriptで音を生成するには?
Web MIDI API について試したところ,OS標準のMIDIは反応しなかった.
なので,特に事前準備が少なそうなWeb Audio API を使うことにします.
参考URL
1
非常に分かりやすい日本語のチュートリアルサイトです.僕が追加で説明する必要は無いと思います.
weblike-curtaincall.ssl-lolipop.jp
2
いい感じに見やすく,リファレンスマニュアルとしてまとまっています
コーディング
仕組みについては,参考URL1を見てください.
音を鳴らす
たった5行で音が鳴ります.
const context = new AudioContext(); const oscillator = context.createOscillator(); // oscillator -> context.destination oscillator.connect(context.destination); oscillator.start(0); setTimeout(()=>{ oscillator.stop(0); }, 1000);
oscillator
という Node が正弦波を生成してcontext.destination
に流し込んでいる訳ですね.
最後の行を setTimeout(()=>{ oscillator.disconnect(); }, 1000);
に書き換えても音は止まる.
繋がっているコードを引き抜いているイメージでしょうか*1.
音を変える・同時に鳴らす
OscillatorNode
のプロパティについては https://www.javascripture.com/OscillatorNode が参考になります.
同時に2つの OscillatorNode
を鳴らすには,2つとも context.destination
に繋いでしまえば良さそう.
const context = new AudioContext(); const oscillator1 = context.createOscillator(); const oscillator2 = context.createOscillator(); oscillator1.connect(context.destination); oscillator2.connect(context.destination); oscillator1.type = 'square'; // note: effectiveFrequency = frequency * pow(2, detune / 1200) oscillator1.detune.value = 0; oscillator2.detune.value = 0; oscillator1.start(0); oscillator2.start(0); setTimeout(()=>{ oscillator1.stop(0); }, 2000); setTimeout(()=>{ oscillator2.stop(0); }, 2000); setInterval(()=>{ oscillator1.detune.value += 100; }, 200); setInterval(()=>{ oscillator2.frequency.value += 10; }, 20);
音の大きさを変える
音の大きさを変えるパラメータが無いなぁと思ったら,GainNode でコントロールするんですね.
つまり, OscillatorNode -> GainNode -> context.destination
と繋ぎます.
const context = new AudioContext(); const oscillator1 = context.createOscillator(); const gain = context.createGain(); gain.gain.value = 0.2; oscillator1.connect(gain); gain.connect(context.destination); oscillator1.type = 'square'; oscillator1.detune.value = 0; oscillator1.start(0); setTimeout(()=>{ oscillator1.stop(0); }, 2000);
delay
DelayNode
があったので,これを使ってみます.
ただし,OscillatorNode -> DelayNode -> context.destination
と繋げると本当にただ遅延して流れるだけ.
なので,反響しているかのように音を鳴らすには OscillatorNode -> context.destination
と OscillatorNode -> DelayNode -> context.destination
の2つのパスによる接続が必要.
下の例では,OscillatorNode -> GainNode[分岐点] -> [合流点]context.destination
と,GainNode[分岐点] -> DelayNode -> GainNode -> [合流点]context.destination
で繋いでいます.
const context = new AudioContext(); const oscillator1 = context.createOscillator(); const gain = context.createGain(); gain.gain.value = 0.5; const delay = context.createDelay(); delay.delayTime.value = 0.12; const delaygain = context.createGain(); delaygain.gain.value = 0.3; oscillator1.connect(gain); gain.connect(context.destination); gain.connect(delay); delay.connect(delaygain); delaygain.connect(context.destination); oscillator1.type = 'square'; oscillator1.detune.value = 0; oscillator1.start(0); setTimeout(()=>{ oscillator1.stop(0); }, 2000); setInterval(()=>{ oscillator1.detune.value += 100; }, 200);
感想
たのしい!!
caustic3 の modular っぽい.
CAUSTIC 3 | Single Cell Software
*1:このコードで試しても差は感じられないと思います.後半で説明しているdelayのサンプルではstopとdisconnectの違いを感じ取ることができるかもしれません.