语音浮层生命周期(macOS)
受众:macOS 应用贡献者。目标:在唤醒词与按键说话重叠时保持语音浮层行为可预测。当前意图
- 如果浮层已因唤醒词显示,此时用户按下热键,热键会话会接管现有文本而非重置。浮层在热键按住期间保持显示。用户松开时:如果有去除空白后的文本则发送,否则关闭。
- 单独使用唤醒词时仍在静音后自动发送;按键说话在松开时立即发送。
已实现(2025 年 12 月 9 日)
- 浮层会话现在为每次捕获(唤醒词或按键说话)携带一个令牌。当令牌不匹配时,部分/最终/发送/关闭/音量更新会被丢弃,避免过时回调。
- 按键说话会接管任何可见的浮层文本作为前缀(因此在唤醒浮层显示时按下热键会保留文本并追加新语音)。它最多等待 1.5 秒获取最终转录结果,然后回退到当前文本。
- 提示音/浮层日志以
info级别输出,分类为voicewake.overlay、voicewake.ptt和voicewake.chime(会话开始、部分、最终、发送、关闭、提示音原因)。
后续步骤
- VoiceSessionCoordinator(actor)
- 同一时间只拥有一个
VoiceSession。 - API(基于令牌):
beginWakeCapture、beginPushToTalk、updatePartial、endCapture、cancel、applyCooldown。 - 丢弃携带过时令牌的回调(防止旧识别器重新打开浮层)。
- 同一时间只拥有一个
- VoiceSession(模型)
- 字段:
token、source(wakeWord|pushToTalk)、已提交/临时文本、提示音标志、计时器(自动发送、空闲)、overlayMode(display|editing|sending)、冷却截止时间。
- 字段:
- 浮层绑定
VoiceSessionPublisher(ObservableObject)将活跃会话镜像到 SwiftUI。VoiceWakeOverlayView仅通过 publisher 渲染;绝不直接修改全局单例。- 浮层用户操作(
sendNow、dismiss、edit)携带会话令牌回调到 coordinator。
- 统一发送路径
endCapture时:如果去除空白后文本为空 → 关闭;否则performSend(session:)(播放一次发送提示音、转发、关闭)。- 按键说话:无延迟;唤醒词:可选自动发送延迟。
- 按键说话结束后对唤醒运行时施加短暂冷却,防止唤醒词立即重新触发。
- 日志
- Coordinator 在子系统
bot.molt、分类voicewake.overlay和voicewake.chime下输出.info级别日志。 - 关键事件:
session_started、adopted_by_push_to_talk、partial、finalized、send、dismiss、cancel、cooldown。
- Coordinator 在子系统
调试清单
-
复现浮层粘滞问题时流式查看日志:
- 验证只有一个活跃会话令牌;过时回调应被 coordinator 丢弃。
-
确保按键说话松开时始终使用活跃令牌调用
endCapture;如果文本为空,预期dismiss且不播放提示音或发送。
迁移步骤(建议)
- 添加
VoiceSessionCoordinator、VoiceSession和VoiceSessionPublisher。 - 重构
VoiceWakeRuntime,使其创建/更新/结束会话,而非直接操作VoiceWakeOverlayController。 - 重构
VoicePushToTalk,使其接管现有会话并在松开时调用endCapture;施加运行时冷却。 - 将
VoiceWakeOverlayController连接到 publisher;移除来自 runtime/PTT 的直接调用。 - 添加会话接管、冷却和空文本关闭的集成测试。