參考資料 ----
以 mobaXterm 管理員權限登入 AS1102TL
要生成日語,需要 pyopenjtalk,下載並在 NAS 上進行編譯。
# 先在 AppCentral 找到 Entware 並安裝,這是 NAS 的套件管理 # 安裝的所有二進位檔、函式庫與標頭檔全都統一存放在 /opt(即 /volume1/.@plugins/AppCentral/entware/opt/)底下。 user$ opkg update user$ opkg install gcc make cmake # The scripts cygdb, cython and cythonize are installed in '/volume1/.@plugins/AppCentral/python3/bin' which is not on PATH. user$ export TMPDIR=/volume1/tmp user$ ~/.local/bin/pip3 install cython unidic-lite --only-binary=:all: --no-cache-dir # 因為 pyopenjtalk 沒有提供適用於 ARM64 Linux + Python 3.12 的現成 Wheel 檔 # 所以要手動下載重新編譯 user$ cd /volume1/ # 建立暫存目錄 tmp user$ mkdir tmp user$ cd tmp user$ mkdir build_pyopenjtalk user$ cd build_pyopenjtalk user$ ~/.local/bin/pip3 download pyopenjtalk --no-binary :all: # 本筆記下載了 pyopenjtalk-0.4.1.tar.gz # 解壓縮 user$ tar -xzf pyopenjtalk-0.4.1.tar.gz # 解壓縮的檔案放在 pyopenjtalk-0.4.1 目錄 user$ cd /volume1/tmp/build_pyopenjtalk/pyopenjtalk-0.4.1/pyopenjtalk # 這指令會將 openjtalk.pyx 生成 openjtalk.cpp user$ python3 -m cython --cplus openjtalk.pyx # 以文字編輯軟體打開 openjtalk.cpp,如:vscode, notepad++...等,將這幾行插入 openjtalk.cpp 的第一列,再存檔 #include <unistd.h> #include <limits.h> #ifndef SSIZE_MAX #define SSIZE_MAX LONG_MAX #endif pyopenjtalk/openjtalk.cpp # 可透過 head -n 6 pyopenjtalk/openjtalk.cpp 確認前幾行已包含這些定義。 user$ export PATH=/opt/bin:$PATH user$ export CC=/opt/bin/gcc user$ export CXX=/opt/bin/g++ user$ export TMPDIR=/volume1/tmp # 這步驟會消耗蠻多時間...請耐心等候 user$ ~/.local/bin/pip3 install . --no-build-isolation --no-cache-dir # 日語假名/羅馬字轉換工具 user$ ~/.local/bin/pip3 install jaconv mojimoji --no-cache-dir
合成日語 tts_jp.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 ja
# 限制執行緒數量,避免在 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 = "各作品はいくつかの世界設定を共有している。例外もあるが正式な作品のクレジットにはサンライズの共同筆名である矢立肇と、シリーズ第1作を手掛けた富野由悠季の名義を入れることが慣習となっている。"
text_input = "ガンダム(IP)は、映像作品を中心に、ガンプラなどのトイホビー、コンピュータゲーム、専門雑誌やコミックなどの書籍、ネットワークコンテンツ、アミューズメント施設など、さまざまな分野に展開する多角的な戦略を通じて、日本を代表するIPへと成長を遂げている。"
# 初始化 Kokoro ONNX
kokoro = Kokoro(model_path=MODEL_PATH, voices_path=VOICES_PATH)
# 初始化日語音素處理器 (Misaki G2P)
g2p = ja.JAG2P()
res = g2p(text_input)
phonemes = res[0] if isinstance(res, tuple) else res
# 將中文轉為 Kokoro 需要的音素格式
print(f"轉換音素結果: {phonemes}")
# 呼叫 create,傳入音素
print("正在合成語音...")
samples, sample_rate = kokoro.create(
text=phonemes,
voice="jf_nezumi",
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}")













































