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 默认实现
  • 预留 OpenAIWhisperEngineAzureOpenAIGPTAudioEngine
  • UI 和引擎解耦,切换引擎不影响主逻辑

跨平台方案

开发了基于 FastAPI 的 Web 版原型:

  • 浏览器 MediaRecorder API 录音 → WebSocket 发送到本地服务
  • 后端调用 Azure Speech SDK 识别 + GPT 润色
  • 支持局域网访问(0.0.0.0:8000),手机也能用
  • HTTPS 限制:手机浏览器需 SSL 才能调用麦克风

配置与安全

  • 所有 API Key 通过 .env 管理,代码中使用 ${...} 占位符
  • .gitignore 排除 .envconfig*.jsonbuild/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 端)

相关页面