Commit Graph

12 Commits

Author SHA1 Message Date
Blizzard c66d178efe fix(voice): 思考看门狗覆盖 PTT 模式(别永远卡在"思考中")
此前 armThinkTimer 只在连续对话模式挂,PTT 下后端不回(dispatcher 未就绪/
模型没配/任务失败)就永远卡在"思考中",用户干等且毫无线索。

改:任何模式收到 final 转写都挂看门狗;超时后连续对话回聆听、PTT 回待命,
并明确提示可能原因 + 引导去运行页看任务状态。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 14:57:18 +08:00
Blizzard e60679ed7b fix(voice): PTT 改为主交互——按住说话松开发送(修 PTT 形同虚设的 bug)
VAD 自动断句在真实环境不够稳(环境音/停顿都会误触发),改回按住说话。

修掉 PTT 之前根本不生效的三处:
- keydown 里 `if (!c) return`——用户没先点过麦克风按钮时 clientRef 为 null,
  按空格什么都不发生。改为懒建客户端(keydown 本身就是用户手势,可启 AudioContext)
- 被 `!inConversation()` 挡着:一旦点过麦进了连续对话,PTT 永久失效。改为 PTT 优先,
  必要时先退出对话模式
- 缺 e.repeat 守卫:按住不放会连发 keydown

交互统一为一套 PTT 语义:
- 按住空格 / 按住麦克风按钮(pointer 事件,覆盖鼠标触控) → 说话,松开发送
- 朗读中按按钮 = 打断
- 指针滑出/取消/窗口失焦都算松开,不会卡在录音态
- 录音中按钮变红缩小 + 电平条 + 呼吸环,一眼可见"正在听"

顺带:松开后标签改为"已停止收音"(准确说法——麦克风硬件仍开着以便快速重按,
只是不再上传音频,门控在 shouldSendMic);删掉已无人使用的 micTapAction 及其单测。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 14:52:26 +08:00
Blizzard 52988af196 feat(voice): 按住空格键语音输入(PTT),松开发送
按住空格键开始聆听,松开发送 end 指令提交任务。不销毁麦克风上下文,
下次重按直接复用,避免 getUserMedia/AudioContext 重建延迟。
支持输入框/文本区内不抢空格、窗口失焦自动取消。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-24 21:03:27 +08:00
Blizzard aba88193d8 feat(voice): 提升桌面端语音交互与本地任务执行支持 2026-07-24 16:40:25 +08:00
Blizzard b8095688ba fix(voice): TTS 只播前几字就断——tts_end 别打断,等排队音频放完
火山合成远快于真实语速:一句话的 PCM 秒级全推到客户端,被 nextStart
预约到未来时刻顺序播。此前 tts_end 直接 resetPlayback() stop 掉所有还
没播的 source,于是只听到前三四个字就断。

改:tts_end 走 drainThenReady()——不打断,按 nextStart 算剩余时长等排队
音频自然放完,末段结束再回 ready;speaking 态维持到真播完(HUD 频谱靠它)。
resetPlayback 只留给 barge-in/close,并清 drainTimer 防迟到定时器改状态。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 09:01:02 +08:00
Blizzard a4b9897eaf fix(voice): 麦克风打不开时给准确提示——多约束尝试 + 无设备预检
- 无麦克风设备(如 Mac Studio/Mini 无内置麦)时,getUserMedia 会以 OverconstrainedError
  "Invalid constraint" 误导报错;先 enumerateDevices 预检 audioinput,没有就直说"没检测到麦克风"
- getUserMedia 依次试 {audio:true}/{audio:{}}/带回声消除,全败则暴露真实错误名;
  NotAllowedError 单独提示去系统设置授权

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:56:08 +08:00
Blizzard fe5c4215f7 fix(voice): 麦克风约束兜底——WKWebView 不认高级约束("Invalid constraint")退 {audio:true}
原生壳(Wails WKWebView)的 getUserMedia 不认 channelCount/echoCancellation 等高级约束,
抛 "Invalid constraint"。先试高级约束(浏览器更好),失败退回最简 {audio:true}(最兼容)。
配合上一提交的 .app 打包+NSMicrophoneUsageDescription,原生壳麦克风打通。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:47:19 +08:00
Blizzard 92341be32c fix(desktop): 麦克风优雅降级 + Info.plist 加 NSMicrophoneUsageDescription
- voice.ts: startMic 前探测 navigator.mediaDevices?.getUserMedia——WKWebView(原生壳)非安全
  上下文里它可能未暴露,直接调会抛 "undefined is not an object" 崩掉;改为给可读提示
- build/darwin/Info.plist + Info.dev.plist: 加 NSMicrophoneUsageDescription,否则 macOS
  不会授权麦克风(package 后的 .app 才能弹权限)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:44:27 +08:00
Blizzard 2a743a33f6 feat(desktop): 全屏「JARVIS 模式」HUD——钢铁侠风,接真实语音音频
弧反应堆核心 + 同心旋转环 + 随真实声音反应的环形频谱 + 状态编排 + HUD 边框/扫描线/网格。
可视化数据全来自真实语音会话(不是模拟)。

- voice.ts: micCtx/playCtx 各挂 AnalyserNode + level()——说话读下行TTS、其余读麦克风,
  实时电平 0..1(HUD 每帧读,复用缓冲)
- JarvisHud.tsx: Canvas 2D HUD,按 VoiceState 编排(待命呼吸/聆听炸开/思考散粒子+雷达/说话频谱),
  单一青色强调+思考态琥珀;显示助手名(星期五)+ 我说的 + 打字机回答;尊重 prefers-reduced-motion
- VoiceDock: 加"全屏 JARVIS 模式"按钮(建客户端+拉名字);Esc 退出

真机验证:全屏 HUD 正常渲染,STANDBY 呼吸,品牌位显示自定义名"星期五"。晚上点中心说话即随真声反应。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:28:39 +08:00
Blizzard 99ce07c1b0 feat(voice): 每用户 JARVIS——自定义名字/人设 + 自带豆包配置回落 + 语音不串主记忆
把 JARVIS 做成每用户独立:名字(你叫JARVIS别人叫星期五)、人设(与主偏好记忆分开)、
可自带豆包配置(齐全则用用户的,否则回落系统)。

- store/user_jarvis.go: sundynix_user_jarvis(user_id唯一;name/persona/火山creds,APIKey密文)
  + Get/Save(upsert) + AutoMigrate 注册
- voice_config.go resolveJarvis(uid): 火山配置用户优先系统兜底 + 取名字/人设
- voice_task.go: voiceSystemPrompt(name,persona)——简短是硬基线,名字/语气由用户定;
  buildVoiceGraph 带 name+persona;voice.go 会话升级时按用户解析
- dispatcher compose_compiler: 语音任务(useVoice)不拉主偏好记忆,改用用户 persona(保留短期历史)
- jarvis.go + 路由: GET/PUT /api/v1/me/jarvis(用户级,api_key 脱敏/留空沿用)

真机验证:设 name=星期五+干净人设→语音答"我是星期五…"(自称新名、无糙话、4.4s),
has_own_voice=false 用系统豆包。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:06:32 +08:00
Blizzard b6927247da feat(voice): 下行打字机文本流 + 首块快出,即时反馈
- protocol.go: 新增 ServerReply("reply") 消息——Agent 回答增量文本
- voice_tts.go: token 一到即转发客户端(打字机),同时攒句喂 TTS(音频随后)
- sentence_buffer.go: 本轮首句用低阈值(5 rune)抢首字延迟,之后回常规 12
- 桌面端 voice.ts onReply + VoiceDock 对话气泡(我说的 + JARVIS 打字机回答,思考态光标)

管线已最优:文字在 LLM 首 token 即刻上屏、音频紧随。剩余时延=大模型 TTFT(deepseek-v4-pro
4-7s 且波动大,疑似推理模型),这是模型的账、非管线——真要"马上响应"需换快模型。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:25:20 +08:00
Blizzard 9458bf21f3 feat(voice): 桌面端 JARVIS 语音坞——麦克风上行 + TTS 播放队列(Phase 1 收口)
右下角悬浮麦克风:点按说话→转写→提交任务→跳运行页→朗读回答,接入既有运行·观测流。

- lib/voice.ts: VoiceClient——一条 WS 承载上行 PCM16k/下行转写/下行 TTS PCM24k;
  ScriptProcessorNode 采麦(48k→16k 降采样+Float32→Int16);AudioBufferSourceNode
  排队调度做无缝连续朗读;start/end/barge_in/bye 控制;ready/transcript/task/speaking/tts_end
- shell/VoiceDock.tsx: 悬浮麦克风 UI(聆听/思考/朗读态 + 转写气泡 + 打断),懒建客户端(用户手势启 AudioContext)
- App.tsx: onVoiceTask 把语音 task 挂回 attachRun(后端已提交,前端只 attach);挂载 VoiceDock

真识别/合成需部署联调(真连火山+admin录入语音配置);本地过 tsc/build/68 测试。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:29:47 +08:00