「TTS.cpp」を試す
先日、llama.cppでTTSが使えるのを試した。
その際に、llama.cppをベースに、TTS特化で派生させたプロジェクトがあった。
あと、llama.cppの実装を元にした他のプロジェクトもある。
Orpheus TTS、OuteTTSをサポート
https://github.com/foldl/chatllm.cppParler TTS、Kokoro、Diaをサポート
https://github.com/mmwillet/TTS.cpp
あとはMeloTTSもあるな
llama.cpp本体では現時点(2025/05/28)ではOuteTTSのみがサポートされており、他のTTSについてはllama.cpp側の対応を待つか、上記のプロジェクトを使う必要がある。
今回は上記のうちTTS.cppを試す。
GitHubレポジトリ
TTS.cpp
目的と目標
このリポジトリの一般的な目的は、GGMLテンソルライブラリを使用して、一般的なデバイスアーキテクチャにおいてオープンソースTTS(テキスト音声変換)モデルを用いたリアルタイム生成をサポートすることです。GGML上では、高速なSTT(音声認識)、埋め込み生成、およびLLM生成がすでに whisper.cpp や llama.cpp を通じて広くサポートされています。したがって、本リポジトリは、それらの機能を補完する形で、同様に最適化され移植可能なTTSライブラリを提供することを目指します。
この取り組みにおいては、MacOSおよびMetalサポートが主要なプラットフォームとして扱われ、機能はまずMacOS向けに開発され、その後他のOSへと拡張されます。
サポートされている機能
警告! 現在、TTS.cppは概念実証(Proof of Concept)段階であり、さらなる開発が予定されています。現時点の機能はMacOS X以外の環境ではテストされていません。
モデルサポート
モデル CPU Metalアクセラレーション 量子化 GGUFファイル Parler TTS Mini ✓ ✓ ✓ こちら Parler TTS Large ✓ ✓ ✓ こちら Kokoro ✓ ✗ ✗ こちら Dia ✓ ✓ ✓ こちら 追加モデルのサポートは、TTS Model Arena におけるオープンソースモデルの性能およびアーキテクチャやチェックポイントの可用性に基づいて優先的に追加されます。
機能対応状況
実装予定機能 OS X Linux Windows 基本的なCPU生成 ✓ ✓ ✗ Metalアクセラレーション ✓ _ _ CUDAサポート _ ✗ ✗ 量子化 ✓_*_ ✗ ✗ レイヤーオフロード ✗ ✗ ✗ サーバーサポート ✓ ✗ ✗ Vulkanサポート _ ✗ ✗ Komputeサポート _ ✗ ✗ ストリーミング音声 ✗ ✗ ✗ * 現在は生成モデルのみがこれに対応しています。
パフォーマンス
本ライブラリの中心的な目標は、OS Xにおけるリアルタイム音声生成をサポートすることです。このため、生成速度のベンチマークは対応モデル(Parler Mini バージョン1.0)を用いてOS X環境にて集中的に実施されています。
DAC音声デコーダモデルへのMetalアクセラレーションの導入により、標準的なApple M1 Max上で約3GBのメモリ消費でほぼリアルタイムの音声生成が可能となっています。加速モデルにおける最良のリアルタイム係数は現在1.112033です。これは、1秒の音声生成に約1.112033秒の処理時間を要することを意味します(生成モデルにQ5_0量子化を適用した場合)。最新のベンチマーク統計については perf_batteryのREADME を参照してください。
Mac(M2 Pro)で試す
レポジトリクローン
git clone https://github.com/mmwillet/TTS.cpp && cd TTS.cpp
GGMLのフォーク版もクローン
git clone -b support-for-tts https://github.com/mmwillet/ggml.git
ビルド。テキスト→音素への変換はEspeak-ngを使えるようになっているらしいのでこれを有効化しておく。Espeak-ngはHomebrewでインストールできる。
export ESPEAK_INSTALL_DIR=/opt/homebrew/opt/espeak-ng
cmake -B build
cmake --build build --config Release
モデルをダウンロード。モデルの種類はREADMEからHuggingFaceに飛んで確認。ここでは models
ディレクトリを作成してそこにダウンロードしている。
mkdir models
pushd models
# Parler-TTS
# miniとlargeがあり、それぞれF32、Q5がある。
# ここではlarge・Q5をダウンロード
wget --content-disposition https://huggingface.co/mmwillet2/Parler_TTS_GGUF/resolve/main/Parler_TTS_large_Q5.gguf?download=true
# Dia
# Q4、Q5,Q8、F16、F32がある。
# またそれに`_DAC_F16`がついているものは16ビットDACエンコーダでエンコーダされたバージョン。
# 今回はQ5・16ビットDACエンコーダ版を使う
wget --content-disposition https://huggingface.co/mmwillet2/Dia_GGUF/resolve/main/Dia_Q5_DAC_F16.gguf?download=true
# Kokoro-TTS
# Espeak-ng対応版・非対応版がある様子。
# 英語だけならEspeak-ng非対応のもので良さそうだが、日本語でも使いたいのと一部記号を上手く処理できないみたいなので、Espeak-ng対応版を。
wget --content-disposition https://huggingface.co/mmwillet2/Kokoro_GGUF/resolve/main/Kokoro_espeak.gguf?download=true
popd
推論は./build/bin/cli
を使う。Usageは以下。
./build/bin/cli --help
--temperature (-t):
The temperature to use when generating outputs. Defaults to 1.0.
--repetition-penalty (-r):
The by channel repetition penalty to be applied the sampled output of the model. defaults to 1.0.
--top-p (-tp):
(OPTIONAL) the sum of probabilities to sample over. Must be a value between 0.0 and 1.0. Defaults to 1.0.
--n-threads (-nt):
The number of cpu threads to run generation with. Defaults to hardware concurrency. If hardware concurrency cannot be determined then it defaults to 1.
--topk (-tk):
(OPTIONAL) When set to an integer value greater than 0 generation uses nucleus sampling over topk nucleaus size. Defaults to 50.
--max-tokens (-mt):
(OPTIONAL) The max audio tokens or token batches to generate where each represents approximates 11 ms of audio. Only applied to Dia generation. If set to zero as is its default then the default max generation size. Warning values under 15 are not supported.
--use-metal (-m):
(OPTIONAL) Whether to use metal acceleration
--no-cross-attn (-ca):
(OPTIONAL) Whether to not include cross attention
--vad (-va):
(OPTIONAL) whether to apply voice inactivity detection (VAD) and strip silence form the end of the output (particularly useful for Parler TTS). By default, no VAD is applied.
--play:
(OPTIONAL) Whether to play back the audio immediately instead of saving it to file.
--model-path (-mp):
(REQUIRED) The local path of the gguf model file for Parler TTS mini or large v1, Dia, or Kokoro.
--prompt (-p):
(REQUIRED) The text prompt for which to generate audio in quotation markers.
--save-path (-sp):
(OPTIONAL) The path to save the audio output to in a .wav format. Defaults to TTS.cpp.wav
--conditional-prompt (-cp):
(OPTIONAL) A distinct conditional prompt to use for generating. If none is provided the preencoded prompt is used. '--text-encoder-path' must be set to use conditional generation.
--text-encoder-path (-tep):
(OPTIONAL) The local path of the text encoder gguf model for conditional generaiton.
--voice (-v):
(OPTIONAL) The voice to use to generate the audio. This is only used for models with voice packs.
--espeak-voice-id (-eid):
(OPTIONAL) The espeak voice id to use for phonemization. This should only be specified when the correct espeak voice cannot be inferred from the kokoro voice ( see MultiLanguage Configuration in the README for more info).
では各TTSごとに細かいオプションなどは置いといてまずはざっと実行。
Parler-TTS
./build/bin/cli \
--model-path models/Parler_TTS_large_Q5.gguf \
--prompt "Good morning. It's a beautiful day today. On days like this, I feel like going to the horse races." \
--save-path tts-cpp-parlertts-sample.wav
Dia(Diaだけはプロンプトに[S1]
を追加している)
./build/bin/cli \
--model-path models/Dia_Q5_DAC_F16.gguf \
--prompt "[S1] Good morning. It's a beautiful day today. On days like this, I feel like going to the horse races." \
--save-path tts-cpp-dia-sample.wav
Kokoro-TTS
./build/bin/cli \
--model-path models/Kokoro_espeak.gguf \
--prompt "Good morning. It's a beautiful day today. On days like this, I feel like going to the horse races." \
--save-path tts-cpp-kokoro-tts-sample.wav
CLIのオプションやTTSごとの違いなどは以下にある。
少しTTSごとに異なるオプションを試してみる。
Kokoro TTS
TTS.cppで対応しているTTSのうち日本語が使えるのはKokoro-TTSだけ。--voice
で j*
で始まる声を選択すればいいはず。
./build/bin/cli \
--model-path models/Kokoro_espeak.gguf \
--prompt "おはようございます。今日はいいお天気ですね。こんな日は競馬に行きたくなりますね。" \
--save-path tts-cpp-kokoro-tts-sample-ja.wav \
--voice "jf_alpha"
うーん、これは・・・途中までしか生成されてないし、しかも発音もおかしい。
本来のKokoro-TTSではg2pにmisakiというライブラリを使っているので、Espeak-ngとは多分音素化された結果が違うのだと思うし、Espeak-ngだとひらがな・カタカナしか受け取れないはず。
Espeak-ngで音素化
espeak-ng -v jpx/ja -x "おはようございます。今日はいいお天気ですね。こんな日は競馬に行きた くなりますね。"
,ohaj,oug,ozaim'asu
k_j,ouh,ai:,oteNk,ides'une
k,onnaC,ihak,eib,ani:k,itak,unar`,imas'une
misakiで音素化
from misaki import ja, en
g2p = ja.JAG2P()
phonemes = g2p("おはようございます。きょうはいいおてんきですね。こんなひはけいばにいきたくなりますね。")
print(phonemes)
('ohajoː ɡoʣaimasɨ kʲoː βa iː o teŋkʲi desɨ ne konna çiβa keːba ɲi ikʲi takɯ naɾʲimasɨ ne', None)
この感じだと日本語は厳しそう。。。。せめて音素を直接指定できるといいんだけどな。
参考
Dia
上にも書いたが、Diaは複数話者の音声を生成でき、笑い声などの非言語音声タグを指定することができる。TTS.cppで実行する場合は --topk 35 --temperature 1.3
を設定したほうが良いらしい。
./build/bin/cli \
--model-path models/Dia_Q5_DAC_F16.gguf \
--prompt "[S1] Good morning. It's a beautiful day today. [S2] Yep. On days like this, I feel like going to the horse races. [S1] (laughs) Definitely." \
--save-path tts-cpp-dia-sample2.wav \
--topk 35 \
--temperature 1.3
ただ、生成がめちゃめちゃ遅い・・・デフォルトだとCPU推論っぽいので、--use-metal
を有効にしてみたのだが、それでも遅い。
参考
Parler-TTS
Parler-TTSはプロンプトに対応している。このプロンプトは--conditional-prompt
で指定するのだが、これをエンコードするための google/flan-t5-large
のGGUFモデルが必要になる。そのためのPythonスクリプトが用意されているので、それを使う。このあたりは以下のモデルの量子化についてのドキュメントに記載がある。
python仮想環境を作成
uv venv -p 3.12.9
uv pip install pip
source .venv/bin/activate
py-ggufディレクトリに移動してパッケージインストール
cd py-gguf
pip install -r requirements.txt
スクリプトを実行。この時 --save-path
はなぜかカレントディレクトリが指定できないので(no such file or directory
になる)、他のモデルと同じパスを指定している。
python ./convert_t5_encoder_to_gguf --save-path ../models/t5-encoder-large.gguf --large-model
モデルがダウンロードされ、GGUF変換される。
WARNING:parler_tts.modeling_parler_tts:Flash attention 2 is not installed
INFO:gguf.gguf_writer:gguf: This GGUF file is for Little Endian only
INFO:T5Encoder:Preparing tensors for GGUF file, t5-encoder-large.gguf.
config.json: 100%|█████████████████████████████████████████████████████████████████| 7.72k/7.72k [00:00<00:00, 7.89MB/s]
model.safetensors.index.json: 100%|█████████████████████████████████████████████████| 94.5k/94.5k [00:00<00:00, 618kB/s]
Downloading shards: 0%| | 0/2 [00:00<?, ?it/s]
model-00001-of-00002.safetensors: 16%|███████ | 776M/4.98G [01:23<07:32, 9.31MB/s]
(snip)
INFO:T5Encoder:Preparing configuration for GGUF file, ../models/t5-encoder-large.gguf.
INFO:T5Encoder:Beginning to write to GGUF file, ../models/t5-encoder-large.gguf.
INFO:gguf.gguf_writer:Writing the following files:
INFO:gguf.gguf_writer:../models/t5-encoder-large.gguf: n_tensors = 221, total_size = 4.9G
Writing: 100%|██████████████████████████████████████████████████████████████████| 4.91G/4.91G [00:04<00:00, 1.02Gbyte/s]
INFO:T5Encoder:Finishing GGUF converstion process for file, ../models/t5-encoder-large.gguf.
クローンしたレポジトリのルートに戻って
cd ..
推論。--text-encoder-path
で先程のモデルを指定、--consditional-prompt
でプロンプトを指定。
./build/bin/cli \
--model-path models/Parler_TTS_large_Q5.gguf \
--prompt "I am saying some words" \
--text-encoder-path models/t5-encoder-large.gguf \
--conditional-prompt "male deep voice" \
--save-path tts-cpp-parlertts-sample2.wav
うーん、、、
Segmentation fault: 11
ちょっとわかんないな。Linux環境でやり直してみたいところ。
参考
OpenAI互換のAPIサーバもある
まとめ
本家llama.cppより対応しているTTSモデルが多いんだけど、日本語という観点ではちょっと厳しいかな。英語で普通に使うならまあ・・・というところではあるが、READMEに、
この取り組みにおいては、MacOSおよびMetalサポートが主要なプラットフォームとして扱われ、機能はまずMacOS向けに開発され、その後他のOSへと拡張されます。
警告! 現在、TTS.cppは概念実証(Proof of Concept)段階であり、さらなる開発が予定されています。現時点の機能はMacOS X以外の環境ではテストされていません。
とあって、個人的にはMacで使うのはMLX−Audioに任せてしまえばいいのでは?という気がしなくもない。Macネイティブだし、モデルもそこそこあるし。
どっちかというとLinuxやWindowsのほうが選択肢としての魅力はありそうな気がするんだけどね、あくまでも個人的な所感だけど。
とはいえ、TTSモデルがGGUF化されてポータブルに使えるようになるといろいろ嬉しいことが多いと思う。llama.cpp本家でのTTS対応もまだ始まったばかりのようだし、今後に期待。