2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLM-8850 で Kokoro 音声合成を試してみた

Last updated at Posted at 2025-12-10

Hugging Face の AXERA-TECH で AX650N 用の Kokoro デモが公開されていたので LLM-8850 で試してみました。

環境作成

デモのダウンロード

git lfs をインストールしていない場合は、ここの手順に従ってインストールしてください。

Hugging Face から kokoro.axera をダウンロードします。

git clone https://huggingface.co/AXERA-TECH/kokoro.axera

デモの実行に必要な axengine もダウンロードしておきます。

git clone https://huggingface.co/AXERA-TECH/PyAXEngine

uv 仮想環境の作成

READNE.md の説明では conda を使っていますが、uv を使ってみます。uv をインストールしていない場合は以下でインストールできます。

curl -LsSf https://astral.sh/uv/install.sh | sh

依存Pythonパッケージをインストールします。

cd kokoro.axera
uv init
uv add -r requirements.txt
uv add axengine@file://../PyAXEngine/axengine-0.1.3-py3-none-any.whl 
uv run python -m spacy download en_core_web_sm

デモの実行

uv run demo_kokoro_ax.py --text \
  "こんにちは、これはテキスト読み上げのテストです。" \
  --lang j --voice checkpoints/voices/jf_alpha.pt \
  --output output_jp.wav -d models

以下のようなメッセージが表示されます。

[INFO] Available providers:  ['AXCLRTExecutionProvider']
初始化模型...
[INFO] Using provider: AXCLRTExecutionProvider
[INFO] SOC Name: AX650N
[INFO] VNPU type: VNPUType.DISABLED
[INFO] Compiler version: 5.0-patch1-dirty d9477bd2-dirty
[INFO] Using provider: AXCLRTExecutionProvider
[INFO] SOC Name: AX650N
[INFO] VNPU type: VNPUType.DISABLED
[INFO] Compiler version: 5.0-patch1-dirty d9477bd2-dirty
[INFO] Using provider: AXCLRTExecutionProvider
[INFO] SOC Name: AX650N
[INFO] VNPU type: VNPUType.DISABLED
[INFO] Compiler version: 5.0-patch1-dirty d9477bd2-dirty
2025-12-10 23:27:51.282885442 [W:onnxruntime:Default, device_discovery.cc:164 DiscoverDevicesForPlatform] GPU device discovery failed: device_discovery.cc:89 ReadFileContents Failed to open file: "/sys/class/drm/card1/device/vendor"
初始化完成: 4.191s


============================================================
输出: output_jp.wav | 时长: 3.52s
============================================================
初始化: 4.191s
音频推理: 0.488s (共1次)
  ├─ Model1: 0.021s (平均21.0ms)
  ├─ Model2: 0.017s (平均17.3ms)
  ├─ Model3: 0.202s (平均202.2ms)
  └─ Model4 onnx: 0.086s (平均86.3ms)

 rtf:0.138
============================================================

途中、GPUが見つからない旨のメッセージが表示されていますが、これは onnxruntime モジュールのインポート時に出るもので、このメッセージが出ても ONNX モデルは CPU で処理されるので問題ありません。

生成された音声は output_jp.wav ファイルに出力されます。以下で再生できます。

aplay output_jp.wav

デモスクリプトのパラメータ

パラメータ 説明
--axmodel_dir, -d モデルファイルのディレクトリ(デフォルト models)
--voice, -v 声紋ファイルのパス(必須)
--text, -t 合成するテキスト(多言語対応)
--lang, -l 言語コード(例: a, z, j, ...)。現状は中国語、英語、日>本語のみ検証済み
--config, -c 設定ファイルのパス
--output, -o 出力する wav ファイル名
--fade_out, -f 音声末尾のフェードアウト時間(秒)。音声末尾のノイズ軽>減のため
--max_len, -m 最大文分割長(デフォルト 96)

TTSサービス

Kokoro を利用した音声合成サービスのデモもあります。 kokoro_svr.py というものですが、中国語音声にしか対応していないので、とりあえず日本語に対応した kokoro_svr_jp.py にしてみました。差分は次のとおりです。

--- kokoro_svr.py	2025-12-08 21:52:48.734836469 +0900
+++ kokoro_svr_jp.py	2025-12-10 00:30:22.692752456 +0900
@@ -13,7 +13,7 @@
 _tts_cache = {}
 
 
-def get_tts(lang = "z"):
+def get_tts(lang = "j"):
     axmodel_dir = "models"
     config = "checkpoints/config.json"
     if lang not in _tts_cache:
@@ -67,7 +67,7 @@
         if not sentence:
             self._send_json({"error": "Field 'sentence' is required"}, 400)
             return
-        lang = params.get("language", "z")
+        lang = params.get("language", "j")
         try:
             sample_rate = int(params.get("sample_rate", 24000))
         except ValueError:
@@ -84,7 +84,7 @@
         # 生成音频
         try:
             tts = get_tts(lang)
-            audio = tts.run(sentence, speed=speed, sample_rate=sample_rate, voice = "checkpoints/voices/zf_xiaoyi.pt")
+            audio = tts.run(sentence, speed=speed, sample_rate=sample_rate, voice = "checkpoints/voices/jf_alpha.pt")
         except Exception as e:
             self._send_json({"error": f"TTS failed: {e}"}, 500)
             return

TTSサービスを起動します。

uv run kokoro_svr_jp.py --port 28000

別のターミナルから以下を実行してみます。

curl  -X POST "http://127.0.0.1:28000/tts" \
  -H 'Content-Type: application/json' \
  -d '{ "sentence": "こんにちは、これはテキスト読み上げのテストです。" }' \
  --output tts_jp.wav

生成された音声は tts_jp.wav ファイルに出力されます。以下で再生できます。

aplay tts_jp.wav

2025/12/27 追記

2025/12/26 に kokoro.axera が更新され、上記記事から以下が変更になりました。

  1. ボイス用ファイルの形式が .pt (PyTorch モデル形式)から .npy (.npy 形式ファイル)に変更
  2. gradio を用いた Web ブラウザ UI を追加

kooro_svr.py の変更

1 によって kokoro_svr.py も変更になったので、日本語用に書き換えていた kokoro_svr_jp.py も変更します。ついでにデフォルトのポートも変更してしまいます(8000 のままだと OpenAI API のポートとかぶってしまうので)。

--- kokoro_svr.py	2025-12-27 18:41:42.609301820 +0900
+++ kokoro_svr_jp.py	2025-12-27 18:50:35.220352964 +0900
@@ -74,7 +74,7 @@
         if not sentence:
             self._send_json({"error": "Field 'sentence' is required"}, 400)
             return
-        lang = params.get("language", "z")
+        lang = params.get("language", "j")
         try:
             sample_rate = int(params.get("sample_rate", 24000))
         except ValueError:
@@ -88,7 +88,7 @@
             return
         
         
-        voice = params.get("voice", "zf_xiaoyi")
+        voice = params.get("voice", "jf_alpha")
         if voice not in voice_list:
             self._send_json({"error": f"Voice {voice} not found"}, 400)
             return
@@ -124,7 +124,7 @@
 
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description="Whisper Server")
-    parser.add_argument("--port", type=int, default=8000, help="Port to run the server on")
+    parser.add_argument("--port", type=int, default=28000, help="Port to run the server on")
     args = parser.parse_args()
     host = "0.0.0.0"
     port = args.port

動かすには scipy のインストールが必要になりました。次のコマンドでインストールしておいてください。

uv add scipy

Gradio による Web UI デモ

Gradioを使った Web UI デモが追加になりました。使うには、ます Gradio をインストールします。

uv add gradio

デモスクリプトは gradio_demo.py というものですが、UI のラベルなどが中国語なので、日本語にしてみた gradio_demo_jp.py にしてみました。差分は次のとおりです。

--- gradio_demo.py	2025-12-26 20:33:37.447867328 +0900
+++ gradio_demo_ja.py	2025-12-27 18:52:50.559960867 +0900
@@ -10,10 +10,10 @@
     result = subprocess.run(['ip', 'a'], capture_output=True, text=True)
     output = result.stdout
 
-    # 匹配所有IPv4
+    # すべての IPv4 アドレスを得る
     ips = re.findall(r'inet (\d+\.\d+\.\d+\.\d+)', output)
 
-    # 过滤掉回环地址
+    # ループバックアドレスを除外
     real_ips = [ip for ip in ips if not ip.startswith('127.')]
 
     return real_ips
@@ -50,20 +50,20 @@
     lang_key = LANG_CODES_REV[language]  # 例:Mandarin Chinese -> z
     filtered_voices = [v for v in voice_list.keys() if v.startswith(lang_key)]
     
-    # 例句填充
+    # 例文を挿入
     example_text = EXAMPLE_SENTENCES.get(lang_key, "")
     
     if not filtered_voices:
         return gr.update(value=example_text), gr.update(value=None, choices=[])
 
     return (
-        gr.update(value=example_text),               # 更新输入文本
-        gr.update(value=filtered_voices[0], choices=filtered_voices) # 更新音色
+        gr.update(value=example_text),               # 入力テキストを更新
+        gr.update(value=filtered_voices[0], choices=filtered_voices) # 音色を更新
     )
 
 voice_list = {}
 
-# 加载checkpoints/voices下的所有pt文件的文件名作为key
+# checkpoints/voices_npy 配下にあるすべての .npy ファイルのファイル名をキーとして読み込む
 voice_list = glob.glob("checkpoints/voices_npy/*.npy")
 voice_list = {os.path.basename(v).replace(".npy", ""): v for v in voice_list}
 
@@ -90,20 +90,20 @@
     
     with gr.Row():
         with gr.Column():
-            sentence = gr.Textbox(label="输入文本",value="爱芯元智半导体股份有限公司,致力于打造世界领先的人工智能感知与边缘计算芯片.")
-            language = gr.Dropdown(label="选择语言", choices=list(LANG_CODES_REV.keys()), value="Japanese")
+            sentence = gr.Textbox(label="テキストを入力",value="こんにちは、これはテキスト読み上げのテストです。")
+            language = gr.Dropdown(label="言語を選択", choices=list(LANG_CODES_REV.keys()), value="Japanese")
             speed = gr.Slider(label="速度", minimum=0.5, maximum=2.0, value=1.0, step=0.1)
             voice = gr.Dropdown(
-                        label="选择音色",
+                        label="音色を選択",
                         choices=list(v for v in voice_list.keys() if v.startswith(LANG_CODES_REV["Japanese"])),
                         value=list(v for v in voice_list.keys() if v.startswith(LANG_CODES_REV["Japanese"]))[0] if voice_list else None,
                         allow_custom_value=True
                     )
-            generate = gr.Button("生成音频")
+            generate = gr.Button("音声を生成")
         with gr.Column():
-            audio = gr.Audio(label="输出音频")
+            audio = gr.Audio(label="音声を出力")
             
-        # 点击生成按钮时,调用服务器端的TTS API
+        # 生成ボタンをクリックすると、サーバー側の TTS API を呼び出す
         generate.click(
             fn=tts,
             inputs=[sentence, language, speed, voice],
@@ -115,10 +115,11 @@
             outputs=[sentence, voice],
         )
 
-# 启动
+# 起動
 ips = get_all_local_ips()
 port = 7861
 for ip in ips:
     print(f"* Running on local URL:  http://{ip}:{port}")
 ip = "0.0.0.0"
+os.makedirs("history", exist_ok=True)
 demo.launch(server_name=ip, server_port=port)

TTSサービスを起動します。

uv run kokoro_svr_jp.py

別のターミナルでデモスクリプトを起動します。

uv run gradio_demo_jp.py

Web ブラウザから次の URL にアクセスします。

http://127.0.0.1:7861

テキストを入力して「音声を生成」ボタンをクリックすると、hostory/ 配下に合成した音声ファイルが出力されます。生成した音声の再生も Web UI 上で可能です。

スクリーンショット 2025-12-27 22.07.39.png

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?