feat: 集成 VAD 语音活动检测,打通核心交互链路

- 重写 useVAD Hook,接入 @ricky0123/vad-web 的 MicVAD
- 支持 onSpeechStart/onSpeechEnd/onVADMisfire 回调
- useVisionSession 中串联摄像头→麦克风→WebSocket→VAD 完整流程
- App.tsx 新增 VAD 加载中和错误状态的 UI 指示
- VAD 参数对齐设计文档:positiveSpeechThreshold=0.5, minSpeechMs=250

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-13 11:10:29 +08:00
parent 14550d3aa2
commit 9ccd4d6238
4 changed files with 140 additions and 29 deletions

View File

@@ -13,6 +13,8 @@ function App() {
currentReply,
isProcessing,
isSpeaking,
isVADReady,
vadError,
connectionStatus,
videoRef,
stream,
@@ -21,12 +23,18 @@ function App() {
interrupt,
} = useVisionSession();
const isConnected = connectionStatus === "connected";
return (
<div className="app">
<header className="app-header">
<h1>CamTalk</h1>
<span className={`status status--${connectionStatus}`}>
{connectionStatus === "connected" ? "已连接" : connectionStatus === "connecting" ? "连接中..." : "未连接"}
{isConnected
? "已连接"
: connectionStatus === "connecting"
? "连接中..."
: "未连接"}
</span>
</header>
@@ -34,6 +42,16 @@ function App() {
<div className="video-section">
<VideoPreview ref={videoRef} isStreaming={!!stream} />
{isSpeaking && <div className="vad-indicator">🎤 ...</div>}
{isConnected && !isVADReady && !vadError && (
<div className="vad-indicator vad-indicator--loading">
...
</div>
)}
{vadError && (
<div className="vad-indicator vad-indicator--error">
{vadError}
</div>
)}
</div>
<div className="chat-section">
@@ -48,7 +66,7 @@ function App() {
</main>
<footer className="app-footer">
{connectionStatus !== "connected" ? (
{!isConnected ? (
<button className="btn btn--primary" onClick={startSession}>
</button>