Skip to content

Module-LLM 単体オンデバイス MeloTTS 日本語発話 (#67)

AX630C (Module-LLM) 単体で日本語 TTS を生成できるか、の調査結果。

結論: 可能。ただし M5Module-LLM ライブラリが叩く標準の StackFlow デーモン経路ではなく、 ml-inory/melotts.axera の Python 経路を使う。

2 つの経路

StackFlow デーモン経路melotts.axera Python 経路 ✅
実行主体llm-melotts 1.8 (C++ デーモン)デバイス Ubuntu 上の Python
呼び出しCoreS3 → M5Module-LLM lib → UARTModule-LLM 内で直接 (CoreS3 非経由)
日本語正規化WeTextProcessing (FST _tn_/_itn_)MeCab + pykakasi + tokenizers (自前 G2P)
結果クラッシュ (ABRT)発話成功

デーモン経路がクラッシュする理由

-DSKIP_MELOTTS_SETUP=0 で焼くと melotts setup が work_id を返さず、デバイスの llm-melotts デーモンログに以下:

FstHeader::Read: Bad FST header: /opt/m5stack/data/melotts-ja-jp/
F [wetext_processor.cc:36] Invalid fst prefix, prefix should contain either "_tn_" or "_itn_"
code=killed, status=6/ABRT

/opt/m5stack/data/melotts-ja-jp/ には ja_tn_tagger.fst / ja_tn_verbalizer.fst存在し命名も正しい (_tn_ を含む) のに、デーモンがディレクトリパスを FST として 読もうとして失敗している。デーモン (1.8) と ja-jp 0.6 パッケージの組み合わせの不整合で、 ライブラリ経路では現状回避困難。NPU メモリ (CMM) 不足ではない (LLM skip で 307MB 空けても crash)。

melotts.axera 経路 (動作する方法)

ポイント: モデルファイル (encoder-jp.ort / decoder-jp.axmodel / g-jp.bin) は デバイス上の既存 ja-jp パッケージにそのまま入っているので再利用する。 不足するのは Python ランタイムだけ。Module-LLM はネット不通 (eth0 NO-CARRIER) なので 母艦 Mac で aarch64 wheel を集めて adb push する。

bash
git clone https://github.com/ml-inory/melotts.axera /tmp/melotts.axera
cd mac-studio/scripts/melotts-onboard
./01-collect-wheels.sh          # Mac で aarch64/cp310 wheel 収集
./02-push-install.sh            # adb push + offline pip install (--target /opt/melotts-rt)
./03-speak.sh "こんにちは、ドナックです"   # out.wav 生成 → Mac に pull

実行時の要点

  • 起動: cd /opt/melotts-py/python; PYTHONPATH=/opt/melotts-rt LD_LIBRARY_PATH=/opt/lib python3 tts_jp.py ...
    • axengine (pyaxengine) は /opt/lib/libax_engine.so を ctypes で叩く → LD_LIBRARY_PATH=/opt/lib 必須
    • numpy 1.24.4 をシステム (2.1.3) より優先させるため専用 target を PYTHONPATH で先頭に
  • BERT/torch は 不要 (japanese.py の transformers 経路はコメントアウト済み)
  • soundfile/libsndfile は使わない。tts_jp.py が stdlib wave で int16 PCM を書く (上流 split_utils.py の未使用 import soundfile だけ無効化する)

計測 (MC20E = AX630C, Engine 2.6.3sp)

model load            5.3 s   (プロセス毎 1 回)
MeCab 辞書ロード       3.3 s   (プロセス毎 1 回; 2 文目以降は 0.06ms)
encoder (NPU)        ~215 ms / 文
decoder (NPU)        ~225 ms / slice
→ 実質合成: ~0.9 s / 文 (ロード除く)

出力: 44100Hz / mono / int16 PCM。例: 約 22 文字 → 5.2 秒の音声。

音声出力 (#69, 解決済み ✅)

Module-LLM はスピーカーを持ち、ALSA card0 の再生デバイス pcmC0D1p で鳴らせる。 ただし 48kHz / stereo / 16bit でないと hw params が通らない (44100/mono は失敗)。

tts_jp.py --play で合成音声を 48kHz/stereo に線形リサンプル → tinyplay -D0 -d1 で再生:

bash
./03-speak.sh "やあ、ぼくドナック"   # 合成して実機スピーカーで再生

重要: llm-audio デーモンを止めずに再生できる (再生 pcmC0D1p と録音 pcmC0D0c は 別サブデバイスで競合しない)。よって KWS/whisper の mic 入力を維持したまま発話できる。

直接 Axera AO API (sample_audio ao / AX_AO_EnableDev) は失敗する (MW を llm-audio が管理)。 tinyalsa (tinyplay) で ALSA レイヤから直接叩くのが正解。 tinyplay は /opt/usr/lib/libtinyalsa.so.2 を要するので、LD_LIBRARY_PATH/opt/lib で上書きする場合は /opt/usr/lib を補うこと。

実運用へ統合する場合の残課題

  1. プロセスのコールド起動が重い (model+辞書 ~8.6s)。発話毎に起動すると遅い。 → 常駐サーバ (melotts_svr.py 相当) にして 1 回だけロードする設計が必要。
  2. 発話テキストの供給。オフライン時に誰が melotts サーバへテキストを渡すか (UART/StackFlow 経由 or CoreS3 側ロジック)。
  3. 現行ハイブリッド (TTS=Mac VOICEVOX 満別花丸) は 7 秒応答・高音質で安定。 このオンデバイス TTS が効くのは 完全オフライン (Mac 切り離し) 運用のときのみ。

関連: [[hybrid-asr-claude-voicevox]] / [[module-llm-japanese-pipeline]]

aieo-product / stack-chan project