AudioContext と GainNode の基本
Web Audio API でブラウザ上の音を扱うとき、最初に出会うのが AudioContext と GainNode です。この2つを理解することは、シンセサイザー・音楽プレイヤー・効果音処理など、すべての音声機能の出発点になります。この記事では、最小限のコードでこの2つの基本を解説します。
AudioContext とは何か
AudioContext は「音声処理の作業場」のような存在です。すべての音声ノード(音源・加工・出力)はこのコンテキストの中に作られ、つながり合って動作します。1ページに通常1つだけ作るのが基本です。
const audioCtx = new AudioContext();
console.log(audioCtx.sampleRate); // 44100 もしくは 48000
sampleRate は1秒間に何回サンプルを生成するかという数値で、ブラウザやデバイスによって 44.1kHz か 48kHz が一般的です。
オートプレイ制限への対処
2018年以降、ほとんどのブラウザは「ユーザー操作なしでの音声再生」をブロックします。AudioContext は最初、suspended 状態で生成されるため、明示的に resume() しないと音が出ません。
button.addEventListener('click', async () => {
if (audioCtx.state === 'suspended') {
await audioCtx.resume();
}
// ここから音を鳴らす処理
});
「再生ボタンが押されたら resume する」という流れが、Web Audio アプリの定型パターンです。
GainNode で音量を操る
GainNode はその名のとおり「ゲイン(音量倍率)」を担当するノードです。値は通常 0〜1 の範囲(1で原音そのまま、0で無音)です。
const gain = audioCtx.createGain();
gain.gain.value = 0.5; // 50%の音量
注意点として、ノードを destination に接続するまで音は出ません。audioCtx.destination がスピーカー出力にあたります。
ノードを接続する
Web Audio はノードグラフ(有向グラフ)です。音源 → 加工 → 出力という流れで connect() を使って接続します。
const osc = audioCtx.createOscillator();
osc.frequency.value = 440; // A4
osc.connect(gain).connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 1); // 1秒後に停止
このコードを実行すると、440Hz のサイン波が音量50%で1秒間鳴ります。connect() をチェーンできるのが便利です。
滑らかな音量変化
gain.gain.value = 0.5 のように直接代入することもできますが、急な音量変化は「プチッ」というノイズを発生させることがあります。これを避けるには、専用のオートメーションメソッドを使います。
// 0秒で0、0.1秒かけて1.0までフェードイン
gain.gain.setValueAtTime(0, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 0.1);
主な選択肢は以下の3つです:
setValueAtTime(value, time): 指定時刻に瞬時に変更linearRampToValueAtTime(value, time): 直線的に変化exponentialRampToValueAtTime(value, time): 指数的に変化(人間の聴覚に自然)
複数の音源をミックスする
GainNode は「合流点」として使うこともできます。複数の音源を1つの GainNode に接続すれば、合算した音量で出力されます。
const masterGain = audioCtx.createGain();
masterGain.gain.value = 0.7;
masterGain.connect(audioCtx.destination);
[osc1, osc2, osc3].forEach(o => o.connect(masterGain));
シンセサイザーやDAW的なアプリでは、楽器ごとに個別のGainを持ち、最後にマスターGainで束ねる構造が定番です。
注意したい落とし穴
1. AudioContext を毎回作らない
クリックのたびに new AudioContext() を作ると、リソースが解放されないまま増えていきます。1ページ1つを使い回しましょう。
2. start() は1回しか呼べない
OscillatorNode は使い捨てのノードです。start() を呼んだ後に再生する場合は、新しいオシレータを作り直す必要があります。
3. 完了したノードは自動で解放されない
ノード自体はガベージコレクタに任せますが、長時間動くアプリでは disconnect() を明示的に呼ぶと安全です。
動作確認用の最小コード
<button id="play">再生</button>
<script>
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
document.getElementById('play').addEventListener('click', async () => {
if (audioCtx.state === 'suspended') await audioCtx.resume();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.frequency.value = 440;
gain.gain.setValueAtTime(0, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(0.3, audioCtx.currentTime + 0.05);
gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.5);
osc.connect(gain).connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.5);
});
</script>
このコードをHTMLファイルに保存してブラウザで開けば、ボタンを押すたびに440Hzのビープが0.5秒間鳴ります。Web Audio API の入口として、まずはこの「音を鳴らせた」体験を踏むのが大事です。
次のステップ
AudioContext と GainNode の基本が分かれば、次は音作りに進めます。オシレータの波形を変えると音色がどう変わるのか、フィルタで質感をどう作るのか — Web Audio の世界はここから広がっていきます。