背景
Claude Codeに公式の音声入力機能(Voice Mode)がロールアウト中。
Voice mode is rolling out now in Claude Code. It's live for ~5% of users today, and will be ramping through the coming weeks.
/voice to toggle it on!
-- Thariq (@trq212)
/voice コマンドで有効化
- スペースバー長押しでPush-to-Talk
- 現在約5%のユーザーに展開中、今後数週間で段階的にロールアウト
ClaudeWorkでの課題
ClaudeWorkはサーバー側PTYでClaude Codeを実行し、XTerm.jsでブラウザに表示するアーキテクチャのため、Claude Codeの /voice 機能はサーバー側のマイクを使おうとする。ブラウザのマイクからは直接使えない。
方針: 公式 /voice + 仮想マイク転送
Claude Codeの公式 /voice 機能をそのまま活用する方針。ブラウザのマイク音声をサーバーに転送し、PulseAudio仮想マイクデバイス経由でClaude Codeに認識させる。
アーキテクチャ
ブラウザのマイク
→ MediaRecorder API (WebM/Opus)
→ WebSocket (audio-stream メッセージ)
→ サーバー: 音声データ受信
→ FFmpeg でPCM変換
→ PulseAudio 仮想デバイスに書き込み
→ module-null-sink + module-remap-source(仮想マイク)
→ Claude Code /voice がデフォルトマイクとして認識
実装要素
1. ブラウザ側: マイクキャプチャと転送
// MediaRecorder APIで音声キャプチャ
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mediaRecorder = new MediaRecorder(stream, {
mimeType: 'audio/webm;codecs=opus'
});
// WebSocket経由でサーバーに送信
mediaRecorder.ondataavailable = (event) => {
ws.send(JSON.stringify({
type: 'audio-stream',
data: arrayBufferToBase64(event.data),
mimeType: 'audio/webm'
}));
};
mediaRecorder.start(250); // 250msごとにチャンク送信
2. サーバー側: PulseAudio仮想マイク設定
# セッションごとに仮想マイクデバイスを作成
pactl load-module module-null-sink sink_name=virtmic_session_<id> \
sink_properties=device.description="VirtualMic-Session-<id>"
# モニターソースをマイクデバイスとして公開
pactl load-module module-remap-source \
master=virtmic_session_<id>.monitor \
source_name=virtmic_src_<id> \
source_properties=device.description="VirtualMic-Source-<id>"
3. サーバー側: 音声データの仮想デバイスへの書き込み
// WebSocketで受信した音声データをFFmpegで変換し、PulseAudioに送信
import { spawn } from 'child_process';
const ffmpeg = spawn('ffmpeg', [
'-i', 'pipe:0', // stdinから入力
'-f', 's16le', // PCM 16-bit出力
'-ar', '44100', // サンプルレート
'-ac', '1', // モノラル
'pipe:1' // stdoutに出力
]);
const pacat = spawn('pacat', [
'--playback',
'-d', `virtmic_session_${sessionId}`, // 仮想シンクに出力
'--format=s16le',
'--rate=44100',
'--channels=1'
]);
ffmpeg.stdout.pipe(pacat.stdin);
// WebSocketから受信した音声データをFFmpegに渡す
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (msg.type === 'audio-stream') {
const buffer = Buffer.from(msg.data, 'base64');
ffmpeg.stdin.write(buffer);
}
});
4. Docker環境対応
# docker-compose.yml に追加
services:
app:
volumes:
- /run/user/${UID}/pulse/native:/run/user/1000/pulse/native
- ${HOME}/.config/pulse/cookie:/home/app/.config/pulse/cookie:ro
environment:
- PULSE_SERVER=unix:/run/user/1000/pulse/native
# PulseAudioクライアントライブラリが必要
# Dockerfileに追加: apt-get install -y pulseaudio-utils ffmpeg
5. Claude Code PTYへの /voice コマンド送信
// セッション開始時またはユーザー操作時に/voiceを送信
ptySession.write('/voice\n');
// PULSE_SOURCEでセッション固有の仮想マイクを指定
// PTYプロセスの環境変数として設定
const env = {
...process.env,
PULSE_SOURCE: `virtmic_src_${sessionId}`
};
セッション分離
各セッションに独立した仮想マイクデバイスを割り当て、複数ユーザーの同時利用に対応する:
- セッション作成時:
module-null-sink + module-remap-source をロード
- セッション終了時: ロードしたモジュールをアンロード(
pactl unload-module <module-id>)
- Claude Code PTYのPULSE_SOURCE環境変数でセッション固有のデバイスを指定
未確認事項・リスク
| 項目 |
内容 |
影響度 |
/voice のマイク選択方式 |
デフォルトデバイスのみか、PULSE_SOURCE環境変数に従うか |
高 |
| ロールアウト状況 |
現在~5%、GA時期は未定 |
高 |
| Docker内PulseAudio |
ソケット共有でコンテナ内のClaude Codeが認識するか |
高 |
| オーディオレイテンシー |
ブラウザ→WebSocket→FFmpeg→PulseAudio→Claude Code |
中 |
| ブラウザ互換性 |
MediaRecorder APIはChrome/Firefox/Safari対応 |
低 |
実装フェーズ
Phase 0: 検証(POC)
Phase 1: 基盤実装
Phase 2: UI/UX
Phase 3: Docker対応
代替案(フォールバック)
Phase 0の検証でClaude Codeの /voice が仮想マイクを認識しない場合のフォールバック:
Web Speech API方式: ブラウザ側で音声をテキストに変換し、既存の type: 'input' メッセージとしてWebSocket送信。サーバー側変更不要、実装シンプル。
参考リソース
背景
Claude Codeに公式の音声入力機能(Voice Mode)がロールアウト中。
/voiceコマンドで有効化ClaudeWorkでの課題
ClaudeWorkはサーバー側PTYでClaude Codeを実行し、XTerm.jsでブラウザに表示するアーキテクチャのため、Claude Codeの
/voice機能はサーバー側のマイクを使おうとする。ブラウザのマイクからは直接使えない。方針: 公式
/voice+ 仮想マイク転送Claude Codeの公式
/voice機能をそのまま活用する方針。ブラウザのマイク音声をサーバーに転送し、PulseAudio仮想マイクデバイス経由でClaude Codeに認識させる。アーキテクチャ
実装要素
1. ブラウザ側: マイクキャプチャと転送
2. サーバー側: PulseAudio仮想マイク設定
3. サーバー側: 音声データの仮想デバイスへの書き込み
4. Docker環境対応
5. Claude Code PTYへの
/voiceコマンド送信セッション分離
各セッションに独立した仮想マイクデバイスを割り当て、複数ユーザーの同時利用に対応する:
module-null-sink+module-remap-sourceをロードpactl unload-module <module-id>)未確認事項・リスク
/voiceのマイク選択方式実装フェーズ
Phase 0: 検証(POC)
/voiceがPULSE_SOURCEを認識するか検証Phase 1: 基盤実装
audio-streamメッセージタイプ追加Phase 2: UI/UX
/voiceコマンドの自動送信Phase 3: Docker対応
代替案(フォールバック)
Phase 0の検証でClaude Codeの
/voiceが仮想マイクを認識しない場合のフォールバック:Web Speech API方式: ブラウザ側で音声をテキストに変換し、既存の
type: 'input'メッセージとしてWebSocket送信。サーバー側変更不要、実装シンプル。参考リソース