Aloud — 把语音输入的引擎换成豆包流式 ASR
为什么做这个
说话比打字快,但 macOS 自带听写对中英混说基本没用。「用 Python 写个 retry 装饰器」这种句子,识别出来是一堆同音字加错断句。我要的很简单:按一下说话,再按一下出字,注入到任何当前聚焦的输入框,能听懂中英混和技术词,最好再有一个 LLM 顺手把明显的口误改回来——只改听错的,不许它润色改写。
yetone 有个开源的 voice-input,菜单栏交互和文字注入逻辑都现成。问题在引擎:它用 Apple 的 SFSpeechRecognizer,中英混和专业术语恰好是这个引擎的弱项。壳子对,引擎不对。fork 下来换掉。
它是什么
macOS 菜单栏小工具。Fn 按一下开始录音,胶囊浮窗带实时波形和上字,再按一下停,最终文本注入当前输入框,原剪贴板自动恢复。可选一层 LLM 纠错(豆包 seed lite),默认开,能关。
识别后端是火山豆包流式语音识别 2.0,WebSocket 连。纯本地触发,凭证存在本机,不走任何第三方中转。
实现过程
有意思的部分都在引擎替换上。
火山的流式 ASR 不是 REST,是自定义二进制 WebSocket 协议:4 字节头 + uint32 payload size + body,大端序,帧类型靠 header 第二字节的高低位区分(full client request / audio / server response / error)。手写了一层编解码(VolcProtocol.swift),最后一个音频包打特殊 flag 收尾。
音频管线得改。macOS 输入节点默认 44.1/48kHz Float32,豆包只吃 16kHz 16bit 单声道,用 AVAudioConverter 重采样,200ms 一块(6400 字节)往上推。握手没完成前积累的 buffer 要在握手回调里补发,不能丢。
踩得最深的坑:豆包流式 2.0 的 resource id 是 volc.seedasr.sauc.duration,1.0 才是 volc.bigasr.sauc.duration。用错服务端直接报 403 not granted,错误信息指向「服务未开通」,真实原因是 resource id 多写错一个词,bigasr 应是 seedasr。endpoint 也得是 bigmodel_async,/bigmodel 只认 1.0。这一个词调了挺久。
LLM 纠错那层用豆包 seed lite,关键是把 thinking 关掉——开着深度思考纠一句话要 30 秒,关掉 3 秒。prompt 写死只修明显语音误识别(「配森」→Python、「杰森」→JSON),不许改写、润色、删内容。
状态收敛用一个串行 queue 把 SpeechEngine 的读写和发送决策全压到一条线上,teardown 和 removeTap 加幂等 guard。这块跑了 Codex 审查,修掉几个重入崩溃和跨线程竞态的 P1。
它怎么到你手上
和 marklite 一样:不挂 GitHub Release。.app 打包成 zip 放 Cloudflare R2,独立桶挂 aloud-releases.openedon.com,下载页是本站的一部分。R2 出口流量免费,对单人项目这是目前最便宜的分发渠道。
基础壳子(Apple Speech 版)的源码是 yetone 公开的,在 https://github.com/yetone/voice-input-src;豆包引擎这一层是我自己改的。
下载
下载 Aloud for macOS — 仅 Apple Silicon。
用之前要先在火山引擎控制台开通豆包流式语音识别,把 AppID / Access Token 填进 App 的 Voice Engine Settings。未签名,首次打开右键选「打开」,或去 系统设置 → 隐私与安全性 → 仍要打开。还要给麦克风和辅助功能权限——监听 Fn 键和注入文字都需要。
这是个自用工具,早期、未签名、没自动更新。能用,但别指望它像成品。出问题 [email protected]。