语音唤醒与按键说话
模式
- 唤醒词模式(默认):始终开启的语音识别器等待触发词(
swabbleTriggerWords)。匹配成功后开始捕获,显示带有部分文本的悬浮窗,并在静音后自动发送。 - 按键说话(按住右 Option 键):按住右 Option 键立即捕获——无需触发词。按住时显示悬浮窗;松开后会在短暂延迟后完成并转发,让你可以调整文本。
Runtime 行为(唤醒词模式)
- 语音识别器运行在
VoiceWakeRuntime中。 - 只有在唤醒词和下一个词之间有明显停顿时才会触发(约 0.55 秒间隔)。悬浮窗/提示音可以在停顿时就开始,甚至在命令开始之前。
- 静音窗口:语音流动时 2.0 秒,只听到触发词时 5.0 秒。
- 硬性停止:120 秒,防止失控的 Session。
- Session 之间的防抖:350 毫秒。
- 悬浮窗通过
VoiceWakeOverlayController驱动,带有已提交/临时状态的颜色区分。 - 发送后,识别器会干净地重启,监听下一个触发词。
生命周期不变量
- 如果启用了语音唤醒且权限已授予,唤醒词识别器应该处于监听状态(除非在显式的按键说话捕获期间)。
- 悬浮窗的可见性(包括通过 X 按钮手动关闭)绝不能阻止识别器恢复。
悬浮窗卡住的故障模式(之前的问题)
之前,如果悬浮窗卡在可见状态并且你手动关闭它,语音唤醒可能会显得”死掉”,因为 Runtime 的重启尝试可能被悬浮窗可见性阻塞,并且没有安排后续重启。
加固措施:
- 唤醒 Runtime 重启不再被悬浮窗可见性阻塞。
- 悬浮窗关闭完成时会通过
VoiceSessionCoordinator触发VoiceWakeRuntime.refresh(...),所以手动 X 关闭总是会恢复监听。
按键说话细节
- 热键检测使用全局
.flagsChanged监视器监听右 Option 键(keyCode 61+.option)。我们只观察事件(不吞噬)。 - 捕获管道运行在
VoicePushToTalk中:立即启动语音识别,将部分结果流式传输到悬浮窗,并在松开时调用VoiceWakeForwarder。 - 当按键说话开始时,我们会暂停唤醒词 Runtime 以避免音频捕获冲突;松开后会自动重启。
- 权限:需要麦克风 + 语音识别权限;监听事件需要辅助功能/输入监控授权。
- 外接键盘:有些可能不会按预期暴露右 Option 键——如果用户报告问题,提供备用快捷键。
用户设置
- 语音唤醒开关:启用唤醒词 Runtime。
- 按住 Cmd+Fn 说话:启用按键说话监视器。在 macOS < 26 上禁用。
- 语言和麦克风选择器、实时音量表、触发词表、测试器(仅本地;不转发)。
- 麦克风选择器会在设备断开时保留上次选择,显示断开提示,并临时回退到系统默认设备,直到它重新连接。
- 声音:在检测到触发词和发送时播放提示音;默认使用 macOS “Glass” 系统声音。你可以为每个事件选择任何
NSSound可加载的文件(如 MP3/WAV/AIFF),或选择无声音。
转发行为
- 当启用语音唤醒时,转录文本会转发到活动的 Gateway/Agent(与 Mac 应用其他部分使用的本地 vs 远程模式相同)。
- 回复会发送到最后使用的主 Provider(WhatsApp/Telegram/Discord/WebChat)。如果发送失败,错误会被记录,运行仍然可以通过 WebChat/Session 日志查看。
转发负载
VoiceWakeForwarder.prefixedTranscript(_:)在发送前添加机器提示。唤醒词和按键说话路径共享此逻辑。
快速验证
- 打开按键说话,按住 Cmd+Fn,说话,松开:悬浮窗应该显示部分结果然后发送。
- 按住时,菜单栏耳朵图标应该保持放大状态(使用
triggerVoiceEars(ttl:nil));松开后缩小。