技術
約5分で読めます
AIと喋れる環境を作る(2)音声入力の実装編
前回の調査編では音声APIの選択肢を比較した。今回は「実際にどうやって音声を取り込むか」という実装面を掘り下げる。
音声入力の2つのアプローチ
ブラウザで音声入力を実装する方法は大きく2つ。
- Web Speech API - ブラウザ標準の音声認識。テキスト変換まで自動でやってくれる
- MediaRecorder - 音声を録音してBlobで取得。自分でAPIに送って処理する
どちらを選ぶかは用途次第。
Web Speech API
ブラウザ標準の音声認識API。喋った内容をリアルタイムでテキストに変換してくれる。
基本的な使い方
// SpeechRecognitionのインスタンス作成(Chrome対応)
const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
// 設定
recognition.lang = 'ja-JP'; // 日本語
recognition.continuous = true; // 継続的に認識
recognition.interimResults = true; // 途中結果も取得
// 認識結果の取得
recognition.onresult = (event) => {
const result = event.results[event.results.length - 1];
const transcript = result[0].transcript;
const isFinal = result.isFinal;
if (isFinal) {
console.log('確定:', transcript);
// ここでAI APIに送信
} else {
console.log('認識中:', transcript);
}
};
// エラーハンドリング
recognition.onerror = (event) => {
console.error('認識エラー:', event.error);
};
// 開始
recognition.start();
主要な設定項目
| プロパティ | 説明 | デフォルト |
|---|---|---|
lang | 認識言語(ja-JP, en-US等) | ブラウザ設定 |
continuous | 継続的に認識するか | false |
interimResults | 途中結果を返すか | false |
maxAlternatives | 候補の最大数 | 1 |
Chromeの仕様問題
Web Speech APIはブラウザによって実装が異なる。
| ブラウザ | 処理場所 | オフライン |
|---|---|---|
| Chrome/Edge | Googleサーバー | × |
| Safari | オンデバイス | ○ |
| Firefox | 未実装 | - |
Chromeは音声データをGoogleのサーバーに送信して処理している。つまり:
- ネット接続必須
- プライバシー面で気になる場合は要注意
- レイテンシはサーバー往復分
とはいえ、どうせAI APIに送る前提なら、ネット接続は必須だし、プライバシー面も大差ないかもしれない。
ブラウザ対応状況
// 対応チェック
const isSupported = 'SpeechRecognition' in window || 'webkitSpeechRecognition' in window;
if (!isSupported) {
alert('お使いのブラウザは音声認識に対応していません');
}
MediaRecorderで録音 → API送信
Web Speech APIを使わず、自分で録音してWhisper API等に送るパターン。より細かい制御が可能。
基本的な流れ
// 1. マイク取得
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// 2. MediaRecorder作成
const mediaRecorder = new MediaRecorder(stream, {
mimeType: 'audio/webm;codecs=opus' // ブラウザ対応フォーマット
});
const chunks = [];
// 3. データ取得
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
chunks.push(event.data);
}
};
// 4. 録音停止時にBlob作成
mediaRecorder.onstop = async () => {
const blob = new Blob(chunks, { type: 'audio/webm' });
// Whisper APIに送信
const formData = new FormData();
formData.append('file', blob, 'audio.webm');
formData.append('model', 'whisper-1');
const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${OPENAI_API_KEY}`
},
body: formData
});
const result = await response.json();
console.log('認識結果:', result.text);
};
// 録音開始
mediaRecorder.start();
// 5秒後に停止
setTimeout(() => mediaRecorder.stop(), 5000);
リアルタイム送信(チャンク分割)
長い音声を細切れにして送信するパターン。
const mediaRecorder = new MediaRecorder(stream);
// 1秒ごとにデータを取得
mediaRecorder.start(1000);
mediaRecorder.ondataavailable = async (event) => {
if (event.data.size > 0) {
// 1秒分のチャンクをAPIに送信
await sendToAPI(event.data);
}
};
リアルタイム vs 録音後送信
WebSocket(リアルタイム)
const socket = new WebSocket('wss://api.example.com/realtime');
// 音声データをリアルタイム送信
mediaRecorder.ondataavailable = (event) => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(event.data);
}
};
// 認識結果を受信
socket.onmessage = (event) => {
const result = JSON.parse(event.data);
console.log('認識結果:', result.text);
};
メリット:
- 低レイテンシ
- 喋りながら認識結果が返ってくる
デメリット:
- 実装が複雑
- 対応APIが限られる(OpenAI Realtime API等)
REST(録音後送信)
// 録音完了後に一括送信
const blob = new Blob(chunks, { type: 'audio/webm' });
const result = await sendToWhisperAPI(blob);
メリット:
- 実装がシンプル
- 対応APIが多い
デメリット:
- 録音が終わるまで結果が返らない
- 長い音声は待ち時間が長い
使い分け
| ユースケース | おすすめ |
|---|---|
| 音声チャットボット | WebSocket(会話のレスポンス重視) |
| 音声メモ | REST(一括処理でOK) |
| 文字起こし | REST(精度重視) |
| 簡易実装 | Web Speech API(一番楽) |
音声フォーマット
ブラウザとAPIで対応フォーマットが異なるので注意。
ブラウザ側(MediaRecorder)
// 対応フォーマットを確認
MediaRecorder.isTypeSupported('audio/webm;codecs=opus'); // true(Chrome)
MediaRecorder.isTypeSupported('audio/mp4'); // true(Safari)
MediaRecorder.isTypeSupported('audio/wav'); // false(通常)
| ブラウザ | デフォルト |
|---|---|
| Chrome | audio/webm;codecs=opus |
| Firefox | audio/webm;codecs=opus |
| Safari | audio/mp4 |
API側の対応フォーマット
| API | 対応フォーマット |
|---|---|
| OpenAI Whisper | mp3, mp4, mpeg, mpga, m4a, wav, webm |
| Google Speech-to-Text | FLAC, LINEAR16, MULAW, OGG_OPUS, WEBM_OPUS等 |
WebMはほとんどのAPIで対応しているので、ブラウザのデフォルトのまま送ればOKなことが多い。
WAVが必要な場合
一部のAPIやローカル処理でWAVが必要な場合は、変換が必要。
// AudioContextでPCMデータを取得してWAV化
async function recordAsWav(stream, duration) {
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
const processor = audioContext.createScriptProcessor(4096, 1, 1);
const pcmData = [];
processor.onaudioprocess = (event) => {
const inputData = event.inputBuffer.getChannelData(0);
pcmData.push(new Float32Array(inputData));
};
source.connect(processor);
processor.connect(audioContext.destination);
// 録音
await new Promise(resolve => setTimeout(resolve, duration));
// WAV形式に変換
return createWavBlob(pcmData, audioContext.sampleRate);
}
ただし、WAV変換は面倒なので、WebM対応のAPIを使うほうが楽。
構成パターン別おすすめ
パターン1: とにかく簡単に
Web Speech API一択
const recognition = new webkitSpeechRecognition();
recognition.lang = 'ja-JP';
recognition.onresult = (e) => sendToAI(e.results[0][0].transcript);
recognition.start();
- 10行で動く
- 無料
- Chromeのサーバー送信が気にならなければこれでOK
パターン2: 精度重視
MediaRecorder + Whisper API
- 録音 → WebMで送信 → 高精度な認識結果
- Whisperの精度はWeb Speech APIより上
- 有料だけど安い($0.006/分)
パターン3: リアルタイム重視
OpenAI Realtime API or Gemini Live API
- WebSocketでリアルタイム双方向
- コストは高め(特にOpenAI)
- 本格的な音声チャットボット向け
まとめ
- 手軽に始めるなら: Web Speech API
- 精度が必要なら: MediaRecorder + Whisper API
- リアルタイム会話なら: Realtime系API
次回はアバター連携(Live2D? VRM?)を調査予定。音声入力 → AI → 音声出力の流れができたら、見た目も付けたい。