2026-10-13

在 ASUSTOR AS1102TL NAS 跑 kokoro tts(text-to-speech) 生成 mp3 語音檔

參考資料 ----

kokoro


以 mobaXterm 管理員權限登入 AS1102TL

因為 NAS AppCentral 的 FFmpeg 並不具生成 mp3 的能力,所以需要另外下載
 
user$  cd  /volume1/Web/kokoro/
user$  wget  https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz
user$  tar  -xf  ffmpeg-release-arm64-static.tar.xz
 


安裝 python3 的套件管理工具 pip
 
user$  curl  -sS  https://bootstrap.pypa.io/get-pip.py  -o  get-pip.py

# 透過本機 python3 執行安裝(建議加上 --user 避免破壞系統預設權限)
user$  python3  get-pip.py  --user

# 因為有加 --user 參數
# 所以 pip 會安裝在 user 家目錄的 ~/.local/bin
 




安裝輕量版的 kokoro-onnx
 
# 怕將 pip 路徑加入環境變數會對 NAS 造成影響,因此暫不加
# 執行 pip 時就必須輸入完整的 pip 路徑
user$   ~/.local/bin/pip3.12  install  --no-cache-dir  kokoro-onnx  soundfile  pydub

# 因為需中文音素支援,所以還要安裝 misaki 的中文模組
user$  ~/.local/bin/pip3.12  install  --no-cache-dir  kokoro-onnx  "misaki[zh]"
 




下載音色庫
 
# 建立一個目錄存放,在本筆記,我放在 /volume1/Web/kokoro

# 下載 ONNX 模型權重 (約 300MB)
user$  wget  -O  kokoro-v1.0.onnx  https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx

# 下載音色庫 voices-v1.0.bin (約 25MB)
user$  wget  -O  voices-v1.0.bin  https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin
 


想知道有哪些音色
 
user$  python3  -c  "from  kokoro_onnx  import  Kokoro;  k=Kokoro('/volume1/Web/kokoro/kokoro-v1.0.onnx', '/volume1/Web/kokoro/voices-v1.0.bin'); print(k.get_voices())"
 

紅框內就是 kokoro 的角色音色,
第一個字母代表語言:
z:中文 (Zho / Chinese)
a:美式英語 (American English)
b:英式英語 (British English)
j:日語 (Japanese)
e:西班牙語 (Español)
f:法語 (Français)
h:印地語 (Hindi)
i:義大利語 (Italian)
p:巴西葡萄牙語 (Portuguese)

第二個字母代表性別,f 是女性(female),m 是男性(male)。


警告訊息可以不理會,因為 joblib(底層用來處理並行運算的套件)在啟動時會嘗試在共享記憶體目錄(通常是 /dev/shm)建立旗標或暫存檔。在 ASUSTOR ADM 系統下,一般使用者帳號預設對 /dev/shm 沒有寫入權限,因此 joblib 自動降級為單線程序列模式(serial mode)運作。

因為在 1GB RAM 的 NAS 上本來就建議以單線程/序列模式執行以防爆記憶體,這個降級對生成語音完全沒有負面影響。


合成中文 tts_zh.py
 
import os
import warnings
import io
import onnxruntime as ort
import soundfile as sf
from kokoro_onnx import Kokoro
from pydub import AudioSegment
from pydub.utils import which
from misaki import zh


# 限制執行緒數量,避免在 1GB RAM NAS 上過載
os.environ["OMP_NUM_THREADS"] = "2"
os.environ["ORT_NUM_THREADS"] = "2"

warnings.filterwarnings("ignore", category=UserWarning, module="joblib")

# 將靜態 ffmpeg 所在目錄放到環境變數 PATH 最前端
FFMPEG_DIR = "/volume1/Web/kokoro/ffmpeg-7.0.2-arm64-static"
os.environ["PATH"] = FFMPEG_DIR + ":" + os.environ.get("PATH", "")

# 明確指定使用支援 libmp3lame 的 ffmpeg 執行檔路徑
AudioSegment.converter = os.path.join(FFMPEG_DIR, "ffmpeg")
AudioSegment.ffmpeg = os.path.join(FFMPEG_DIR, "ffmpeg")


# 檔案路徑設定
MODEL_PATH = "/volume1/Web/kokoro/kokoro-v1.0.onnx"
VOICES_PATH = "/volume1/Web/kokoro/voices-v1.0.bin"
OUTPUT_MP3 = "/volume1/Web/mp3/lee_mood.mp3"


# 目標字串
text_input = "你現在是怎樣的心情呢?是歡喜悲傷,還是一點點不知名的愁?"


# 初始化 Kokoro ONNX
kokoro = Kokoro(model_path=MODEL_PATH, voices_path=VOICES_PATH)

# 初始化中文音素處理器 (Misaki G2P)
g2p = zh.ZHG2P()
res = g2p(text_input)
phonemes = res[0] if isinstance(res, tuple) else res

# 將中文轉為 Kokoro 需要的音素格式
print(f"轉換音素結果: {phonemes}")

# 呼叫 create,傳入音素 (lang 改用 'en-us' 或省略驗證,is_phonemes=True)
print("正在合成語音...")
samples, sample_rate = kokoro.create(
    text=phonemes,
    voice="zm_yunyang",
    speed=1.0,
    is_phonemes=True
)


# 轉存為 MP3
# 先在記憶體中建立輕量 WAV 緩衝區,再由 pydub 調用 ffmpeg 封裝成 MP3
wav_buffer = io.BytesIO()
sf.write(wav_buffer, samples, sample_rate, format="WAV")
wav_buffer.seek(0)

audio_seg = AudioSegment.from_wav(wav_buffer)
audio_seg.export(OUTPUT_MP3, format="mp3", bitrate="192k")

print(f"完成!音訊已輸出至: {OUTPUT_MP3}")
 


合成美語 tts_en.py
 
import os
import warnings
import io
import onnxruntime as ort
import soundfile as sf
from kokoro_onnx import Kokoro
from pydub import AudioSegment
from pydub.utils import which
# from misaki import zh


# 限制執行緒數量,避免在 1GB RAM NAS 上過載
os.environ["OMP_NUM_THREADS"] = "2"
os.environ["ORT_NUM_THREADS"] = "2"

warnings.filterwarnings("ignore", category=UserWarning, module="joblib")

# 將靜態 ffmpeg 所在目錄放到環境變數 PATH 最前端
FFMPEG_DIR = "/volume1/Web/kokoro/ffmpeg-7.0.2-arm64-static"
os.environ["PATH"] = FFMPEG_DIR + ":" + os.environ.get("PATH", "")

# 明確指定使用支援 libmp3lame 的 ffmpeg 執行檔路徑
AudioSegment.converter = os.path.join(FFMPEG_DIR, "ffmpeg")
AudioSegment.ffmpeg = os.path.join(FFMPEG_DIR, "ffmpeg")


# 檔案路徑設定
MODEL_PATH = "/volume1/Web/kokoro/kokoro-v1.0.onnx"
VOICES_PATH = "/volume1/Web/kokoro/voices-v1.0.bin"
OUTPUT_MP3 = "/volume1/Web/mp3/lee_mood.mp3"


# 目標字串
text_input = "你現在是怎樣的心情呢?是歡喜悲傷,還是一點點不知名的愁?"
text_input = "American professors Robert E. Hall and Michael Woodford have won the 2026 Nobel prize in economics for developing new economic models that help governments and central banks stabilize their economies to minimize the impact of external shocks. "


# 初始化 Kokoro ONNX
kokoro = Kokoro(model_path=MODEL_PATH, voices_path=VOICES_PATH)


# 合成美英語音
print("正在合成美英語音...")
samples, sample_rate = kokoro.create(
    text=text_input,
    voice="af_alloy",      # 美式女聲 (亦可更換為 am_adam, af_sarah, af_nicole 等)
    speed=1.0,
    lang="en-us"           # 指定美語
)


# 轉存為 MP3
# 先在記憶體中建立輕量 WAV 緩衝區,再由 pydub 調用 ffmpeg 封裝成 MP3
wav_buffer = io.BytesIO()
sf.write(wav_buffer, samples, sample_rate, format="WAV")
wav_buffer.seek(0)

audio_seg = AudioSegment.from_wav(wav_buffer)
audio_seg.export(OUTPUT_MP3, format="mp3", bitrate="192k")

print(f"完成!音訊已輸出至: {OUTPUT_MP3}")
 



沒有留言:

張貼留言