Web Audio APIを使って、ブラウザ上でリアルタイムの音声波形を表示したり、マイク入力を取得する処理を実装する際、以下のような警告やエラーに遭遇することがあります。
The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
コンソールを確認すると、AudioContext.state が "suspended"(一時停止状態)のままになっており、音声処理が正しく開始されません。
これは、Google Chromeなどのモダンブラウザに実装されている**「自動再生ポリシー(Autoplay Policy)」**が原因です。ユーザーの予期しない大音量の再生を防ぐため、画面に対するクリックやタップなどの「ユーザーの操作(User Gesture)」がないと、音声の再生やキャプチャがブロックされる仕組みになっています。
本記事では、この問題を回避するためのシンプルな実装パターンを共有します。
解決策:ユーザーアクション時に resume() を実行する
最も確実な方法は、音声処理を開始するボタンの click イベントハンドラー内で、AudioContext を初期化するか、すでに作成済みの場合は resume() を呼び出すことです。
以下は、ReactやVanilla JSで使える一般的な実装例です。
// AudioContextの生成(この時点では suspended 状態になる可能性がある)
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const startAudioProcess = async () => {
// ブラウザの自動再生ポリシーを回避するため、ユーザー操作のコンテキスト内で resume() を実行
if (audioCtx.state === 'suspended') {
await audioCtx.resume();
console.log('AudioContextがアクティブになりました。現在の状態:', audioCtx.state);
}
// ここからマイク入力や音声解析の処理を開始する
setupMicrophone(audioCtx);
};
// HTML上のスタートボタンなどにイベントをバインド
document.getElementById('start-btn').addEventListener('click', startAudioProcess);
ページのどこをクリックしても自動復帰させるパターン
ユーザーに特定の「スタートボタン」を押させず、ページ全体のどこかを最初にクリックしたタイミングで自動的にサスペンド状態を解除したい場合は、以下のようなグローバルなイベントリスナーを設定するのも有効です。
JavaScript
const handleFirstClick = () => {
if (audioCtx.state === 'suspended') {
audioCtx.resume().then(() => {
console.log('ユーザー操作を検知し、AudioContextを復帰させました。');
// 一度アクティブになればリスナーは不要なので削除
window.removeEventListener('click', handleFirstClick);
});
}
};
window.addEventListener('click', handleFirstClick);
まとめ
AudioContext はブラウザ読み込み時ではなく、必ずユーザーのクリック等のイベント発火後にアクティブ(running)にする必要がある。
安全に実装するには、audioCtx.state === 'suspended' の判定を入れて await audioCtx.resume() を実行する。
音声AIのフロントエンド実装や、WebRTCを用いた通話ツールの開発で最初に踏みがちな仕様なので、備忘録として残しておきます。