技術 約5分で読めます

AIと喋れる環境を作る(2)音声入力の実装編

前回の調査編では音声APIの選択肢を比較した。今回は「実際にどうやって音声を取り込むか」という実装面を掘り下げる。

音声入力の2つのアプローチ

ブラウザで音声入力を実装する方法は大きく2つ。

  1. Web Speech API - ブラウザ標準の音声認識。テキスト変換まで自動でやってくれる
  2. 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/EdgeGoogleサーバー×
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(通常)
ブラウザデフォルト
Chromeaudio/webm;codecs=opus
Firefoxaudio/webm;codecs=opus
Safariaudio/mp4

API側の対応フォーマット

API対応フォーマット
OpenAI Whispermp3, mp4, mpeg, mpga, m4a, wav, webm
Google Speech-to-TextFLAC, 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 → 音声出力の流れができたら、見た目も付けたい。