Local TTS
預設語音:Qwen3-TTS 1.7B CustomVoice,說話者 aiden,8-bit GGUF (Q8_0),由 qwentts.cpp (C++/ggml, MIT) 執行,並使用以下風格指令:
溫暖、清晰且充滿好奇,就像一位友善的科技解說影片旁白。節奏放鬆平穩。
它在盲聽測試中擊敗了 Kokoro、Chatterbox、PyTorch Qwen3-TTS、VoxCPM2 和 Phonon(詳見 references/benchmarks.md)。在 8 GB RTX 4070 筆記型電腦 GPU 上,包含模型載入時間,執行速度約為即時速度的 4 倍:一段 18 分鐘的旁白只需約 4.5 分鐘即可生成。
設定(每台機器只需一次)
首先執行 python -I <skill>/scripts/say.py --check。如果輸出 OK,請跳過此章節。否則請設定引擎,或請使用者協助設定。整個過程約需 10-20 分鐘,並佔用約 2.5 GB 的磁碟空間。
- 建置 qwentts.cpp。 目前尚無預先建置的 Windows 二進位檔,因此請從原始碼建置:
在 Windows 上,請安裝 VS 2022(或更新版本)Build Tools(需包含 C++ 工作負載)、CMake、Ninja 和 CUDA Toolkit,然後從儲存庫執行git clone --recurse-submodules https://github.com/ServeurpersoCom/qwentts.cpp ~/tools/qwentts.cpp cd ~/tools/qwentts.cpp ./buildcuda.sh # NVIDIA(Linux);若為 AMD/Intel 請使用 ./buildvulkan.sh;若僅使用 CPU(例如 macOS)請使用 ./buildcpu.shbuildcuda.cmd。如果vcvars64.bat位於其他位置,請編輯該腳本頂部的路徑。若要僅針對一個 GPU,請在 cmake 指令行中加入-DCMAKE_CUDA_ARCHITECTURES=89(RTX 40xx;86 = RTX 30xx,120 = RTX 50xx)。 - 下載權重(約 2 GB)從 Hugging Face 的
Serveurperso/Qwen3-TTS-GGUF:pip install -U huggingface_hub hf download Serveurperso/Qwen3-TTS-GGUF qwen-talker-1.7b-customvoice-Q8_0.gguf qwen-tokenizer-12hz-Q8_0.gguf --local-dir ~/tools/qwen-gguf - 將 say.py 指向這些資料夾(如果您使用了其他資料夾):設定
QWEN_TTS_HOME(檢查目錄)、QWEN_TTS_EXE(二進位檔)或QWEN_TTS_MODELS(.gguf 資料夾)。在 Windows 上,say.py會自動為您將%CUDA_PATH%\bin\x64加入 PATH(可透過CUDA_BIN覆蓋)。 - 再次執行
say.py --check,然後測試一行文字。
硬體需求:8 GB VRAM 足以執行 Q8_0(約使用 3 GB)。如果沒有 GPU,CPU 版本的執行速度約為 GPU 版本的四分之一。設定 GGML_BACKEND=CPU 可強制 GPU 版本使用 CPU 執行。
生成前
- GPU 是否被其他程式佔用? 遊戲或其他 GPU 密集型應用程式會使生成速度降低約 4 倍,並導致該package 發生卡頓。在開始長時間任務前請先詢問使用者。如果使用者提供了此類應用程式的名稱,請設定
TTS_BUSY_APPS=app1,app2,當say.py偵測到這些應用程式正在執行時會發出警告。 - 是否需要檢查更新的模型? 請參閱下方「持續尋找更好的模型」。如果上次檢查已超過約 6 週,且任務是實際的旁白(非一行測試),請先進行快速掃描。
- 文字聽起來是否自然? 語音朗讀 AI 產生的文字時,每次都會使用相同的語調,因此模式會比在頁面上更明顯。對於超過幾行長的旁白,請閱讀來自
whiteboard-explainer技能(相同插件,references/)的natural-narration.md,並執行其中的scripts/aispeak_scan.py,該腳本接受 script.py 或純文字檔案。
生成
此腳本僅使用標準程式庫,因此任何 Python 3.10+ 版本皆可執行。<skill> 是此技能所在的資料夾。
python -I <skill>/scripts/say.py "第一行。" "第二行。" -o out.wav # 一個 WAV 檔案,間隔 0.35 秒
python -I <skill>/scripts/say.py --file lines.txt -o clips/ # 000.wav, 001.wav, ...
選項:
| 標記 | 功能說明 |
|---|---|
--speaker | aiden(預設);ryan 是另一個原生英語語音 |
--instruct | 風格文字,例如「興奮、快節奏」或「冷靜、嚴肅」 |
--seed N | 產生可重複的結果 |
--gap S | 合併檔案中行與行之間的靜音秒數 |
--srt F | 同時生成與合併 WAV 時間對應的字幕(請參閱字幕) |
--quant | Q8_0(預設,最佳)或 Q4_K_M(較小,音質略差;請先下載) |
--check | 報告找到的引擎、模型和 CUDA 資料夾 |
運作原理:
- 每一行文字都透過同一個程序處理,因此模型只會載入一次(3-8 秒)。請批次傳入行數,而不是每行呼叫一次。
- 輸出為 24 kHz 單聲道 16 位元 WAV。
每次生成的結果都不同:
- 保留您喜歡的音檔。根據文字和設定作為鍵值,對每一行進行快取。
- 如果某一行輸出的長度與預期(約 0.07 秒/字元)相差甚遠,請重新生成。這表示模型可能遺漏或發明了詞語。
對於影片旁白,也請:
- 將每個音檔的開頭和結尾靜音修剪至約 50 毫秒,然後在時間軸上自行設定間隔放置各行。
- 對最終音軌進行音量正規化:
ffmpeg -af loudnorm=I=-16:TP=-1.5:LRA=11。
字幕 (.srt):為每個有旁白的影片提供此功能
您已經知道每行文字的開始和結束時間,因此無需進行轉錄。直接根據這些時間戳記建立字幕即可。
- 搭配
say.py: 加入--srt narration.srt(需要使用.wav的-o輸出)。字幕會與合併後的音軌完全吻合。 - 在自己的流程中: 呼叫
scripts/srt.py,to_srt([(start_s, end_s, text), ...], display=[...]),其中參數為每個口說行在影片時間中的開始/結束時間。或使用 CLI:python srt.py timings.tsv -o video.srt。 - 修正語音拼寫。 旁白文字通常是為語音朗讀而寫的:「D I L」、人名以語音方式重新拼寫、「runner dot HTML」、數字以單字表示。請使用
display/--display display.tsv(口說書寫)將它們對應回去。書寫形式應採用螢幕上顯示的內容,數字使用阿拉伯數字(「$232」、「83%」)。 - 檢查結果。 使用 grep 搜尋殘留的大寫字母間隔(
[A-Z] [A-Z])和「 dot 」。 - 模組套用的規則:
- 每行字幕最多兩行,每行 42 個字元
- 斷句優先級:句子結尾 > 子句結尾 > 單字結尾
- 合併短句以避免閃爍
- 時間按長度比例分配
- 命名規則: 與影片同名(
影片名稱.srt放在影片名稱.mp4旁邊),以便播放器自動載入。在 VLC 中,可使用--sub-file=強制載入檔案。
whiteboard-explainer 技能中的 template/narration/build_vo.py 是一個完整的實作流程:包含逐行快取、重新生成、靜音修剪、暫停整形、單字對齊、提示表以及唇形同步包絡線。
注意事項
- CUDA DLL 必須在 PATH 中(Windows)。 CUDA 13 將它們放在
...\CUDA\v13.x\bin\x64中。如果該資料夾不在 PATH 中,qwen-tts.exe會無聲退出且不產生任何輸出。say.py會自動為您加入。 --stream-by-line -o -會寫入串流 WAV 標頭(大小為 0x7fffffff),每個輸出一行。請在RIFF....WAVEfmt處分割,並丟棄每個 44 位元組的標頭。請勿相信大小欄位。- Aiden 可能會誤讀單個字母和奇怪的標記。 由未經提示的 Whisper 審計發現:
- 「h star」輸出為「a hap」或「our star」,「y t」輸出為「VT」,「y」輸出為「we」,「arXiv」輸出為「ArcSireFolk」。
- 以下寫法有效:大寫的「H star」/「under H」、「why tee」/「why tilde」、「archive」和「SMILES check」。
- 「aitch」表現不穩定。一個名字單獨出現時可能沒問題,但放在句子中間可能就會亂掉:請在整句文字中測試。
- 句子開頭的 K 可能會被省略(「Kwenputer」→「When Pooter」):請在這樣的句子開頭加上「And」或「So」。
- 審計方法:在不使用腳本作為初始提示的情況下轉錄每個音檔,然後與腳本進行比對。使用提示的 Whisper 會「聽到」預期的單字並隱藏錯誤。工具:
whiteboard-explainer/template/narration/audit_vo.py和tts_try.py。
- 冒號處的停頓。 Qwen 在冒號處會停頓 0.6-0.8 秒,有時在短語中間也會停頓。在旁白中,每個靜音的長度取決於其前面的標點符號(範本中
build_vo.py的shape()函數),並避免將一行文字在冒號處斷開(例如「首先:...」)。 - CPU 備援方案: Kokoro 82M (
kokoro-onnx) 在 CPU 上音質較平淡但速度快,適合在 GPU 忙碌時用於草稿。
持續尋找更好的模型
本地 TTS 技術進步迅速,當前的選擇很快會被超越。請將其視為現任冠軍,而非最終答案。特別是在執行大型旁白任務前,或上次檢查已超過約 6 週時,請尋找更新、更好的模型。
- 掃描。 搜尋網路以尋找自上次檢查以來發布或更新的開放權重 TTS。查看:
- TTS Arena / Artificial Analysis 語音排行榜
- Hugging Face 趨勢文字轉語音模型
- r/LocalLLaMA
- 下方引擎的 GitHub 發布版本
- 篩選。 僅當候選模型滿足以下條件時才保留:
- 可在本地免費使用(開放權重,許可證寬鬆或允許個人使用)
- 可在使用者的 GPU 記憶體和作業系統上執行
- 支援所需的語言
- 在品質上擊敗現任冠軍,或在品質相同的情況下速度快得多
- 試用。 使用候選模型生成
eval/lines.txt。使用scripts/voice_metrics.py(pip install numpy librosa;冠軍和基準錄音在eval/中)對其進行評分:- 音高標準差 ≥ 約 5 個半音且音高範圍 ≥ 約 17 個半音才被認為聽起來自然。
- 測量速度:每秒生成的音訊秒數。
- 聆聽。 請使用者在候選模型和
eval/champion_*.wav(ffplay -nodisp -autoexit file.wav)之間進行盲聽選擇。指標僅用於篩選候選者;最終決定權在耳朵。 - 記錄。 無論是否有任何改變,請更新下方的「上次檢查日期」和
references/benchmarks.md。如果冠軍變更,請取代eval/champion_*.wav並更新say.py,且在刪除舊模型前先詢問使用者。
上次檢查日期:2026-11-02(網路調查加上本地 VoxCPM2 Q8 和 Phonon 31m/Miles 試用)。Aiden 在盲聽測試中勝過這兩者。尚未在本地嘗試的模型:Phonon 40m、OmniVoice、Breeze TTS 2、Drama 3、Orpheus、Dia、Sesame CSM、Spark-TTS、IndexTTS-2、VibeVoice、Higgs Audio、Kyutai TTS 和 Fish/OpenAudio。詳細資訊請參閱 references/benchmarks.md。