顯示具有 macOS 標籤的文章。 顯示所有文章
顯示具有 macOS 標籤的文章。 顯示所有文章

macOS 最快 TTS 實測:30 秒語音 379ms,Apple Neural Engine 才是隱藏王牌

Mac Studio 黑底霓虹綠終端風,audio waveform 從機器中散出

把 30 秒的英語語音算完,要多久?答案是 0.379 秒。在一台 Mac Studio(64GB)上,用 mattmireles/kokoro-coreml 把 Kokoro-82M 餵進 Apple Neural Engine,30 秒台詞 379 毫秒生完,等於 79 倍 realtime。同一台機器、同一個模型,丟給 MLX(GPU)跑要 763 毫秒,丟給 PyTorch CPU 慢到三五千毫秒——同模型不同 runtime,速度可以差一個數量級。

過去兩年寫過幾個 voice agent 的人應該都很熟那種痛:LLM 已經把 token 吐出來了,TTS 在那邊吭吭吭算半秒,整個對話節奏就斷掉。雲端 API 不是不能用,但 ElevenLabs Flash 官方公告 first-byte ~135ms ,加上網路 round-trip 直接破半秒。如果你的 Mac 就在桌上,這延遲根本沒道理付出去。

這篇把 2026 年中目前 macOS 上最快的幾個 TTS 工具實測數字攤開來講,順便分享一個多數人沒注意到的事實:Apple Neural Engine 才是 M 系列上真正的 TTS 速度王牌,而不是 GPU 或 MPS。

環境前置:本文所有實測數字來自 2026 年 6 月的公開 benchmark。硬體涵蓋 M1 Mini / M2 Air / Mac Studio(64GB) / M4 Pro。所有工具都是 100% on-device 本機推論,不打雲端 API。


速度排行榜:一表看完

先丟結論。下面這張是我把所有來源交叉驗證後整理出來的,數字都有出處可查:

排名工具加速後端實測速度模型中文支援
🥇kokoro-coremlApple Neural EngineMac Studio 64GB:30s 音檔 379 ms(79×RT);M1 Mini 14×RTKokoro-82M, 170 MB✅ 8 voices
🥈Supertonic 3ONNX Runtime(CPU / WebGPU)M4 Pro CPU 中等文 RTF 0.013(≈77×RT);長文峰值 RTF 0.006(≈167×RT)66~99M❌ 31 語言沒中文
🥉Piper TTSONNX Runtime(CPU)RTF 0.008(≈ 125×RT), ~22 MBVITS, 20M✅ zh_CN
4macOS say / AVSpeechSynthesizer系統內建啟動瞬間(無模型載入)內建✅ Mei-Jia / Ting-Ting
5MLX-Audio(Kokoro / Qwen3-TTS / Higgs)MLX(Metal GPU)Kokoro RTF 0.44;Qwen3-TTS RTF ~282M ~ 4B✅ Qwen3-TTS 中文業界頂尖

來源:mattmireles/kokoro-coreml 實測表 、Supertonic 3 官方 benchmark 、Soniqo Apple Silicon 對照 、sherpa-onnx Supertonic 對照表 。

這份表我自己整完有幾個地方讓我重看了三次。

最便宜的 M1 Mini(16GB,現在二手不到三萬台幣)跑 kokoro-coreml 也有 14× realtime。意思是你聽 1 秒語音,它只算 70 毫秒。這個延遲已經比你的鍵盤防彈跳還短,跑 voice agent 不會聽到「呃……」的卡頓。

Supertonic 3 的數字更誇張——M4 Pro 純 CPU 中等文本 RTF 0.013(≈ 77×RT),長文本峰值衝到 RTF 0.006(≈ 167×RT)。同一個 sherpa-onnx 對照表裡,ElevenLabs Flash v2.5 API 中等文本 RTF 0.077、OpenAI TTS-1 RTF 0.302、Gemini 2.5 Flash TTS RTF 0.673。一個 99M 參數的開源 ONNX 模型純 CPU 跑出 6 倍於 ElevenLabs、23 倍於 OpenAI 的速度,這在 2024 年根本不敢想。

被嚴重低估的還有 macOS 內建的 say。它的「啟動延遲」是真正的 0,因為根本不用載模型——系統永遠開著。對於 shell 通知、簡單無障礙場景,這還是最快的工具,下面會專段聊。


kokoro-coreml:把 ANE 真正榨乾

神經處理器晶片在黑色電路板上發出綠色資料光流

先講背景。Apple Neural Engine(ANE)這顆 NPU 從 A12 開始就裝在每一支 iPhone 跟每一台 M 系列 Mac 上,FLOPS 數字很漂亮、能耗也低——社群實測 LLM 工作負載大約是 GPU 的 1/10 (M4 Pro 上有人量到 ANE 2W、GPU 20W)。但開發者社群一直碰不到它——CoreML 是黑箱排程器,你連模型有沒有真的跑在 ANE 上都很難確認,更別說優化。

mattmireles/kokoro-coreml 把 Kokoro-82M 拆成 5 個子模型,每個子模型分派到最適合的處理器(ANE / CPU / GPU),用 Swift 的 MLModel(contentsOf:) 載 .mlpackage 就能跑。實測數字:

音檔長度M1 Mini (16GB)M2 Air (24GB)Mac Studio (64GB)
3 秒234 ms148 ms51 ms
10 秒686 ms466 ms126 ms
30 秒1,959 ms1,405 ms379 ms

對比同硬體上的 MLX-Audio (Metal GPU 路線):

音檔Mac Studio:CoreML vs MLX加速倍率
7 秒96 ms vs 224 ms2.3×
10 秒126 ms vs 289 ms2.3×
30 秒379 ms vs 763 ms2.0×

對比 PyTorch:

  • vs PyTorch MPS(GPU):1.8~3.4× 快
  • vs PyTorch CPU:3.5~7.3× 快

每個 bucket 都更快,越新的 silicon 差距越大——這正是 ANE 路線真正的潛力。GPU 你加再多核心也卡在記憶體頻寬,ANE 是專為神經網路設計的固定函式硬體,跑 conv / matmul 不會跟 GPU 搶 Metal queue。

整合到 Swift App 大概長這樣:

import CoreML

// 載入預編譯的 .mlpackage(5 個子模型)
let config = MLModelConfiguration()
config.computeUnits = .all  // 讓 CoreML 自動派 ANE/CPU/GPU

let textEncoder = try MLModel(contentsOf: textEncoderURL, configuration: config)
let durationPredictor = try MLModel(contentsOf: durationURL, configuration: config)
let decoder = try MLModel(contentsOf: decoderURL, configuration: config)
// ... 然後 pipeline 串起來

// 同步推論
let pcmBuffer = try synthesizer.synthesize(text: "Hello, world.")
// 在 M1 Mini 上這行回來大約 50ms(短句)

raw 數字、benchmark 腳本、跨 Mac 比較矩陣都在 GitHub ,要做 Swift app 整合直接拿來用。

唯一的坑:作者老實寫了,iPhone(A14 / A17 Pro)的 ANE 編譯器會拒絕完整的 ANE 計畫(ANECompile() FAILED),iPhone 版本只能用 staged policy——decoder-pre 跑 ANE、其他階段跑 CPU+GPU。但 M 系列 Mac 沒這個問題,本文聚焦的是 macOS。


Supertonic 3:純 CPU 也能贏雲端 API

CPU 形狀的賽車在賽博龐克高速公路上超車

如果 ANE 是「在 Apple 生態裡作弊」,那 Supertonic 3 就是「連 GPU 都不用,純 CPU 直接幹翻所有人」。

Supertonic 是 Supertone Inc.(韓國公司,被 HYBE 收購過)開源的 TTS,66~99M 參數、用 flow-matching 架構、純 ONNX Runtime 跑。實測在 M4 Pro 上:

系統RTF (mid 152 chars)chars/sec
Supertonic 3 (M4 Pro CPU)0.0131,048
Supertonic 3 (M4 Pro WebGPU)0.0071,801
ElevenLabs Flash v2.5(API)0.077209
OpenAI TTS-1(API)0.30255
Gemini 2.5 Flash TTS(API)0.67318
Kokoro(sherpa-onnx CPU)0.124107

數字來源:sherpa-onnx HuggingFace benchmark (同一頁有完整 API 對照)。

讓我們把這個數字放在桌上看清楚——Supertonic 用 M4 Pro 的 CPU,跑出比 ElevenLabs Flash 快 6 倍、比 OpenAI TTS-1 快 23 倍的速度。它甚至不需要 ANE,連 GPU 都不用。一個 99M 的 ONNX 模型輾過所有商業 TTS API,乾。

Flow matching 架構是它的祕密武器——傳統 autoregressive TTS(像 Tacotron、Bark)要 token by token 慢慢吐,flow matching 把生成重新定義成「從 noise ODE 解到 mel-spectrogram」的平行解碼問題。對 CPU 來說,平行化的工作量比 sequential token 友善太多。

裝起來就一行:

pip install supertonic
from supertonic import SupertonicTTS

tts = SupertonicTTS(language="en")
audio = tts.synthesize("Hello, this is Supertonic running on CPU.")
# audio 是 numpy array,可以直接寫 wav

但有個非常致命的限制:官方支援的 31 種語言 包含阿拉伯、保加利亞、捷克、丹麥、荷蘭、英、愛沙尼亞、芬蘭、法、德、希臘、印地、克羅埃西亞、匈、印尼、義、日、韓、拉脫維亞、立陶宛、波、葡、羅馬尼亞、俄、斯洛伐克、斯洛文尼亞、西、瑞典、土、烏克蘭、越南——就是沒有中文。對台灣讀者來說這個直接 deal-breaker,後面會講中文場景該怎麼選。


Piper 與 macOS say:老兵不死

黑色終端機浮在太空中顯示綠色音訊波形

聊完最快的兩隻,回來講「夠用但被低估」的兩個老兵。

Piper TTS

Piper (Rhasspy 開發、現在轉到 Open Home Foundation)是輕量場景的事實標準。VITS 架構、ONNX 編譯、medium voice ~22 MB、CPU 上 RTF 0.008(125 倍 realtime),30+ 語言含中文 zh_CN。Home Assistant 用它做語音助理、NVDA 螢幕閱讀器用它做無障礙、LocalAI 內建它做 TTS endpoint。

brew install piper-tts  # 或 pip install piper-tts
piper --model en_US-amy-medium --output_file out.wav <<< "Hello world"

它的優勢不在絕對速度(在 M-Mac 上 Supertonic 跟 kokoro-coreml 都比它快),而在「樹莓派級裝置都能跑即時 TTS」這件事。如果你想做家用智慧音箱、想在 Mac mini server 上跑 voice 服務、想嵌進低耗能設備,Piper 還是首選。

要注意的坑:原本的 rhasspy/piper repo 在 2025 年 10 月封存了,授權從 MIT 改成 GPL-3.0。如果你的產品要商用閉源發布,先確認新授權對你 OK 再用。

macOS 內建 say 跟 AVSpeechSynthesizer

這個老實說被嚴重低估。say 是 macOS 內建 CLI,啟動延遲是真正的 0——因為系統永遠把語音引擎掛在 background,根本不用載模型。

# shell 通知用法
sleep 30 && say -v Fiona "Build done, get back to work"

# 跑模型訓練不想盯著螢幕
python train.py && say "Training finished" || say "Training failed"

# 列出所有聲音
say -v "?"

macOS 26 開始,內建的「Premium / Neural Voice」品質已經非常 OK,繁體中文有 Mei-Jia(台灣國語)、粵語有 Sin-Ji、普通話有 Ting-Ting。設定 → 輔助使用 → 旁白 → 語音 → 自訂裡可以下載高品質版(每個約 200~500 MB)。

say -v Mei-Jia "你好世界,今天天氣真好"

對應的 Swift API 是 AVSpeechSynthesizer,可以調語速、音高、音量:

import AVFoundation

let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: "你好世界")
utterance.voice = AVSpeechSynthesisVoice(language: "zh-TW")
utterance.rate = 0.5
utterance.pitchMultiplier = 1.0
synthesizer.speak(utterance)

但這隻有個 voice agent 開發者必須知道的硬限制:AVSpeechSynthesizer 不能處理串流 partial text ——你必須把整段文字餵進去它才會開講。對 LLM token-by-token 輸出的場景完全不適用。Apple 至今沒補這個洞,這也是為什麼 voice agent 開發者寧可裝 kokoro-coreml 或 Supertonic 也不用內建 API。


中文場景:別被 Supertonic 騙了,Qwen3-TTS 才是王者

古典詩集冒出綠色音波

如果你是台灣讀者要做中文 TTS,前面 Supertonic 的速度數字看了會流口水,但記住——它沒中文。中文場景必須重新排名:

中文需求推薦工具為什麼
速度第一、中文夠用kokoro-coreml + zf_xiaoyi8 個普通話 voice,社群實測 zf_xiaoyi 達 A-、zm_yunxi B+;ANE 加速速度跟英文一樣
中文品質第一、可接受慢一點Qwen3-TTS-0.6B via MLX-Audio阿里出品、訓練數據海量、中文表現業界頂尖;M-Mac RTF ~2
零安裝、繁中say -v Mei-Jia台灣國語內建語者、無門檻
跑中文 audiobookMLX-Audio + Qwen3-TTSmyByways 有 ePub→audiobook 腳本 可參考
樹莓派 / HA 中文Piper zh_CN輕量、ONNX、長期維護

Qwen3-TTS 是這次研究中我覺得中文使用者最該認識的工具——阿里巴巴 Qwen 團隊 2026 年 1 月開源(Apache 2.0),支援 12Hz / 25Hz、0.6B / 1.7B 多檔位、3 秒語音 cloning、10+ 語言。在 SEED 中文測試上 WER 0.77(1.7B 12Hz),比 CosyVoice 3、F5-TTS、Spark TTS 都低。

Apple Silicon 透過 MLX-Audio 跑:

pip install mlx-audio
# 下載 8bit 量化版(M Mac 上記憶體友善)
huggingface-cli download mlx-community/Qwen3-TTS-12Hz-1.7B-CustomVoice-8bit

# 跑
python -m mlx_audio.tts.generate \
  --model mlx-community/Qwen3-TTS-12Hz-1.7B-CustomVoice-8bit \
  --text "你好,今天我們來聊聊 macOS 上最快的 TTS。" \
  --voice "default"

實測在 M2 macMini 大約「1,000 字元 / 分鐘」(myByways 實測 ),不是很快但中文品質直接屌打 Kokoro 中文 voice。如果你要做中文 podcast、audiobook、新聞播報,這就是目前 M-Mac 上最強的開源解。


不同情境怎麼選:決策樹

綠色光徑從中央節點分歧出去像決策樹

把所有工具放一起看會頭暈,下面是我給自己的決策邏輯:

你要做什麼?
│
├─ Shell 通知、無障礙、簡單腳本播報
│   └─→ macOS 內建 say(5 秒就能上手)
│
├─ Swift / Mac App 整合最快本機 TTS
│   ├─ 英語為主 →→→ kokoro-coreml(ANE)
│   └─ 中文為主 →→→ kokoro-coreml + zf_xiaoyi voice
│
├─ Python 跑 batch(audiobook / podcast)
│   ├─ 純英語、追速度 →→→ Supertonic 3
│   ├─ 中文品質要好 →→→ MLX-Audio + Qwen3-TTS
│   └─ 多語混合 →→→ MLX-Audio + Higgs v3 / OmniVoice
│
├─ Voice agent(要串流、LLM token-by-token)
│   ├─ 英語 →→→ sherpa-onnx + Supertonic
│   ├─ 中文 →→→ sherpa-onnx + Kokoro zh
│   └─ ⚠️ 千萬別用 AVSpeechSynthesizer(不能串流)
│
├─ 家庭自動化、Home Assistant、樹莓派
│   └─→ Piper TTS(zh_CN / en_US-amy-medium)
│
└─ 要 voice cloning(3 秒樣本複製音色)
    └─→ Qwen3-TTS via MLX-Audio(速度交換功能)

決策邏輯背後其實是兩條軸——「速度 vs 中文品質」跟「腳本場景 vs App 整合」。速度王是 ANE 跟 ONNX 兩條路線,中文品質王是阿里的 Qwen3-TTS(犧牲速度換來的),其餘工具都是在這兩端之間取捨。


一個你可能踩到的坑:runtime 換錯,速度差 10 倍

研究這個主題最讓我訝異的不是哪隻最快,而是同一個模型用不同 runtime,速度可以差到一個數量級。

以 Kokoro-82M 為例,同樣是 Mac Studio(64GB)、同樣的 voice af_heart、同樣的 30 秒測試文本:

Runtime完成時間相對速度
CoreML (ANE)379 ms1.0× baseline(最快)
MLX (Metal GPU)763 ms2× 慢
PyTorch MPS (GPU)700~1300 ms1.8~3.4× 慢
PyTorch CPU1300~2800 ms3.5~7.3× 慢

我看過太多人在 Reddit / HN 抱怨「Kokoro 在我的 Mac 上跑超慢」,結果一問,全部都是 PyTorch CPU 或 PyTorch MPS——這就像買了 Ferrari 用一檔開上高速公路。如果你的工作流會頻繁呼叫 TTS,花一個下午把 runtime 換到 CoreML 或 MLX,效能直接翻 2~7 倍。

決策原則記一下:

  • 要 絕對最快 → CoreML(限 Apple Silicon)
  • 要 跨平台 + 多模型 → MLX-Audio(限 Apple Silicon,但模型多)
  • 要 跨平台 + 跨硬體 → ONNX Runtime(CPU / WebGPU / iOS / Android 都通)
  • 要 完整生態、不挑硬體 → PyTorch(最慢但相容性最好)

寫 demo 用 PyTorch 沒問題,但要 ship 進產品請務必把模型轉成對應加速 runtime——這件事的 ROI 高到你不做白不做。


結語:本機 TTS 的世代交替

研究這個主題之前,我的預設是「本機 TTS 撐不住 production,雲端 API 還是必要」。研究完之後我整個立場反轉——M2 Air 上把 LLM token-by-token 接 sherpa-onnx 的 sentence-level Supertonic,中間用 VAD 切句、TTS 串流播放,整套 pipeline end-to-end 對話延遲打到 500 ms 以下完全有可能。這已經接近人類對話的自然節奏,雲端 API 加網路 round-trip 反而追不上。

中文場景目前是兩條路:要速度選 kokoro-coreml,要品質選 MLX-Audio + Qwen3-TTS。Supertonic 雖然數字嚇人但沒中文,官方 31 種語言列表 跳過 zh 我猜是訓練資料授權問題,期待下半年補上。

如果你還沒在自己的 Mac 上跑過任何一個本機 TTS,今天就去 clone 一個跑跑看。最低成本:開 terminal 打 say -v Mei-Jia "你好",五秒鐘體驗 macOS 內建語音的進化。進階:pip install supertonic 跑英語、pip install mlx-audio 跑 Qwen3-TTS 中文。最進階:拉 mattmireles/kokoro-coreml 進 Xcode 專案,把 voice agent 的 first-byte 延遲打到 50ms 以下。

雲端 TTS 不會死,但本機 TTS 在 2026 年已經不是「能用就好」的妥協選項——它是「比雲端更快、更便宜、更隱私」的明顯贏家。差別只是你願不願意花一個下午把 runtime 換對。


延伸閱讀

對這個主題有想討論的細節、或是你的 Mac 跑出不一樣的數字,歡迎來 bashcat.net 找我。

LibreCAD 在 macOS 26 Tahoe 打不開?這不是 LibreCAD 的鍋,是整個 codesigning 政策變了

實測環境:本機 crash report 抓到的真實環境如下——OS Version: macOS 26.5.1 (25F80)、Hardware Model: Mac15,6(Apple Silicon)、Homebrew cask LibreCAD 2.2.1.4 升級到 2.2.1.5、Termination Reason 為 Namespace CODESIGNING, Code 2, Invalid Page。版本號完整字串取自 sw_vers 與 crash report,並非虛構或預測版本;不同 minor 版的讀者照本文步驟操作應仍然適用,但出現新症狀時記得貼出自己的 crash report 來比對。

TL;DR:升級到 macOS Tahoe 後,舊版 Homebrew cask 的 LibreCAD 點兩下會瞬間崩。三行指令治本:brew reinstall --cask librecad → xattr -dr com.apple.quarantine → codesign --force --deep --sign -。但要知道,2026-09-01 之後這個 cask 會直接被 Homebrew 從本家停掉,所以順便講一下長期怎麼活下去。

LibreCAD 圖示在 Dock 跳一下就消失的視覺意象

我點兩下 LibreCAD 圖示。Dock 上的 icon 微微跳一下,然後沒了。沒有彈窗、沒有錯誤訊息、沒有「應用程式意外結束」的對話框,就純粹消失。ps aux | grep -i librecad 也是空的。

第一個直覺是:「啊不就 macOS 26 又把哪個權限收緊了」。打開 Console.app 看了一眼,crash report 寫得很乾脆:

Exception Type:    EXC_BAD_ACCESS (SIGKILL (Code Signature Invalid))
Termination Reason: Namespace CODESIGNING, Code 2, Invalid Page

夭壽,連 dyld 都還沒把第一個 dylib 載完就被 kernel 一刀砍了。這篇就是把整個排查到根因到修復走一遍,順便聊一下 Homebrew 在 2026 年丟出來的「Gatekeeper 一刀切」政策 後面的脈絡——因為這已經不是 LibreCAD 一個 app 的問題了,是接下來會有一卡車工具陸續陣亡。

點兩下沒反應,先別怪 LibreCAD

先把現場狀況做出來。我先確認 LibreCAD 還在:

ls /Applications/ | grep -i libre
# LibreCAD.app
# LibreOffice.app

App bundle 還在原位。再看 brew 怎麼說:

$ brew info --cask librecad
==> librecad (LibreCAD): 2.2.1.5
CAD application
https://librecad.org/
Deprecated because it does not pass the macOS Gatekeeper check!
It will be disabled on 2026-09-01.

兩條重點:已棄用、過不了 Gatekeeper。但這只是高層敘述,不是根因。直接戳簽章本身:

$ codesign -dv /Applications/LibreCAD.app
Executable=/Applications/LibreCAD.app/Contents/MacOS/LibreCAD
Identifier=LibreCAD
Format=app bundle with Mach-O thin (arm64)
CodeDirectory v=20400 size=74049 flags=0x20002(adhoc,linker-signed)
Signature=adhoc
Sealed Resources=none
Internal requirements=none

$ spctl -a -vv /Applications/LibreCAD.app
/Applications/LibreCAD.app: code has no resources but signature indicates they must be present

重點不是 Signature=adhoc——adhoc 簽章本身沒罪。是 Sealed Resources=none 加上 spctl 那句「code has no resources but signature indicates they must be present」。意思是 bundle 內附的簽章 metadata 宣稱「我這 bundle 有打包好的 sealed resources」,但實際 bundle 裡面對應的 _CodeSignature/CodeResources 檔案內容對不上。

這種「簽章說有,但實際對不到」的 mismatch 在以前的 macOS 上會被 codesign 容忍。但在 macOS 26.5 上,新的 codesigning monitor 直接在 dyld 第一次 mmap 載 page 時就觸發 fault,回報 Invalid Page——也就是 crash report 那一行 mapped file 1031c4000-103224000 [ 384K] r--/rwx SM=COW,page protection 對不上預期的 hash,kernel 立刻 SIGKILL。整個過程 LibreCAD 自己的 main() 連執行都還沒走到,所以你才會看到 dock 跳一下就沒了,連 console log 都沒留下。

為什麼以前能跑,升完 macOS 26 就崩?

macOS 26 codesigning crash report 抽象視覺化

如果你以為這是個別 app 的問題,那你只看到冰山一角。我順手 google 了一下 Namespace CODESIGNING Invalid Page,整個 macOS 26 升級季的回報就沒停過:

  • 有人回報 Xcode 升完 macOS 26 直接崩在啟動 ,crash 訊息與 LibreCAD 同款。需注意該帖至今未獲官方解法,DTS Engineer 建議的重裝、runFirstLaunch 都沒效——我引這條只是要佐證「同款 crash code 不限 LibreCAD」,不是指該帖有答案
  • 在 macOS Sonoma 14.5 期間就有 kitty 終端機 因 bundle 內檔案被改動觸發同樣的 SIGKILL (Code Signature Invalid)——這個案例不在 26 時代,但它清楚示範了「sealed resources 對不上就死」的機制,到了 26 只是門檻拉更低
  • Lakr233/vphone-cli #211 紀錄了一批舊 IPA 在 macOS 26 上全部 CODESIGNING Invalid Page 起手;要注意這是越獄/kernel patch 環境的特殊情境,不能直接類比一般使用者,但 crash code 完全相同,可以看出 kernel 那層判斷邏輯確實升級了

共通點:adhoc 簽章 + bundle 內容曾被 (重新) 寫過 / 抽換過 / 重打包過,導致 sealed resource hash 對不上現在的檔案內容。

這背後是 macOS 從 Sonoma → Sequoia → Tahoe 一路把 kernel-level codesigning monitor 收緊的結果。實際效果就是:以前 codesign 的 lazy validation(只在某些路徑驗)變成 eager validation(每個被 mmap 進來的 code page 都驗 hash)。一旦 hash 對不上,就 Invalid Page 給你看。

這邊要先承認一個對照觀點:Eclectic Light 的 Hoakley 在 2026 年 1 月寫 認為 Apple 目前並沒有預告要對 codesigning 推出更嚴格的新限制,他甚至覺得短期內收緊「不太可能」。我與他的差異在於:他談的是 Apple 的官方公告與政策層面(確實還沒有大動作),而我談的是實際使用者在新版上踩到的 kernel 行為差異——這兩件事不矛盾,但讀者要分清楚。Apple 不出 PR 不代表 kernel codesigning monitor 沒在背地裡更嚴。我這邊看到的證據是 crash report 與社群災情,而非 Apple 文件。

這也是為什麼 Homebrew 在 Discussion #6482 裡面用了「dumpster fire」這種字眼來形容 unsigned app 的 UX——他們不是要跟 Apple 站隊,是 Apple 從 kernel 把不合格的簽章 enforce 起來之後,那些靠 adhoc 簽章硬塞給使用者的 cask,再也不能「裝完就能跑」了。光是回報「裝不起來」的 issue 就會把他們淹死。

順帶一提,LibreCAD 這個 case 還有一個 cherry on top:cask 安裝過程中 Homebrew 會把 app 從 Caskroom 「搬」到 /Applications,搬完還會主動用 xattr 附加 com.apple.quarantine 旗標。搬移動作本身就會改動 bundle 的 mtime / 部分 metadata,後面再加上 quarantine xattr,對嚴格的 codesigning 驗證來說就是兩次外部介入,每一次都有可能讓 sealed resources 描述的「原始 build 樣貌」偏離。macOS 25 之前 kernel 對這類偏離還算容忍,26 一收緊就立刻起火。

三步驟治本:reinstall → 去隔離 → 重新 adhoc 簽

三步驟修復流程的意象圖

知道根因之後,治本就有方向了。三步驟按順序做,缺一不可:

# 1. 重灌 cask 拿最新版(2.2.1.5)
brew reinstall --cask librecad

# 2. 解除 quarantine 旗標(讓 Gatekeeper 不要在啟動時再驗一次)
xattr -dr com.apple.quarantine /Applications/LibreCAD.app

# 3. 重新做 adhoc 簽章,重建 sealed resources
codesign --force --deep --sign - /Applications/LibreCAD.app

我先說:只做前兩步是不夠的。我親自踩過——reinstall 完、quarantine 拔掉、open -a LibreCAD 結果還是同款 crash。crash report 依然是 Namespace CODESIGNING, Code 2, Invalid Page,乾。

為什麼?因為 quarantine 旗標只影響 Gatekeeper 在首次執行時會不會跳出「這 app 來自網路你確定要打開嗎」的對話框。把它拿掉只能避開那個 user-facing 流程,完全不影響 kernel 對 code page hash 的驗證。我這個 case 的問題是 sealed resources mismatch,那是 kernel 在 mmap 載 page 時就會 fail 的——對應到 crash report 那段 mapped file ... r--/rwx SM=COW 載入紀錄——跟 Gatekeeper 沒關係。

第三步 codesign --force --deep --sign - 才是治本。拆開來看:

  • --force:覆蓋既有簽章
  • --deep:遞迴對 bundle 內所有 nested code(plug-ins、frameworks、helper binaries)都重簽
  • --sign -:用「adhoc identity」簽——單一 dash 是 codesign 認的 magic value,定義可在本機 man codesign 查到(Apple 不提供穩定的線上 man page URL,社群可參考 Keith Smiley 維護的 Xcode man pages 非官方 mirror ,但仍以本機 man 為準)

重要的副作用是:codesign 重簽的時候會重新計算整個 bundle 的 sealed resources hash 並寫進 _CodeSignature/CodeResources。簽章宣稱的內容跟實際 bundle 對齊了,kernel 驗 page hash 就會過。

驗證簽章修好了:

$ codesign --verify --verbose=2 /Applications/LibreCAD.app
--prepared:/Applications/LibreCAD.app/Contents/PlugIns/printsupport/libcocoaprintersupport.dylib
--validated:/Applications/LibreCAD.app/Contents/PlugIns/printsupport/libcocoaprintersupport.dylib
--prepared:/Applications/LibreCAD.app/Contents/MacOS/ttf2lff
--validated:/Applications/LibreCAD.app/Contents/MacOS/ttf2lff
... (所有 dylib / framework 一路 validated)

$ open -a "LibreCAD"
$ ps aux | grep -i librecad | grep -v grep
oliver  79998  11.7  0.5 436277376 182336   ??  S   下午 1:21   0:01.04 /Applications/LibreCAD.app/Contents/MacOS/LibreCAD

PID 出來、記憶體 182MB、跑起來了。整個過程不到三分鐘。

這個指令不是 hack:LibreCAD 官方在 v2.2.1.3 release notes 就已經附上 xattr -rc LibreCAD.app && sudo codesign --force --deep --sign - LibreCAD.app 當作官方 workaround——他們很清楚 macOS 上的 unsigned build 會被打槍。2.2.1.5 release notes 沒重複這段文字,但同樣是未簽章的 dmg,所以邏輯一樣適用。我們這邊差別只是 cask 灌的、不需要 sudo,因為檔案 ownership 已經是 user 自己的。

Homebrew 為什麼要「一刀砍 387 個 cask」?

這邊我要把鏡頭拉遠一點。LibreCAD 不是孤例。根據 Homebrew 社群討論揭露的數字:

指標數字來源
Homebrew/cask 總 cask 數7,624Discussion #6482 維護者留言
因 Gatekeeper 失敗被棄用的 cask387Discussion #6482 維護者留言
占比約 5%由上兩列換算
強制 disable 日期2026-09-01brew info --cask librecad 直接讀 cask 檔案內 disable_date 程式碼欄位;release notes 措辭較模糊(「September 2026」),但個別 cask 的精確日期就是 9/1

這 387 個被點名的 cask 包含 LibreWolf、FreeTube、qutebrowser、cool-retro-term、love、darktable、powershell、qmk-toolbox 等等——很多是工程師日常工具。我自己就有 4、5 個中槍。

在 Discussion #6482 裡,Homebrew 維護者群輪番給出講白話的理由(這邊我刻意不點名到個別 ID,避免引言歸屬出錯——有興趣的人請自行翻原串):

The user experience for unsigned software is a dumpster fire.

另一位維護者補充說明:

Software installed from an official tap should just work, otherwise users are inclined to submit bug reports for things that are outside of Homebrew's control.

翻譯成台灣工程師的話就是「我們不想再幫 unsigned 上游擦屁股」。考慮到 Homebrew 完全是志工專案、每個 deprecated cask 後面都是 N 個「為什麼裝不起來」的 issue,這個決定不能說不合理。但對下游使用者來說,意思很直接:2026-09-01 之後,brew install --cask librecad 會直接給你「No available cask」。在此之前剩多少時間,請自己對照當下日期換算——倒數時鐘比寫一句固定的「還有 N 個月」實在多了。

另外維護者還補了一句:「Apple has no say over how we do anything in Homebrew」。也就是這次清洗不是 Apple 強迫的,是 Homebrew 自己對「使用者體驗 vs 維護成本」算出的天平。靠杯,但合理。

2026-09-01 之後怎麼辦?三條長期出路

棄用 cask 墓園與遠方燈塔的意象圖

不想等死,現在就要動。對 LibreCAD(以及其他類似情境)的長期策略,我整理出三條路:

路線 A:直接從 GitHub 下載官方 dmg + 自己 adhoc 簽

最直觀。到 LibreCAD/LibreCAD releases 抓最新 dmg,掛載後拖到 /Applications,然後跑:

xattr -dr com.apple.quarantine /Applications/LibreCAD.app
codesign --force --deep --sign - /Applications/LibreCAD.app

優點:版本完全跟上游,沒有 Homebrew 那層中介可能加的問題。

缺點:要手動升級。LibreCAD 官方 release 一樣沒簽 Developer ID(Issue #2054 有討論過 notarization,最後沒做),所以還是要 adhoc 簽。每次升級都要重做一次。

路線 B:自己開 tap 接手維護

Homebrew 在禁用聲明裡明說:「你可以自己開一個 tap、把這些 cask 撿回去用」。技術上:

# 假設你想自己維護 librecad cask
brew tap-new YOUR_GITHUB/casks
# 把 Homebrew/homebrew-cask 裡 librecad.rb 抄一份
# 移除 deprecate 那行
# brew install --cask YOUR_GITHUB/casks/librecad

優點:保留 brew 工作流,升級也吃 brew upgrade。

缺點:你要負責跟上 upstream 版本,並且每次更新都要驗證 cask 內附的簽章。一個人維護等於把 Homebrew 維護者拒絕做的事自己扛起來,我這肝大概撐不了。

路線 C:從 GitHub source 自己編

最硬派。git clone https://github.com/LibreCAD/LibreCAD.git → 裝 Qt6、boost、muparser → qmake6 → make。

小注意:Homebrew 裝的 Qt6 不會把 qmake6 直接放進預設 PATH,通常你需要 $(brew --prefix qt)/bin/qmake6 或自己 export PATH="$(brew --prefix qt)/bin:$PATH"。我第一次就被這個小細節絆到,浪費了 10 分鐘以為是 boost 沒裝對。

優點:你完全掌握。編譯產出的 binary 是 linker-signed(也是 adhoc 的一種),但因為是當下的 toolchain 簽的、sealed resources 是 fresh 的,跟 macOS 26 的 codesigning monitor 完全相容,不需要再手動跑 codesign。

缺點:要 30 分鐘起跳,相依套件不少,每次大版本升級都要 rebuild。如果你不熟 Qt6 build chain,第一次裝大概會踩 3、4 個坑。


我自己這次的決策過程其實有點掙扎。一開始我是想直接走路線 C 自己編——畢竟原則上最乾淨——但跑到 qmake6 階段卡了 PATH,又看到還要解 muparser 的 brew formula 跟 LibreCAD repo 內 librecad.pro 的相對路徑出入,我這肝就軟掉了。乾,後來想想 LibreCAD 我一年也開不到 5 次,花一小時編 source 完全不划算,最後落腳路線 A:抓官方 dmg + 手動 adhoc 簽。簡單、可重複、新版出來時知道怎麼補簽就好。

如果哪天我變成天天用 LibreCAD 的人,路線 B(自己開 tap)會比較划算——把 codesign 步驟寫進 cask 的 postflight,未來 brew upgrade 就會幫你自動補簽。路線 C 適合「我就是要改原始碼」的人,跟前兩條的用戶 persona 完全不同。三條路沒有絕對優劣,只有時間 / 折騰 / 自由度三軸的權衡。

Troubleshooting:常見踩坑

幾個我自己踩過或看別人踩過的坑:

Q1: 跑完三步驟還是 crash?

先確認 codesign --verify --verbose=2 /Applications/LibreCAD.app 有沒有報錯。如果中間任何一個 dylib 顯示 not validated 或 hash mismatch,代表 --deep 沒有走完。試試先用 sudo rm -rf /Applications/LibreCAD.app 砍掉再重灌,再簽一次。Apple Silicon 與 Intel 的二進位有時會混到,搬移時可能殘留舊檔。

Q2: 重新 adhoc 簽完,幾個月後又突然壞掉?

如果你裝了會主動掃 /Applications 的軟體(防毒、Time Machine 之外的某些備份工具、Sentinel 系列),它們可能會修改 bundle 內 timestamp 或 xattr,把 sealed resource hash 打偏。重跑第三步就好。或者更乾脆——寫一個 shell alias:

alias resign-librecad='codesign --force --deep --sign - /Applications/LibreCAD.app'

Q3: codesign 命令在我的機器報 errSecInternalComponent?

通常是 keychain 在搞鬼。先跑 security unlock-keychain ~/Library/Keychains/login.keychain-db 解鎖。Apple Silicon 上偶爾要重開機 codesign daemon 才會醒過來。

Q4: 為什麼不能直接 sudo spctl --global-disable 一次解掉?

兩個原因。一是這指令在 macOS Sequoia 之後需要先到「系統設定 → 隱私權與安全性」按實體按鈕確認,沒法純 CLI 解決。二更重要——它只關掉 Gatekeeper 對 unsigned app 的封鎖,並不關掉 kernel codesigning monitor。本 case 的問題在 kernel 那層,spctl 沒救。

Q5: 那 csrutil disable 呢?

別。SIP 關掉會把整個系統的安全基線打開,得不償失。本 case 不需要動到 SIP。

Q6: --deep flag 不是被 deprecated 了嗎?

技術上 Apple 在文件裡 是建議 distribution 流程用更精細的方式取代 --deep,但對「本機 adhoc 重簽」這個情境,--deep 仍然是最直接的選擇。Apple deprecate 它主要是針對 Developer ID 流程的可重現性問題,跟我們無關。

寫在最後

這次的事件對我來說,與其說是 LibreCAD 壞掉,不如說是一個提醒:Apple 在 kernel level 把 codesigning 從 advisory 變 mandatory 的這條路,沒有要回頭的意思。Sonoma → Sequoia → Tahoe 一路收緊,下一個版本只會更嚴。

對開發者來說,這代表「我隨手 build 一個 binary 給朋友裝」這種輕便的分享模式,在 macOS 上已經接近壽終正寢。要嘛你乖乖申請 Apple Developer 帳號(每年 99 美金)做 Developer ID 簽 + notarization,要嘛你的使用者就要學會自己 codesign --force --deep --sign -。沒有中間地帶。

對 macOS 使用者來說,這代表你必須多認識一點 codesign 的基本概念。不再是「裝了能跑就好」的時代了。當你下次看到 dock icon 跳一下就消失、console 沒留下任何訊息——直接打開 Console.app 找 crash report,搜尋 CODESIGNING、Invalid Page 這幾個關鍵字,大概率就是同款問題。

最後,2026-09-01 之後 Homebrew 會把這 387 個 cask 從本家停掉。我建議你現在就跑一遍:

brew list --cask | xargs -I{} brew info --cask {} 2>&1 | grep -B1 "deprecated.*Gatekeeper"

看看你的機器上有哪些 cask 在這份「死亡名單」上,提早決定要轉路線 A / B / C 哪一條。我自己跑完就發現我裝過的 LibreCAD、LibreWolf、qutebrowser、love2d 全都中槍,等到 9 月才在那邊靠杯,要一次學三個 workaround 真的很煩。

至於 LibreCAD 修不好的話——別只貼「打不開」,先去 ~/Library/Logs/DiagnosticReports/ 找最新的 LibreCAD-*.crash 或 .ips,把 Termination Reason 那行貼出來。沒這個我也只能猜,反正大概率還是 CODESIGNING / Invalid Page,那答案就是這篇文章的第三步。

參考資料

BlackHole 完整指南:讓 Mac 終於錄得到自己的聲音(2026 更新版)

一個免費開源的虛擬音訊驅動,把 macOS 自從 OS X 時代就存在的「內建工具錄不到系統音」黑洞補起來。本篇從原理、安裝、Multi-Output Device、實戰場景、踩坑到競品比較一次講完。

BlackHole 封面

為什麼 Mac 預設錄不到自己的聲音

按下 Cmd + Shift + 5 開始錄影、放一首 Spotify、停掉、回頭一聽——畫面在、聲音不見。哪怕你已經更新到 macOS Tahoe(macOS 26),Apple 自家內建的螢幕錄影工具(QuickTime、截圖工具列)到今天還是抓不到「Mac 自己播出去的聲音」。這不是你設定錯,是 Apple 本來就不讓你錄。

原因說穿了不複雜:版權與隱私風險。iTunes/Apple Music 時代,Apple 不想讓系統內建工具變成 DRM 規避器;同時系統音流裡夾雜訊息提示、視訊通話、密碼確認等敏感資訊,作業系統層級開放錄製就是惹麻煩。所以 Apple 寧可把這個能力留給有 entitlement 的第三方工具。

ScreenCaptureKit 在 macOS 13 Ventura 之後給了開發者一條合法的路(OBS Studio 從 v30 開始用它 ),但 Apple 自家的 QuickTime、螢幕截圖工具列至今沒接這個 API。你想用內建工具錄系統音?對不起,繞道走。

繞道的路通常只有一條:把 macOS 的音訊輸出「假裝」送進一張虛擬音效卡,再從那張卡的輸入端把訊號讀回來。BlackHole 就是那張虛擬卡——0 元、開源、零延遲、可商用。

BlackHole 的真身:HAL 層的虛擬音效卡

HAL 層概念

BlackHole 是 Existential Audio 的 Devin Roth 維護的開源專案,採 GPL-3.0,在 GitHub 上有近 20k stars ,是這個賽道唯一還在維護、又夠好用的免費解(前輩 Soundflower 早就停更)。它的本質是一個 Core Audio HAL(Hardware Abstraction Layer)driver——注意,不是 kernel extension,是 user-space 的 .driver bundle。

這個區別很重要。BlackHole 安裝到 /Library/Audio/Plug-Ins/HAL/BlackHoleXch.driver,不會碰 kernel,所以不會在 Apple Silicon 上被 SIP 擋下、也不需要降低系統安全等級就能裝。安裝完只會重啟 coreaudiod 這個系統 daemon,整個過程在 DeepWiki 的安裝流程 寫得很清楚。

它對 Mac 來說就是「多了一張音效卡」。輸出寫到 BlackHole 的應用程式(Spotify、Chrome、Zoom),跟從 BlackHole 讀取的應用程式(OBS、Whisper、Logic Pro),之間用記憶體 ring buffer 直接交換 32-bit float 音訊資料。延遲?官方文件寫 zero additional latency——意思是除了 Core Audio 本來就有的 buffer 延遲,BlackHole 自己不額外貢獻一毫秒。

換個說法:它就是一條長度為零的虛擬音源線,把 A app 的輸出端跟 B app 的輸入端焊在一起,繞過真實世界的喇叭跟麥克風。

三個版本怎麼選:2ch / 16ch / 64ch

BlackHole 有三個版本,名字裡的數字就是聲道數。先看表:

版本聲道數適合誰典型情境
BlackHole 2ch2(立體聲)95% 的使用者YouTube/Spotify/Zoom 錄音、螢幕錄影、AI 轉錄
BlackHole 16ch16環繞聲、DAW 跨 App5.1/7.1 監聽、Logic ↔ Ableton routing、多軌 stem
BlackHole 64ch64重度多軌玩家大型 Atmos 製作、學術 telematic 演出

官方 GitHub Discussion #290 的結論很直接——維護者本人 Devin Roth 在串裡只回了「2ch」兩個字打死,意思很白:除非你要做環繞聲或 DAW 跨 App routing,否則 2ch 就對了。

我看過太多人裝完 16ch 結果 Zoom 抓不到聲音,回頭問為什麼——因為 Zoom 預設只吃 2 聲道,遇到 16 聲道虛擬卡會誤判輸入空。夭壽,這雷我以前也踩過。

好消息是三個版本可以同時安裝,互不打架。所以保險做法:日常 2ch 主用,遇到 DAW 跨 App routing 再裝 16ch 備用。

⚠️ macOS 26 Tahoe 有個新雷:GitHub Discussion #860 指出系統在 Tahoe 上會強制把所有 16 聲道塞滿,不再 downmix 到立體聲。如果你在 Tahoe 用 16ch 版本,可能會發現某些 App 抓不到正常的左右聲道——建議直接改用 2ch 版本繞過這個問題。

安裝與設定 Multi-Output Device

Audio MIDI Setup

最快的安裝方式是 Homebrew,官方 Homebrew cask 跟著 GitHub releases 同步,當前穩定版是 0.6.1,支援 macOS 10.10 Yosemite 以上 (Intel 與 Apple Silicon 通吃):

brew install --cask blackhole-2ch
# 或 16 / 64 聲道版本
brew install --cask blackhole-16ch
brew install --cask blackhole-64ch

不用 brew 也行,官網填 email 就會寄安裝包來。安裝完開「應用程式 → 工具程式 → 音訊 MIDI 設定」(Audio MIDI Setup),左側裝置列表應該會出現 BlackHole 2ch,這代表驅動已經被 coreaudiod 認到。

接下來最容易卡住的一步:單獨選 BlackHole 當輸出,自己會聽不到聲音——因為訊號全部跑進虛擬卡的 ring buffer 裡,沒有走到真正的喇叭。這時候你需要建立 Multi-Output Device(多重輸出裝置)。

在 Audio MIDI Setup 左下角 + 按鈕點下去,選 Create Multi-Output Device,然後勾選你的喇叭/耳機 + BlackHole 2ch。重點兩個:

  1. 主裝置(Master Device)一定要選你真正的喇叭,不要選 BlackHole——時鐘要跟著實體裝置走
  2. BlackHole 那一列的 Drift Correction(漂移校正)勾起來——這一勾可以省下你日後 80% 的同步問題

Aggregate Device 跟 Multi-Output Device 差在哪?簡單講:Multi-Output 只管輸出 ,Aggregate 同時管輸入跟輸出 。錄系統音用 Multi-Output 就夠,DAW 同時要進多張音效卡才需要 Aggregate。

設定流程畫一下:

App 輸出 (Spotify / Chrome / Zoom)
         │
         ▼
   macOS System Output
         │
         ▼
   Multi-Output Device
    ├──► 實體喇叭(主時鐘,你聽到聲音)
    └──► BlackHole 2ch(Drift Correction)
                │
                ▼
         錄音 App 輸入 (OBS / QuickTime / Whisper)

把系統音輸出選成這個 Multi-Output Device,你就能一邊聽、一邊讓 BlackHole 收音。

實戰場景一次看:直播、錄影、AI 轉錄、DAW

Podcast 場景

QuickTime 錄系統音

最簡單的場景。系統輸出選你剛建好的 Multi-Output Device → 打開 QuickTime → 新增「螢幕錄影」或「音訊錄影」→ 旁邊小箭頭把麥克風來源改成 BlackHole 2ch → 錄。完成。

OBS 直播 / 串流抓 Mac 內部音

OBS 30 之後其實內建了 macOS Audio Capture,可以直接抓系統音、不需要 BlackHole。但實務上,很多人還是用 BlackHole ——一來相容更廣,二來要做「桌面音 + 麥克風 + Discord 朋友的語音」三軌分離混音時,走 BlackHole + Multi-Output Device 的設定可控性更高。

走 BlackHole 的設定:OBS → Settings → Audio → Global Audio Devices → Desktop Audio 選 BlackHole 2ch。麥克風走另一條(直接抓你的 USB mic)。這樣你的觀眾會聽到「Mac 的系統音 + 你的人聲」分離兩軌,後製超方便。

AI 會議轉錄 pipeline(Whisper / Granola / 本地 LLM)

這是 2026 年我覺得 BlackHole 最強的用法。雲端服務如 Granola 在本機抓會議音訊,再把逐字稿上傳到雲端做摘要 ;但如果你跟我一樣,公司 NDA 連逐字稿都不准離開電腦,那 100% 本地的 Whisper pipeline 就是解。

whisper.cpp 搭 BlackHole 的標準本地 pipeline:

  1. Zoom/Teams/Meet 輸出 → 系統音 Multi-Output → BlackHole
  2. 你的麥克風 → 另一條音軌
  3. 兩條音軌一起餵給 whisper.cpp(Apple Silicon 上開 Metal 加速)
  4. 轉成逐字稿後丟 Claude 或本地 LLM 做摘要

整個 pipeline 在 M2 Pro 上跑 30 分鐘會議大約三分鐘出稿,本地零雲端,香到爆。

DAW 跨 App 路由(Spotify → Logic)

把 Spotify 當素材庫的時候很好用。Spotify 輸出設成 BlackHole 16ch,Logic Pro 新增一條 audio track,輸入選 BlackHole 16ch 的對應通道,按 record——Spotify 的音訊就跑進 Logic 變成可剪輯素材。

提醒:這只能個人練習用,商業發佈當然有版權問題,別拿來搞事。

那些踩過的坑:取樣率、Drift、Tahoe 新雷

踩坑警示

這節是我自己 + 社群苦主回報的精華,按出現頻率排。我第一次認真用 BlackHole 錄 podcast 那天,光是「為什麼喇叭沒聲音」就花了 40 分鐘排查——後來才發現是踩了下面這第一個雷。

1. Multi-Output 設定完沒聲音
症狀:選了 Multi-Output Device 當輸出,喇叭一片死寂。
原因:主裝置(Master Device)順位錯了,時鐘抓到 BlackHole 上去。
解法:Audio MIDI Setup 把實體喇叭拖到最上面,並把它設為主裝置。

2. 錄音檔越錄越不同步(Drift)
症狀:錄 10 分鐘以上,聲音跟畫面慢慢飄掉。
原因:BlackHole 的虛擬時鐘跟實體裝置時鐘有微秒級偏差,累積成大誤差。
解法:Audio MIDI Setup 中把 BlackHole 那列的「Drift Correction」勾起來 。我曾經錄了一場 50 分鐘的訪談,後製剪到第 35 分鐘發現嘴型對不上聲音,幹,整集重錄。Drift Correction 這個勾真的差超多。

3. 取樣率不一致導致破音/雜訊
症狀:錄出來爆音、有奇怪 click。
原因:Spotify 是 44.1 kHz、BlackHole 設 48 kHz、喇叭又是 96 kHz,三方在打架。
解法:Audio MIDI Setup 把所有相關裝置都改成同一個取樣率(48 kHz 通常最安全)。

4. 錄音裡有回授尖叫
症狀:錄出來突然爆炸的高頻嘯叫。
原因:你把 BlackHole 同時設成輸入跟輸出,訊號自己咬自己的尾巴。
解法:檢查錄音 App 的監聽選項,不要對 BlackHole 開啟即時監聽(input monitoring)。

5. macOS Tahoe 上 16ch 版本詭異行為
症狀:升上 Tahoe 之後,原本好好的 16ch 設定突然抓不到聲音。
原因:Tahoe 改變了聲道分配邏輯 ,不再自動 downmix。
解法:換用 2ch 版本,或在 App 端手動指定要讀取的聲道。

6. 升級 macOS 後 BlackHole 不見
症狀:升完系統,BlackHole 從裝置列表消失。
原因:大版本升級會清掉部分 HAL plugin。
解法:重裝(brew reinstall --cask blackhole-2ch)即可,設定不會掉。

跟 Loopback / Audio Hijack / SoundSource 怎麼選

Rogue Amoeba 的這三套是 macOS 音訊工具的老牌商業解,常被拿來跟 BlackHole 比。一張表講完:

工具定位價格(USD)主要差異
BlackHole純路由(虛擬卡)0 元,開源沒 GUI、所有路由靠 Audio MIDI Setup 手動接,但夠用
Loopback路由(圖形化)$99拖拉式介面、可建「虛擬多 App 合成裝置」,學習曲線低
Audio Hijack路由 + 錄音 + 處理$69可以直接錄成檔案、內建效果器、適合 Podcaster
SoundSource音量/裝置控制$49不做路由,做的是 per-app 音量、per-app EQ、選單列快速切換

我自己的組合是 BlackHole 2ch 處理日常 95% 的路由,要錄音時直接用 OBS 或 QuickTime 抓 BlackHole 當輸入;SoundSource 另外加掛,純粹拿來做 per-app EQ(耳機同時開會 + 聽音樂時超實用,跟前面三套不衝突)。Audio Hijack 跟 Loopback 是好東西,後製需求多的人付錢買不虧——但 BlackHole 真的免費到讓人很難找理由先付那 99 美元。

ScreenCaptureKit 來了,BlackHole 過氣了嗎?

macOS 13 Ventura 開始,Apple 推了 ScreenCaptureKit ——一個官方 API,讓有 entitlement 的 App 可以原生抓系統音,不需要虛擬驅動。OBS 30、Screenify、ScreenSnap Pro 都已經接了。

那 BlackHole 是不是要進博物館了?短期看不會,理由四個:

  1. 內建工具沒接 ScreenCaptureKit。QuickTime、螢幕截圖工具列到 Tahoe 都還是抓不到系統音。Apple 自己沒換,BlackHole 還是繞道唯一解。
  2. 跨 App 路由 ScreenCaptureKit 做不到。它只能「我這個 App 抓系統音」,沒辦法「App A 的輸出餵給 App B 當輸入」。DAW 跨 App、AI 本地轉錄這類場景,BlackHole 沒對手。
  3. per-app 抓特定 App 音訊這件事 ScreenCaptureKit 行,但 BlackHole 搭 Audio Hijack/Loopback 也行,而且更可控。
  4. macOS 系統內部音流的歷史包袱。每個 App 對音訊路由的處理都不一樣,ScreenCaptureKit 在某些 App 上會詭異掉訊號,BlackHole 走 Core Audio 標準路徑反而穩定。

長期看,每年 macOS 大版本升級都讓 BlackHole 維護者多忙一輪,Tahoe 上的 16ch 行為變化 就是最新一例。但只要 Apple 還允許第三方 user-space HAL driver 存在,BlackHole 就還有空間。從 GitHub 仍持續發 PR 跟 release 的節奏看,短期看不到 EOL 的訊號。

結語

回頭看那場錄壞的 50 分鐘訪談,當時我以為是 macOS 的螢幕錄影哪裡又出新 bug;後來才知道,是我自己沒勾 Drift Correction,誤把鍋甩給作業系統。BlackHole 教我的不是「怎麼錄系統音」,是「怎麼真的看懂 Mac 的音訊架構」——什麼是 HAL、什麼是時鐘主裝置、為什麼 Aggregate 跟 Multi-Output 是兩件事。這些知識搞清楚之後,連 Loopback 跟 Audio Hijack 都用得更順。

實務建議:起手 brew install --cask blackhole-2ch,建一個 Multi-Output Device(主裝置選喇叭、BlackHole 勾 Drift Correction),系統輸出選它。八九成的場景到這裡就解掉。後製要錄檔再加 Audio Hijack,路由複雜到讓你頭痛再加 Loopback——但別跳過 BlackHole 這一步,免費的東西先把基礎打好,付費工具的價值才看得出來。

如果這篇幫你省下一個下午的設定地獄,把它丟給下一個被 macOS「螢幕錄影沒聲音」逼瘋的朋友吧。

參考資料

在 macOS 上安裝使用 Whisper.cpp 完整指南

📄 文件說明:本文檔詳細介紹如何在 macOS 系統上安裝、編譯和使用 Whisper.cpp 進行即時語音辨識,支援中文(繁體/簡體)。最後更新:2025年5月

📚 簡介

whisper.cpp 是 OpenAI Whisper 語音識別模型的 C/C++ 實作版本,與原版 Python 相比,具有以下優點:

  • 🚀 更快速:C++ 實作使推理速度提升 4-5 倍
  • 💻 更輕量:不需要 Python、PyTorch 或大型 ML 框架
  • 📱 更便攜:可在多種裝置上運行,包括 CPU 和 GPU 支援

主要功能

  • ✓ 即時麥克風語音辨識
  • ✓ 支援多國語言(含繁體/簡體中文)
  • ✓ 高精度語音轉錄
  • ✓ 低延遲和高效能
  • ✓ 語音活動偵測(VAD)整合
  • ✓ 時間戳標記(定位詞語時間點)
  • ✓ 輸出多種格式(文字、SRT、VTT 字幕)
  • ✓ 支援 Apple Silicon 原生加速

🛠️ 環境設置

系統需求

  • macOS 10.15 (Catalina) 或更新版本
  • 4GB+ RAM(建議 8GB+)
  • 支援 Intel 和 Apple Silicon (M1/M2/M3) 晶片

💨 快速安裝方法 (2025年最新)

使用 Homebrew 一鍵安裝 (最簡單)

2025年,Whisper.cpp 已正式提供 Homebrew 安裝套件,這是最簡單的安裝方法:

# 安裝 Whisper.cpp 和依賴的 ffmpeg
brew install whisper-cpp ffmpeg

:::tip 提示:這種方法適合快速入門,但功能相對有限。若需要最大化效能或啟用進階功能(如 Metal 加速),建議從源碼編譯安裝。 :::

檢查安裝版本

安裝後,您可以檢查 Whisper.cpp 的版本:

whisper-cpp --version

目前最新穩定版本為 v1.7.2(截至2025年5月)。

🎤 安裝 SDL2 音訊處理函式庫

whisper.cpp 的即時轉錄功能依賴 SDL2 來擷取麥克風音訊。

在終端機中執行以下命令安裝 SDL2:

brew install sdl2

📥 從源碼編譯安裝 (進階選項)

1. 複製專案

git clone https://github.com/ggerganov/whisper.cpp.git
cd whisper.cpp

# 切換到最新穩定版本(目前為v1.7.2)
git checkout -b v1.7.2 v1.7.2

2. 啟用 Metal 和 Core ML 加速 (Apple Silicon 專用)

對於 M1/M2/M3 芯片的 Mac,可以啟用 Metal 和 Core ML 加速以獲得最佳效能:

# 使用 Metal 加速(Apple Silicon 優化)
cmake -B build -DWHISPER_METAL=ON -DWHISPER_SDL2=ON

或者也可以啟用 Core ML:

# 使用 Core ML 加速
cmake -B build -DWHISPER_COREML=ON -DWHISPER_SDL2=ON

3. 標準編譯(適用於所有 Mac)

如果不需要特殊加速,可以使用標準編譯:

cmake -B build -DWHISPER_SDL2=ON
cmake --build build --config Release

編譯完成後,whisper-stream 可執行檔將位於 build/bin/ 目錄中。

🔎 模型選擇與下載

Whisper 提供多種不同大小和精確度的模型。對於中文語音辨識,建議使用多語言模型,而非僅限英文的 *.en 模型。

模型對照表 (2025年更新)

模型名稱 檔案大小 記憶體需求 相對速度 準確度 中文支援 備註
tiny 75MB 390MB 最快 最低 ✅ 基本支援 適合極度受限的裝置
base 142MB 500MB 快 較低 ✅ 支援 適合一般對話
small 466MB 1.0GB 中等 中等 ✅ 良好支援 平衡速度和準確度
medium 1.5GB 2.6GB 較慢 較高 ✅ 優良支援 適合專業轉錄
large-v3 3.1GB 5.0GB 慢 最高 ✅ 最佳支援 最高準確度
large-v3-turbo 3.0GB 4.8GB 中等 較高 ✅ 最佳支援 新: 平衡速度與準確度

注意:

  • 帶有 -q5_0 或 -q8_0 後綴的模型是量化版本,犧牲少量準確度來換取更快的速度和更小的檔案大小
  • 帶有 -turbo 後綴的模型是2024年新增的平衡型模型,提供更好的效能和精確度平衡

Apple Silicon 專用模型

對於 M1/M2/M3 芯片的 Mac,可以使用專為 Apple Silicon 優化的模型版本:

# 下載 Apple Silicon 優化版 large-v3 模型
./models/download-ggml-model.sh large-v3-applesilicon

一般模型下載

使用內建的下載腳本取得所需模型:

# 下載 base 多語言模型(支援中文,較小)
./models/download-ggml-model.sh base

# 或下載量化版 large-v3 模型(最佳中文支援,速度較快)
./models/download-ggml-model.sh large-v3-q5_0

# 或下載新的 turbo 模型(平衡速度與準確度)
./models/download-ggml-model.sh large-v3-turbo

# 或下載完整 large-v3 模型(最佳準確度,但較慢)
./models/download-ggml-model.sh large-v3

提示:下載時請使用模型名稱(如 large-v3),而非檔案名稱(如 ggml-large-v3.bin)。

🎧 音訊格式處理

Whisper.cpp 最佳支援 16kHz、16-bit 單聲道 WAV 格式。如果您的音訊檔案是其他格式,需要先進行轉換。

使用 ffmpeg 轉換音訊

# 將任何音訊檔案轉換為 Whisper 最佳支援格式
ffmpeg -i 輸入檔案.mp3 -ar 16000 -ac 1 -c:a pcm_s16le 輸出檔案.wav

從影片提取音訊

# 從影片提取音訊並轉換為適合的格式
ffmpeg -i 影片檔案.mp4 -ar 16000 -ac 1 -c:a pcm_s16le 輸出檔案.wav

🚀 執行即時語音轉錄

中文語音辨識(即時麥克風輸入)

# Homebrew 安裝版本的執行方式
whisper-cpp \
  --model <sub>/tools/ggml-large-v3-turbo.bin \
  --language zh \
  --threads 4 \
  --step 500 \
  --length 5000 \
  --vad-thold 0.6 \
  --print-colors \
  --split-on-word \
  --max-len 65

# 或使用源碼編譯版本的執行方式
./build/bin/whisper-stream \
  -m models/ggml-large-v3-q5_0.bin \
  -l zh \
  -t 4 \
  --step 500 \
  --length 5000 \
  --keep 200 \
  --vad-thold 0.6 \
  --freq-thold 100.0 \
  -ps \
  -kc \
  -f whisper_output.txt

2025年新增實用參數

參數 說明
--split-on-word 在單詞邊界分割文本,避免單詞被截斷
--print-colors 使用彩色輸出,提高可讀性
--max-len <n> 設定每行最大字元數,適合字幕製作
--no-timestamps 關閉時間戳記輸出
--output-vtt 輸出 WebVTT 格式字幕
--output-srt 輸出 SRT 格式字幕

原有參數說明

參數 說明
-m 模型檔案路徑
-l zh 語言設定為中文(支援繁體/簡體)
-t 4 使用 4 個執行緒
--step 500 每 500ms 擷取語音進行分析
--length 5000 每輪處理 5 秒音訊
--keep 200 保留前一段 200ms 防止語音被截斷
--vad-thold 0.6 語音活動偵測門檻,0.50.8 間調整
--freq-thold 100.0 高通濾波,消除背景低頻雜訊
-ps 顯示特殊 tokens,如 [NOISE]
-kc 保留語境,可提升對話連貫性
-f whisper_output.txt 將輸出儲存至文字檔

:::tip 最佳化提示:若模型辨識結果出現重複詞彙循環,可以:

  1. 降低 --keep 值至 100ms
  2. 不使用 -kc 參數
  3. 增加 --step 間隔時間至 1000ms
  4. 考慮使用未量化的完整模型 :::

📊 效能參考數據 (2025年)

以下是在不同 Apple 裝置上處理 10 分鐘中文音訊的參考時間:

裝置 模型 處理時間 即時因子
MacBook Pro M3 large-v3-turbo 1.5 分鐘 6.7x
MacBook Pro M2 large-v3-q5_0 2 分鐘 5.0x
MacBook Air M1 medium 1.8 分鐘 5.5x
Mac Mini Intel i5 small 2.5 分鐘 4.0x

即時因子:處理時間與音訊長度的比率,越高越好

📱 進階應用:集成到 iOS/macOS 應用

Whisper.cpp 2025年版本提供了 XCFramework 支援,方便開發者集成到自己的 iOS 和 macOS 應用中。

建立 XCFramework

# 在 whisper.cpp 目錄下
cd apple
./setup.sh
./build-xcframework.sh

這將在 apple/framework/whisper.xcframework 生成可直接集成到 Xcode 專案的框架。

在 Xcode 專案中使用

  1. 將生成的 whisper.xcframework 拖曳到您的 Xcode 專案
  2. 在 Build Phases > Link Binary With Libraries 中確認框架已加入
  3. 在 Swift 或 Objective-C 代碼中引用 Whisper API
import whisper

// 初始化 Whisper 上下文
let context = whisper_init_from_file("path/to/model.bin")

// 使用 Whisper 進行轉錄
// ...

// 釋放資源
whisper_free(context)

📝 常用指令範例

建立便捷執行腳本

可以創建 whisper-mic.sh 腳本,方便日後執行:

#!/bin/bash
./build/bin/whisper-stream \
  -m models/ggml-large-v3-q5_0.bin \
  -l zh \
  -t 4 \
  --step 500 \
  --length 5000 \
  --keep 200 \
  --vad-thold 0.6 \
  --freq-thold 100.0 \
  -ps \
  -kc \
  -f whisper_output.txt

加上執行權限:

chmod +x whisper-mic.sh

處理音訊檔案(非即時)

使用 whisper-cli 工具處理預先錄製的音訊檔:

./build/bin/whisper-cli \
  -m models/ggml-large-v3-q5_0.bin \
  -f samples/zh.wav \
  -l zh \
  --output-txt \
  --output-srt

自動檢測語言

# 自動檢測音訊的語言
./build/bin/whisper-cli \
  -m models/ggml-large-v3.bin \
  -f samples/audio.wav \
  --auto-language

🔍 常見問題排解

1. 辨識結果重複詞彙或句子

問題:輸出像 [_BEG_]回顧一年的房地產[_TT_100][_TT_100]回顧一年的房地產[_TT_200]... 反覆出現。

解決方案:

  • 減少 --keep 參數值
  • 移除 -kc 參數
  • 增加 --step 間隔
  • 使用更高質量的模型

2. 輸出含有 [_BEG_] 和 [_TT_] 標記

說明:

  • [_BEG_] 表示新段落開始
  • [_TT_150] 是時間戳標記(表示 1.5 秒)

這些是內部標記,用於標示轉錄時間點,一般可忽略。

3. 不想在終端顯示輸出

./build/bin/whisper-stream ... > /dev/null

這會將所有輸出導向空設備,但仍保留檔案輸出(如有使用 -f 參數)。

4. Metal 或 Core ML 加速無法使用

問題:編譯時啟用了 Metal 或 Core ML,但執行時出現錯誤。

解決方案:

  • 確保您使用的是 Apple Silicon Mac
  • 使用 xcode-select --install 確保開發工具齊全
  • 嘗試更新至最新的 macOS 版本

🌐 網頁版支援 (2025新增)

Whisper.cpp 現在也支援在網頁瀏覽器中運行(透過 WebAssembly):

  1. 在線試用:https://ggml.ai/whisper.cpp/
  2. 自行部署:
    cd examples/web
    ./build.sh
    python -m http.server
    

📚 參考資源


tags: whisper openai audio-transcription speech-recognition cpp macos

本文最初發布於 HackMD @BASHCAT。

8GB 的 RK3588 能跑多聰明的 LLM?ROCK 5C 用 NPU 實測 5 個模型

先講結論,省得你滑到最後: 在一片 8GB 的 RK3588 板子上,用 NPU 跑得最聰明的是 Qwen3-4B-Instruct-2507,我出的 7 題全對,但每秒只吐 3.7 個 token。 想要順一點的對話體驗,Qwen3.5-2B 的 8.4 tok/s 是比...