Aloud — 把語音輸入的引擎換成豆包流式 ASR
為什麼做這個
說話比打字快,但 macOS 內建聽寫對中英混說基本沒用。「用 Python 寫個 retry 裝飾器」這種句子,辨識出來是一堆同音字加錯斷句。我要的很簡單:按一下說話,再按一下出字,注入到任何當前聚焦的輸入框,能聽懂中英混和技術詞,最好再有一個 LLM 順手把明顯的口誤改回來——只改聽錯的,不許它潤色改寫。
yetone 有個開源的 voice-input,選單列互動和文字注入邏輯都現成。問題在引擎:它用 Apple 的 SFSpeechRecognizer,中英混和專業術語恰好是這個引擎的弱項。殼子對,引擎不對。fork 下來換掉。
它是什麼
macOS 選單列小工具。Fn 按一下開始錄音,膠囊浮窗帶即時波形和上字,再按一下停,最終文字注入當前輸入框,原剪貼簿自動恢復。可選一層 LLM 糾錯(豆包 seed lite),預設開,可關。
辨識後端是火山豆包流式語音辨識 2.0,WebSocket 連。純本機觸發,憑證存在本機,不走任何第三方中轉。
實現過程
有意思的部分都在引擎替換上。
火山的流式 ASR 不是 REST,是自訂二進位 WebSocket 協定:4 位元組 header + 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]。