Voice Typer Mac 语音打字工具
macOS 上的语音转文字桌面工具,支持按键录音、实时上屏、GPT 润色、撤回口令和菜单栏状态指示。
概述
Voice Typer 是一个 Python 编写的 macOS 语音打字工具,由 research-fries 主导开发。用户按住快捷键说话,松开后自动将语音转文字并粘贴到当前光标位置。经过多轮迭代,从一个简单的录音脚本发展为功能完善的桌面应用,并开源到 GitHub。
GitHub 仓库: https://github.com/Restry/voice-typer-mac
核心功能
| 功能 | 说明 |
|---|---|
| 长按 Alt 录音 | 按住1秒触发录音,松开停止并输出 + GPT-4o 润色 |
| 长按 Ctrl 录音 | 仅语音识别直接上屏,不调用润色 |
| 双击 Alt 发送 | 0.5秒内连按两次 Alt 模拟回车键 |
| 菜单栏状态球 | 🎙️ 待机 / 🔴 录音 / ⏳ 处理 |
| 撤回口令 | 说话结尾说”撤回”/“取消”/“删掉”即丢弃整段录音 |
| 实时分段上屏 | 说话停顿时断句实时输出(Azure SDK 版) |
| 语音手账 | 所有转录内容按天记录到 logs/ 目录 |
| 中英混输 | 自动检测语言,支持中英无缝切换 |
| 屏幕上下文 | macOS Vision 框架 OCR 截屏内容作为识别参考 |
| 60秒防呆 | 超时自动停止录音 |
技术演进
第一阶段:GPT Audio 模型
- 初始使用
gpt-audio-1.5(Azure OpenAI),空格键触发 - 问题:速度慢、模型报 500 错误、多模态消息顺序敏感
- 改进:切换到
gpt-audio-mini,触发键从空格改为 Alt
第二阶段:Realtime API(WebSocket 流式)
- 使用
gpt-realtime-mini,边说边传音频 - 问题:模型当成对话助手而非转写工具,需极端提示词约束
- temperature 不能低于 0.6(Realtime API 限制)
第三阶段:Azure Speech SDK(最终方案)
- 使用微软 Azure 语音服务(东亚节点,香港直连)
- 流式 WebSocket 架构,真正的 ASR 模型
- 按住说话松开出字,VAD 端点检测
- 支持短语提示词(Phrase Hints)提高专有名词识别率
第四阶段:GPT 润色层
- 识别后套一层 GPT-4o 润色:删除废话、结构重组、极简重写
- 上下文记忆队列(最近5句)保持连贯性
- 模式 B:说话时不出字,松开后一次性输出润色结果
代码架构
voice-trans/
├── voice_typer_mac.py # macOS 原生版(Alt+润色, Ctrl+直接)
├── voice_typer_modular.py # 模块化重构版(面向对象)
├── rewrite_layer.py # GPT-4o 润色模块
├── web_server.py # 跨平台 Web 版原型(FastAPI)
├── .env # 敏感配置(不提交)
├── .env.example # 配置模板
├── config_speech.json # Azure Speech 配置(含 phrase_hints)
├── logs/ # 语音手账日志
├── README.md # 项目说明
└── requirements.txt # 依赖清单
模块化设计
SpeechEngine基类 → 可插拔的语音引擎AzureSpeechSDKEngine默认实现- 预留
OpenAIWhisperEngine和AzureOpenAIGPTAudioEngine - UI 和引擎解耦,切换引擎不影响主逻辑
跨平台方案
开发了基于 FastAPI 的 Web 版原型:
- 浏览器
MediaRecorder API录音 → WebSocket 发送到本地服务 - 后端调用 Azure Speech SDK 识别 + GPT 润色
- 支持局域网访问(
0.0.0.0:8000),手机也能用 - HTTPS 限制:手机浏览器需 SSL 才能调用麦克风
配置与安全
- 所有 API Key 通过
.env管理,代码中使用${...}占位符 .gitignore排除.env、config*.json、build/、dist/- 推送 GitHub 前自动扫描脱敏
- Azure Speech Key: 东亚节点(
eastasia) - Azure OpenAI: 瑞典节点(
resley-sweden)
踩坑记录
0x13 SPXERR_START_RECOGNIZING_INVALID_STATE_TRANSITION: 快速连按导致状态机冲突 → 加锁防抖 + 捕获异常重建实例stop_continuous_recognition_async().get()可能死锁 → 改用非阻塞 stop + 0.5秒缓冲rumps菜单项 Key 修改后找不到 → 改为遍历匹配 + try-except 保护- 脱敏后忘记恢复本地配置 → 401 认证失败
- Anaconda base 环境的
vision-1.0.0-py3.10-nspkg.pth报错 → 可忽略的警告 doubaoime_asr.asr.ASRError: StartSession 失败:ExceededConcurrentQuota→ 同一设备 ID 同一时间只能保持一个 ASR 连接在线。后台 VoiceTyper 进程未关闭时再开mic_realtime.py会触发。诊断步骤:1)ps aux | grep python找到占线进程并kill -9;2) 若仍报错,可能是旧 Token 被服务器锁死或幽灵会话未超时(30-60秒),需删除~/.config/doubao-asr/credentials.json后关闭代理让log.snssdk.com直连,重新运行生成新 Token
第五阶段:doubaoime-asr 引擎探索(2026-04-07)
- 尝试集成 doubaoime-asr(字节跳动豆包输入法 ASR)作为备选语音识别引擎
- 首次运行需向
log.snssdk.com注册虚拟设备,网络受限时 SSL 握手失败(exit code 35)→ 需关闭代理或用手机热点 - 成功注册后凭据缓存到
credentials.json,之后不再需要联网注册 ExceededConcurrentQuota错误:同一 device_id 只允许一个并发 ASR 连接,需确保无残留进程- 代码整理:将
~/Downloads/voice-trans/中散落的 7-8 个voice_typer_*.py脚本合并整理到~/Projects/VoiceTyper/,统一入口为main.py,启动时可选模式(全局热键 / Web 端)