语音覆盖层
唤醒词和按住说话重叠时的语音覆盖层生命周期
受众:macOS 应用贡献者。目标:在唤醒词和按住说话重叠时保持语音覆盖层可预测。
#
当前意图
- 如果覆盖层已因唤醒词而可见,并且用户按下热键,热键会话将采用现有文本而不是重置它。覆盖层在按住热键时保持显示。当用户释放时:如果有修剪后的文本则发送,否则关闭。
- 仅唤醒词仍然在静音时自动发送;按住说话在释放时立即发送。
#
已实现(2025 年 12 月 9 日)
- 覆盖层会话现在为每次捕获(唤醒词或按住说话)携带一个令牌。当令牌不匹配时,部分/最终/发送/关闭/级别更新将被丢弃,避免过时的回调。
- 按住说话采用任何可见的覆盖层文本作为前缀(因此在唤醒覆盖层显示时按下热键会保留文本并追加新的语音)。它会等待最多 1.5 秒以获取最终转录,然后回退到当前文本。
- 铃声/覆盖层日志在类别 ''voicewake.chime''、''voicewake.overlay'' 和 ''voicewake.ptt'' 中以 ''voicewake.chime'' 级别发出(会话开始、部分、最终、发送、关闭、铃声原因)。
#
后续步骤
1. VoiceSessionCoordinator(参与者)
- 一次只拥有一个 ''VoiceSession''。
- API(基于令牌):''beginWakeCapture''、''beginPushToTalk''、''updatePartial''、''endCapture''、''cancel''、''applyCooldown''。
- 丢弃携带过时令牌的回调(防止旧识别器重新打开覆盖层)。
2. VoiceSession(模型)
- 字段:''token''、''source''(wakeWord|pushToTalk)、已提交/易失性文本、铃声标志、计时器(自动发送、空闲)、''overlayMode''(display|editing|sending)、冷却截止时间。
3. 覆盖层绑定
- ''VoiceSessionPublisher''(''ObservableObject'')将活动会话镜像到 SwiftUI。
- ''VoiceWakeOverlayView'' 仅通过发布者渲染;它从不直接改变全局单例。
- 覆盖层用户操作(''sendNow''、''dismiss''、''edit'')使用会话令牌回调到协调器。
4. 统一发送路径
- 在 ''endCapture'' 上:如果修剪后的文本为空 → 关闭;否则 ''performSend(session:)''(播放发送铃声一次、转发、关闭)。
- 按住说话:无延迟;唤醒词:自动发送的可选延迟。
- 在按住说话完成后对唤醒运行时应用短冷却,以便唤醒词不会立即重新触发。
5. 日志记录
- 协调器在子系统 ''voicewake.chime''、类别 ''bot.molt'' 和 ''voicewake.overlay'' 中发出 ''voicewake.chime'' 日志。
- 关键事件:''session_started''、''adopted_by_push_to_talk''、''partial''、''finalized''、''send''、''dismiss''、''cancel''、''cooldown''。
#
调试清单
- 在重现粘性覆盖层时流式传输日志:
''''`bash", "p3": "sudo log stream --predicate 'subsystem == "bot.molt" AND category CONTAINS "voicewake"' --level info --style compact", "p4": "''''`
sudo log stream --predicate 'subsystem == "bot.molt" AND category CONTAINS "voicewake"' --level info --style compact
''''`
- 验证只有一个活动会话令牌;过时的回调应该被协调器丢弃。
- 确保按住说话释放始终使用活动令牌调用 ''endCapture'';如果文本为空,则期望 ''dismiss'' 而没有铃声或发送。
#
迁移步骤(建议)
1. 添加 ''VoiceSessionCoordinator''、''VoiceSession'' 和 ''VoiceSessionPublisher''。
2. 重构 ''VoiceWakeRuntime'' 以创建/更新/结束会话,而不是直接接触 ''VoiceWakeOverlayController''。
3. 重构 ''VoicePushToTalk'' 以采用现有会话并在释放时调用 ''endCapture'';应用运行时冷却。
4. 将 ''VoiceWakeOverlayController'' 连接到发布者;从运行时/PTT 中删除直接调用。
5. 添加会话采用、冷却和空文本关闭的集成测试。