jpskill.com
🛠️ 開発・MCP コミュニティ

voice-system-expert

Use when working with Quetrex's voice interface, OpenAI Realtime API, WebRTC, or echo cancellation. Knows Quetrex's specific voice architecture decisions and patterns. CRITICAL - prevents breaking working voice system.

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o voice-system-expert.zip https://jpskill.com/download/17477.zip && unzip -o voice-system-expert.zip && rm voice-system-expert.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17477.zip -OutFile "$d\voice-system-expert.zip"; Expand-Archive "$d\voice-system-expert.zip" -DestinationPath $d -Force; ri "$d\voice-system-expert.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して voice-system-expert.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → voice-system-expert フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Quetrex 音声システムエキスパート

重要: 最初にこれを読んでください

Quetrex の音声システムアーキテクチャは、広範囲に文書化され、実戦でテストされています。音声関連のコードに何らかの変更を加える前に、必ず以下をお読みください。

  1. ADR-001-VOICE-ECHO-CANCELLATION.md (決定的なアーキテクチャ上の決定)
  2. docs/architecture/VOICE-SYSTEM.md (技術的な実装)
  3. docs/features/voice-interface.md (ユーザー向けの機能)

場所: src/lib/openai-realtime.ts

コアアーキテクチャの決定

常時オンのマイク + ブラウザ AEC

これは VOICE-SYSTEM.md の 決定 4 であり、ADR-001 に文書化されている決定的なアプローチです。

// ✅ 正しい: 常時オンのマイク
const mediaStream = await navigator.mediaDevices.getUserMedia({
  audio: {
    echoCancellation: true,  // 重要 - ブラウザがエコーキャンセレーションを処理
    noiseSuppression: true,
    autoGainControl: true,
  },
})

// マイクのトラックは会話中ずっと有効
// ブラウザのネイティブ AEC がフィードバックループを防ぐ
// サーバー側の VAD (音声区間検出) がターンの検出を処理

仕組み

オーディオパイプライン

ユーザーが話す
    ↓
マイク (常に有効、echoCancellation: true)
    ↓
WebRTC → OpenAI Realtime API
    ↓
サーバー側の VAD が音声を検出
    ↓
OpenAI が処理して応答
    ↓
WebRTC 経由の音声応答
    ↓
HTMLAudioElement の再生 (ブラウザのパイプラインに残る)
    ↓
ブラウザ AEC がマイク入力 + スピーカー出力を比較
    ↓
エコーが自動的にキャンセルされる (フィードバックループなし)

これがうまくいく理由

ブラウザのエコーキャンセレーションの要件:

  1. マイク入力とスピーカー出力の両方がブラウザのオーディオグラフにある必要がある
  2. オーディオはブラウザの WebRTC スタックを通過する必要がある
  3. 手動での介入は不要 (ブラウザが処理する)

これは業界標準です:

  • ChatGPT 音声モード
  • Google Meet
  • Zoom
  • Discord
  • Microsoft Teams

これらはすべて、常時オンのマイク + ブラウザ AEC を使用しています。

これらのことはしないでください

❌ しないでください: マイクのトラックを切り替える

// ❌ 間違い - これはエコーキャンセレーションを壊す
async function pauseRecording() {
  microphone.enabled = false // これをしないでください
}

async function resumeRecording() {
  microphone.enabled = true // これをしないでください
}

これが失敗する理由:

  • 業界標準ではない
  • オーディオのグリッチを引き起こす
  • エコーキャンセレーションを壊す可能性がある
  • 人工的な遅延を追加する
  • より複雑な状態管理
  • 実際のメリットがない

❌ しないでください: オーディオをブラウザの外にルーティングする

// ❌ 間違い - ネイティブオーディオへの AudioWorklet バイパス
const workletNode = new AudioWorkletNode(audioContext, 'bypass-processor')
workletNode.port.postMessage({ cmd: 'route-to-native' })

これが失敗する理由:

  • ブラウザ AEC を壊す (オーディオがブラウザのパイプラインから離れる)
  • エコー/フィードバックループを引き起こす
  • 複雑なネイティブオーディオ処理が必要
  • プラットフォーム固有の実装
  • すでに試して失敗している (abandoned-approaches/ を参照)

❌ しないでください: カスタムのエコーキャンセレーションを実装する

// ❌ 間違い - カスタム AEC 実装
class CustomEchoCanceller {
  cancelEcho(input: AudioBuffer, output: AudioBuffer) {
    // 複雑な DSP コード...
  }
}

これが失敗する理由:

  • 車輪の再発明
  • ブラウザ AEC は数十億人のユーザーによって実戦でテストされている
  • 正しく実装するのは非常に複雑
  • 深い DSP の知識が必要
  • パフォーマンスの問題
  • プラットフォーム固有の調整が必要

❌ しないでください: 人工的な遅延を追加する

// ❌ 間違い - エコー防止のための遅延
await new Promise(resolve => setTimeout(resolve, 500))
await playAudio()
await new Promise(resolve => setTimeout(resolve, 500))
resumeRecording()

これが失敗する理由:

  • 不要 (ブラウザ AEC が処理する)
  • ユーザーエクスペリエンスを低下させる
  • レイテンシを追加する
  • 実装が間違っている場合、それでもエコーを防ぐことはできない

変更できること

✅ してください: オーディオ設定を調整する

// ✅ これらの設定は調整できる
const constraints = {
  audio: {
    echoCancellation: true,      // true である必要がある
    noiseSuppression: true,       // 調整可能
    autoGainControl: true,        // 調整可能
    sampleRate: 24000,            // 品質のために変更可能
    channelCount: 1,              // 音声にはモノラルで十分
  },
}

✅ してください: 接続状態を処理する

// ✅ 接続ライフサイクルを管理できる
async function connectVoice() {
  // WebRTC 接続をセットアップ
  // ストリーミングを開始
  // 接続イベントを処理
}

async function disconnectVoice() {
  // WebRTC 接続をクリーンアップ
  // ストリーミングを停止
  // マイクを解放
}

✅ してください: UI フィードバックを追加する

// ✅ 視覚的なインジケーターを追加できる
function onVoiceActivity(active: boolean) {
  if (active) {
    // "リスニング" インジケーターを表示
    // マイクのアイコンをアニメーション化
  } else {
    // "アイドル" インジケーターを表示
  }
}

✅ してください: エラーを処理する

// ✅ エラー処理を改善できる
try {
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
} catch (error) {
  if (error.name === 'NotAllowedError') {
    // 許可リクエスト UI を表示
  } else if (error.name === 'NotFoundError') {
    // "マイクが見つかりません" エラーを表示
  }
}

実装場所

主要ファイル: src/lib/openai-realtime.ts

主要な関数:

  • setupMediaStream() - 正しい制約でマイクを初期化
  • connectToOpenAI() - WebRTC 接続を確立
  • handleAudioResponse() - HTMLAudioElement 経由で AI 応答を再生

変更しないでください:

  • マイクの有効/無効ロジック (常にオンのままにする必要がある)
  • エコーキャンセレーション設定 (true である必要がある)
  • オーディオルーティング (ブラウザのパイプラインに残す必要がある)

変更できます:

  • UI フィードバックと視覚的なインジケーター
  • エラー処理とユーザーメッセージ
  • 接続リトライロジック
  • 音質設定 (サンプルレートなど)

このアーキテクチャが選択された理由

ADR-001 より:

オプション A: 業界パターンを信頼する (選択)

  • ✅ ChatGPT、Google Meet、Zoom、Discord で使用
  • ✅ ブラウザベンダーはこのパターンに合わせて最適化
  • ✅ すべてのプラットフォーム (macOS、Windows、Linux、モバイル) で動作
  • ✅ カスタム実装は不要
  • ✅ 実証済みの信頼性

拒否されたオプション:

  • ❌ 手動マイク切り替え

(原文はここで切り詰められています)

📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Quetrex Voice System Expert

CRITICAL: Read This First

Quetrex's voice system architecture is extensively documented and battle-tested. Before making ANY changes to voice-related code, you MUST read:

  1. ADR-001-VOICE-ECHO-CANCELLATION.md (definitive architectural decision)
  2. docs/architecture/VOICE-SYSTEM.md (technical implementation)
  3. docs/features/voice-interface.md (user-facing features)

Location: src/lib/openai-realtime.ts

Core Architecture Decision

ALWAYS-ON MICROPHONE + BROWSER AEC

This is Decision 4 from VOICE-SYSTEM.md and the definitive approach documented in ADR-001.

// ✅ CORRECT: Always-on microphone
const mediaStream = await navigator.mediaDevices.getUserMedia({
  audio: {
    echoCancellation: true,  // CRITICAL - browser handles echo cancellation
    noiseSuppression: true,
    autoGainControl: true,
  },
})

// Microphone track stays ENABLED throughout conversation
// Browser's native AEC prevents feedback loops
// Server-side VAD (Voice Activity Detection) handles turn detection

How It Works

Audio Pipeline

User speaks
    ↓
Microphone (always enabled, echoCancellation: true)
    ↓
WebRTC → OpenAI Realtime API
    ↓
Server-side VAD detects speech
    ↓
OpenAI processes and responds
    ↓
Audio response via WebRTC
    ↓
HTMLAudioElement playback (stays in browser pipeline)
    ↓
Browser AEC compares mic input + speaker output
    ↓
Echo automatically canceled (no feedback loop)

Why This Works

Browser Echo Cancellation Requirements:

  1. Both microphone input AND speaker output must be in browser's audio graph
  2. Audio must flow through browser's WebRTC stack
  3. No manual intervention needed (browser handles it)

This is the industry standard:

  • ChatGPT voice mode
  • Google Meet
  • Zoom
  • Discord
  • Microsoft Teams

They ALL use always-on microphone + browser AEC.

DO NOT DO THESE THINGS

❌ DON'T: Toggle Microphone Track

// ❌ WRONG - This breaks echo cancellation
async function pauseRecording() {
  microphone.enabled = false // DON'T DO THIS
}

async function resumeRecording() {
  microphone.enabled = true // DON'T DO THIS
}

Why this fails:

  • Not industry standard
  • Causes audio glitches
  • Can break echo cancellation
  • Adds artificial delays
  • More complex state management
  • No real benefit

❌ DON'T: Route Audio Outside Browser

// ❌ WRONG - AudioWorklet bypass to native audio
const workletNode = new AudioWorkletNode(audioContext, 'bypass-processor')
workletNode.port.postMessage({ cmd: 'route-to-native' })

Why this fails:

  • Breaks browser AEC (audio leaves browser pipeline)
  • Causes echo/feedback loops
  • Requires complex native audio handling
  • Platform-specific implementations
  • Already tried and failed (see abandoned-approaches/)

❌ DON'T: Implement Custom Echo Cancellation

// ❌ WRONG - Custom AEC implementation
class CustomEchoCanceller {
  cancelEcho(input: AudioBuffer, output: AudioBuffer) {
    // Complex DSP code...
  }
}

Why this fails:

  • Reinventing the wheel
  • Browser AEC is battle-tested by billions of users
  • Extremely complex to implement correctly
  • Requires deep DSP knowledge
  • Performance issues
  • Platform-specific tuning needed

❌ DON'T: Add Artificial Delays

// ❌ WRONG - Delays for echo prevention
await new Promise(resolve => setTimeout(resolve, 500))
await playAudio()
await new Promise(resolve => setTimeout(resolve, 500))
resumeRecording()

Why this fails:

  • Not necessary (browser AEC handles it)
  • Degrades user experience
  • Adds latency
  • Still doesn't prevent echo if implementation is wrong

What You CAN Change

✅ DO: Adjust Audio Settings

// ✅ Can tweak these settings
const constraints = {
  audio: {
    echoCancellation: true,      // MUST be true
    noiseSuppression: true,       // Can adjust
    autoGainControl: true,        // Can adjust
    sampleRate: 24000,            // Can change for quality
    channelCount: 1,              // Mono is fine for voice
  },
}

✅ DO: Handle Connection States

// ✅ Can manage connection lifecycle
async function connectVoice() {
  // Setup WebRTC connection
  // Start streaming
  // Handle connection events
}

async function disconnectVoice() {
  // Clean up WebRTC connection
  // Stop streaming
  // Release microphone
}

✅ DO: Add UI Feedback

// ✅ Can add visual indicators
function onVoiceActivity(active: boolean) {
  if (active) {
    // Show "listening" indicator
    // Animate microphone icon
  } else {
    // Show "idle" indicator
  }
}

✅ DO: Handle Errors

// ✅ Can improve error handling
try {
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
} catch (error) {
  if (error.name === 'NotAllowedError') {
    // Show permission request UI
  } else if (error.name === 'NotFoundError') {
    // Show "no microphone found" error
  }
}

Implementation Location

Primary file: src/lib/openai-realtime.ts

Key functions:

  • setupMediaStream() - Initializes microphone with correct constraints
  • connectToOpenAI() - Establishes WebRTC connection
  • handleAudioResponse() - Plays AI responses via HTMLAudioElement

DO NOT modify:

  • Microphone enable/disable logic (should stay always-on)
  • Echo cancellation settings (must be true)
  • Audio routing (must stay in browser pipeline)

CAN modify:

  • UI feedback and visual indicators
  • Error handling and user messages
  • Connection retry logic
  • Audio quality settings (sample rate, etc.)

Why This Architecture Was Chosen

From ADR-001:

Option A: Trust Industry Pattern (CHOSEN)

  • ✅ Used by ChatGPT, Google Meet, Zoom, Discord
  • ✅ Browser vendors optimize for this pattern
  • ✅ Works across all platforms (macOS, Windows, Linux, mobile)
  • ✅ No custom implementation needed
  • ✅ Proven reliability

Options Rejected:

  • ❌ Manual microphone toggling (not industry standard)
  • ❌ AudioWorklet native bypass (breaks AEC)
  • ❌ Custom echo cancellation (reinventing wheel)
  • ❌ Native Rust WebRTC (2-4 weeks work for no benefit)

Platform Context: Web Application

IMPORTANT: Quetrex is now a pure web application, not a Tauri desktop app.

Why this matters for voice:

  • Browser echo cancellation works perfectly in all browsers
  • No WKWebView limitations (that was the Tauri problem)
  • Universal compatibility (Chrome, Safari, Firefox, Edge)
  • No platform-specific audio handling needed
  • Just works™️

The WKWebView Problem (Historical): Quetrex was originally a Tauri desktop app. On macOS, Tauri uses WKWebView, which has a bug: WebRTC audio playback doesn't work. This forced us to try workarounds (AudioWorklet bypass, native audio routing), which all broke echo cancellation.

Solution: Convert to web application. Now echo cancellation works perfectly everywhere.

Testing Voice Changes

If you must modify voice code:

  1. Test on real browsers (not just dev tools)
  2. Test the echo scenario:
    • Turn up speaker volume
    • Start voice conversation
    • Verify no feedback loop/echo
  3. Test across platforms:
    • Chrome (most common)
    • Safari (macOS, iOS)
    • Firefox
    • Edge
  4. Test edge cases:
    • Poor network connection
    • Microphone permission denied
    • Mid-conversation disconnect

When to Consult Documentation

Before any voice changes, read:

  1. docs/decisions/ADR-001-VOICE-ECHO-CANCELLATION.md
  2. docs/architecture/VOICE-SYSTEM.md
  3. docs/development/abandoned-approaches/

If you see mentions of:

  • Tauri → Ignore (Quetrex is web app now)
  • WKWebView → Ignore (not relevant anymore)
  • AudioWorklet bypass → Don't implement (already tried, failed)
  • Manual mic toggling → Don't implement (not industry standard)

Summary

The Golden Rule: Trust browser echo cancellation. Always-on microphone + echoCancellation: true + audio in browser pipeline = Perfect echo cancellation.

DO:

  • Keep microphone enabled throughout conversation
  • Use echoCancellation: true
  • Keep audio in browser (HTMLAudioElement)
  • Let OpenAI Realtime API handle VAD
  • Trust the industry pattern

DON'T:

  • Toggle microphone track
  • Route audio outside browser
  • Implement custom echo cancellation
  • Add artificial delays
  • Try to "improve" what already works

If in doubt: Read ADR-001 and VOICE-SYSTEM.md. The architecture is thoroughly documented for a reason.