Web Audio API を使ったブラウザでの音声録音
多くの開発者は Web Audio API が録音を担うものだと思い込んでいます。しかし、直接的にはそうではありません。Web Audio API は処理とルーティングのエンジンです。実際の録音作業は MediaRecorder API が担当します。この区別を理解することが、誤った実装を避ける最短の道です。
本記事では、ブラウザで音声録音を行うために現在推奨されているアーキテクチャを順を追って解説します。具体的には、getUserMedia() によるマイク入力のキャプチャ、必要に応じた Web Audio グラフ経由でのルーティング、そして MediaRecorder によるエンコードです。
重要なポイント
- Web Audio API は音声を処理・ルーティングするものであり、実際にファイルに録音するのは
MediaRecorderです。 - 完全な録音パイプラインは 3 段階で構成されます。
getUserMedia()によるキャプチャ、Web Audio ノードによる任意の処理、そしてMediaRecorderによるエンコードです。 getUserMedia()には安全なコンテキスト(HTTPS または localhost)が必要であり、ユーザーからの明示的なマイク許可が求められます。- カスタム DSP 処理には
AudioWorkletを使用してください。ScriptProcessorNodeは非推奨であり、負荷時にグリッチを発生させます。 - Safari と Chrome ではサポートされるフォーマットが異なるため、録音前に必ず
MediaRecorder.isTypeSupported()で MIME タイプのサポート状況を確認してください。
ブラウザでの音声録音の実際の仕組み
現代の録音パイプラインは、明確に区別される 3 つの段階で構成されます。
- キャプチャ —
getUserMedia()がマイクへのアクセスを要求し、MediaStreamを返します。 - 処理(任意) — Web Audio API がオーディオノードを通じてストリームを検査または変換します。
- 録音 —
MediaRecorderがストリームをファイルにエンコードします。
基本的な録音には Web Audio API は不要です。しかし、ノイズフィルタリング、ゲイン制御、可視化、あるいは AudioWorklet によるカスタムエフェクトが必要な場合は、このチェーンの中間にきれいに組み込めます。
細かいながらも重要な点として、MediaRecorder は MediaStream を直接録音します。Web Audio グラフの処理済み出力を録音したい場合は、audioContext.createMediaStreamDestination() を使ってグラフをストリームに戻し、そのノードの .stream を MediaRecorder に渡す必要があります。
ステップ 1: getUserMedia でマイクアクセスを要求する
getUserMedia() には、ブラウザがマイクアクセスを許可する前提として、安全なコンテキスト(HTTPS または localhost)と明示的なユーザー許可が必要です。
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: 44100
}
});
} catch (err) {
console.error('Microphone access failed:', err.name, err.message);
}
これは必ず try/catch で囲んでください。ユーザーが許可を拒否することもあり、また一部の環境(HTTP ページなど)では NotAllowedError や SecurityError で呼び出しが直接拒否されます。なお、sampleRate のような制約はあくまでヒントであり、基盤となるハードウェアによってはブラウザがそれを無視することがあります。
ステップ 2: Web Audio API 経由でルーティングする(任意)
録音前に音声を解析または処理する必要がある場合は、AudioContext を作成してストリームをそこに通します。
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
// 例: 可視化のために AnalyserNode に接続
const analyser = audioContext.createAnalyser();
source.connect(analyser);
// 処理済み出力を録音するには MediaStreamDestination にルーティング
const destination = audioContext.createMediaStreamDestination();
analyser.connect(destination);
// destination.stream を MediaRecorder に渡す
ノイズゲートやリアルタイムレベルメーターのようなカスタム処理には、非推奨の ScriptProcessorNode ではなく AudioWorklet を使用してください。AudioWorklet はメインスレッド外で実行されるため、UI をブロックしたり、負荷時にオーディオサンプルを取りこぼすことがありません。
注意: iOS Safari では、
AudioContextは直接的なユーザー操作によってトリガーされるまで suspended 状態で起動します。ページ読み込み時ではなく、ボタンクリックハンドラー内で作成(またはaudioContext.resume()を呼び出す)してください。
Discover how at OpenReplay.com.
ステップ 3: MediaRecorder で録音とエクスポートを行う
MediaRecorder は MediaStream を受け取ってエンコードします。MIME タイプはハードコードせず、まずサポート状況を確認してください。
function pickMimeType() {
const candidates = [
'audio/webm;codecs=opus',
'audio/ogg;codecs=opus',
'audio/mp4'
];
return candidates.find(type => MediaRecorder.isTypeSupported(type)) || '';
}
const mimeType = pickMimeType();
const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
const chunks = [];
recorder.ondataavailable = (e) => {
if (e.data.size > 0) chunks.push(e.data);
};
recorder.onstop = () => {
const blob = new Blob(chunks, {
type: recorder.mimeType || mimeType || chunks[0]?.type || 'audio/webm'
});
const url = URL.createObjectURL(blob);
document.querySelector('audio').src = url;
};
recorder.start();
WebM/Opus は、現代的なブラウザにおいて通常最良のデフォルトです。ファイルサイズが小さく、音質も優れています。Safari はしばしば audio/mp4 を好みます。候補タイプのリストをフォールバックさせ、ブラウザが対応する最初のものを選ぶ方式が、最も信頼性の高い戦略です。MediaRecorder および関連フォーマットの現在のブラウザサポート状況は Can I Use で確認できます。
MediaRecorder と Web Audio API: クイックリファレンス
| 必要な機能 | 使用するもの |
|---|---|
| シンプルなマイク録音 | getUserMedia + MediaRecorder |
| リアルタイムのエフェクトやフィルター | Web Audio API ノード |
| カスタム DSP 処理 | AudioWorklet |
| 音声の可視化 | AnalyserNode |
| ファイルへのエンコード | MediaRecorder |
避けるべきこと
ScriptProcessorNode— 非推奨で、メインスレッドで動作し、負荷時にオーディオグリッチを引き起こします。- MIME タイプのハードコード — Safari や古い Firefox で気付かないうちに失敗します。
- ページ読み込み時の
AudioContext作成 — ブラウザはユーザー操作が発生するまで suspend するため、イベントハンドラー内で resume してください。 - ストリーム停止の忘れ — 終了時に
stream.getTracks().forEach(t => t.stop())を呼び出さないと、マイクのインジケーターが点灯したままになります。
まとめ
ブラウザでの音声録音は 2 つの API で完結します。getUserMedia() と MediaRecorder がキャプチャとエンコードを担当し、Web Audio API はその間のすべてを担います。要件を満たす最もシンプルなパイプラインから始め、必要なときだけ Web Audio 処理を追加し、MediaRecorder を構成する前には常に MIME タイプのサポートを確認してください。アーキテクチャ全体を一文に凝縮するなら、これに尽きます。
FAQ
いいえ。基本的な録音には getUserMedia() と MediaRecorder で十分です。Web Audio API が必要になるのは、フィルターの適用、可視化の構築、AudioWorklet を介したカスタム DSP の実行など、録音前に音声を処理・解析・変換する必要がある場合のみです。
Safari と Chrome ではサポートされる録音フォーマットが異なります。Safari は audio/mp4 を好み、Chrome は WebM/Opus をよく使用します。サポートされていない MIME タイプをハードコードすると、MediaRecorder がエラーを投げたり、使用不能な出力を生成することがあります。実行時に互換フォーマットを検出するため、必ず MediaRecorder.isTypeSupported() を使用してください。
audioContext.createMediaStreamDestination() を使って MediaStreamAudioDestinationNode を作成し、オーディオグラフの最終ノードをそれに接続して、その .stream プロパティを MediaRecorder に渡します。これにより、getUserMedia() から得られる生のマイクストリームではなく、後処理された音声をキャプチャできます。
iOS Safari では、AudioContext はクリックやタッチイベントなどの直接的なユーザー操作内で作成または resume される必要があります。ページ読み込み中にインスタンス化すると suspended 状態のままになります。AudioContext の作成をボタンハンドラー内に移動するか、ハンドラー内で audioContext.resume() を呼び出して再生と処理をアンロックしてください。
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.