シンプルかつMITライセンスで使いやすいTTS「MeloTTS」を試す
GitHubレポジトリ
MeloTTS
はじめに
MeloTTSは、MITおよびMyShell.aiによる高品質な多言語対応のテキスト音声変換ライブラリです。対応言語は以下の通りです:
言語 サンプル 英語(アメリカ) リンク 英語(イギリス) リンク 英語(インド) リンク 英語(オーストラリア) リンク 英語(デフォルト) リンク スペイン語 リンク フランス語 リンク 中国語(英語混在) リンク 日本語 リンク 韓国語 リンク その他の特徴:
- 中国語の話者は
中国語と英語の混在
をサポートします。CPUリアルタイム推論
に十分な高速性を持っています。使用方法
Python APIおよびモデルカードは、このリポジトリまたはHuggingFaceで確認できます。
ライセンスはMITライセンス
インストール
ローカルのMac上でPython環境を用意してパッケージインストールすることとする。
レポジトリクローン
git clone https://github.com/myshell-ai/MeloTTS.git && cd MeloTTS
Python仮想環境を作成。自分はmiseを使うが、適宜。
mise use python@3.11
cat << 'EOS' >> .mise.toml
[env]
_.python.venv = { path = ".venv", create = true }
EOS
mise trust
依存パッケージインストール
pip install -e .
辞書をダウンロード
python -m unidic download
準備はこれでOK。
TTS
MeloTTSでTTSを行うには以下の3つがある。
- WebUI
- CLI
- Python
CLI
Usageはこんな感じ。
melo --help
Usage: melo [OPTIONS] TEXT OUTPUT_PATH
Options:
-f, --file Text is a file
-l, --language [EN|ES|FR|ZH|JP|KR]
Language, defaults to English
-spk, --speaker [EN-Default|EN-US|EN-BR|EN_INDIA|EN-AU]
Speaker ID, only for English, leave empty
for default, ignored if not English. If
English, defaults to "EN-Default"
-s, --speed FLOAT Speed, defaults to 1.0
-d, --device TEXT Device, defaults to auto
--help Show this message and exit.
日本語で話させるには
melo "おはようございます。今日はいいお天気ですね。競馬観戦にはもってこいですね。" output.wav \
-l JP
初回はモデルがダウンロードされるので少しだけ時間がかかる。
で、その後TTSが行われるはずなのだけど、エラー。
NotImplementedError: Output channels > 65536 not supported at the MPS device. As a temporary fix, you can set the environment variable `PYTORCH_ENABLE_MPS_FALLBACK=1` to use the CPU as a fallback for this op. WARNING: this will be slower than running natively on MPS.
とりあえずCPUに変更したら動いた。ここは環境によって異なるかもしれない。
melo "おはようございます。今日はいいお天気ですね。競馬観戦にはもってこいですね。" output.wav \
-l JP \
-d cpu
おはようございます. 今日はいいお天気ですね.
競馬観戦にはもってこいですね.
> ===========================
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:01<00:00, 1.57it/s]
生成されたものはこちら。
あとは発話スピードを指定できる。
melo "おはようございます。今日はいいお天気ですね。競馬観戦にはもってこいですね。" output.wav \
-l JP \
-d cpu \
-s 1.5
WebUI
以下でWebUIが立ち上がる
melo-ui
以下のように表示されたら、http://127.0.0.1:7860
にアクセス
* Running on local URL: http://127.0.0.1:7860
To create a public link, set `share=True` in `launch()`.
で、テキストを入力してやってみたのだが、エラー。
コンソールにはCLIと同じエラーが出ていた。
NotImplementedError: Output channels > 65536 not supported at the MPS device. As a temporary fix, you can set the environment variable
PYTORCH_ENABLE_MPS_FALLBACK=1
to use the CPU as a fallback for this op. WARNING: this will be slower than running natively on MPS.
Usageを見てみる。
melo-ui --help
Usage: melo-ui [OPTIONS]
Options:
-s, --share Expose a publicly-accessible shared Gradio link usable
by anyone with the link. Only share the link with people
you trust.
-h, --host TEXT
-p, --port INTEGER
--help Show this message and exit.
んー、オプションで変更はできなさそう。app.pyを直接書き換える。
(snip)
import click
device = 'cpu' # 'auto'から変更
models = {
'EN': TTS(language='EN', device=device),
'ES': TTS(language='ES', device=device),
'FR': TTS(language='FR', device=device),
'ZH': TTS(language='ZH', device=device),
'JP': TTS(language='JP', device=device),
'KR': TTS(language='KR', device=device),
}
(snip)
再度WebUIを起ち上げてみると問題なく生成できた。
Python
from melo.api import TTS
speed = 1.0
device = 'cpu'
text = "おはようございます。今日はいいお天気ですね。競馬観戦にはもってこいですね。"
model = TTS(language='JP', device=device)
speaker_ids = model.hps.data.spk2id
output_path = 'jp.wav'
model.tts_to_file(text, speaker_ids['JP'], output_path, speed=speed)
python sample.py
> Text split to sentences.
おはようございます. 今日はいいお天気ですね.
競馬観戦にはもってこいですね.
> ===========================
100%|██████████████████████████████████████████████████████| 2/2 [00:01<00:00, 1.36it/s]
Docker
上のような環境起因等で動かないという場合もあるので、Windows/Macの場合はDockerが推奨されている。
試してないけども、どうやらGUIが立ち上がる感じに見える。
トレーニング
トレーニング用のコードも用意されている。日本語の場合、音声タイプが1種類しかないので、データセットがあれば、トレーニングしてみるのも良さそう。
まとめ
シンプルでMITライセンスなので使いやすいと思う。日本語の発話については、発話自体は自然できれいだけどイントネーションとかはやや変なところもあるかなというところ。
そのあたりは、自分でデータセット用意してトレーニングするのがいいのかなと思う。
> Text split to sentences. おはようございます. 今日はいいお天気ですね. 競馬観戦にはもってこいですね. > =========================== 100%|██████████████████████████████████████████████████████| 2/2 [00:01<00:00, 1.36it/s]
元の文章は、
おはようございます。
今日はいいお天気ですね。
競馬観戦にはもってこいですね。
の3文なのだが、再生してみると1つめと2つめの文章の間の無音が少ないように感じる。
tts_to_file
を見ると、split_sentences_into_pieces
を使って文章を分割、そして分割した文章ごとに音声を生成して最後にそれを全部出力しているように見える。
split_sentences_into_pieces
は以下を呼んでいる。
日本語の場合付けはないので、split_sentences_zh
が実行される。
ここで文を句読点などで分割して、文字数が最小値(min_len
)に満たない場合は2つの文を結合するということをやっている様子。
日本語向けに用意された処理ではないのでしょうがないのだけども、最初に上げた例文だと
おはようございます。
今日はいいお天気ですね。
競馬観戦にはもってこいですね。
の最初の2つが結合されてしまうので、この文間の無音区間が短くなり、最後の文の前だけ無音区間があるようになっているのではないかと思う。
このmin_len
だけども、ラテン言語(英語等)の場合も中国語の場合もデフォルトは10
になっている。ラテン言語での10文字はおそらく1〜2単語程度なので結合されたとしても10数文字でそれほど違和感がでないのかもだけど、日本語の場合は「おはようございます。」で10文字(内部的には句読点はカウントされないように思える)なので、別の文と結合されると最大20文字程度になってしまう。もっと短くしたほうがいいような気がする。
ただ、このmin_len
、TTSクラスからはさわれないので、サブクラスを作るか、コードを修正する必要がある。
サブクラスの場合
from melo.api import TTS
from melo.split_utils import split_sentence # split_sentence をインポート
class TTSWithMinLen(TTS):
def __init__(self,
language,
device='auto',
use_hf=True,
config_path=None,
ckpt_path=None,
min_len=10): # min_len を追加
# 親クラス (TTS) の初期化
super().__init__(language, device, use_hf, config_path, ckpt_path)
self.min_len = min_len # min_len を保存
# split_sentences_into_pieces をオーバーライド
def split_sentences_into_pieces(self, text, language, quiet=False):
texts = split_sentence(text, min_len=self.min_len, language_str=language)
if not quiet:
print(" > Text split to sentences.")
print('\n'.join(texts))
print(" > ===========================")
return texts
speed = 1.0
device = 'cpu'
text = "おはようございます。今日はいいお天気ですね。競馬観戦にはもってこいですね。"
model = TTSWithMinLen(language='JP', device=device, min_len=5)
speaker_ids = model.hps.data.spk2id
output_path = 'jp.wav'
model.tts_to_file(text, speaker_ids['JP'], output_path, speed=speed)
実行してみる
python sample2.py
> Text split to sentences.
おはようございます.
今日はいいお天気ですね.
競馬観戦にはもってこいですね.
分割されているのがわかる。実際に生成されたものは以下。
「おはようございます」が「オワ ヨウゴザイマス」になっているが、これは現在のモデルの学習がそうなっているため(「おはようございます」単体で生成させると再現する。)。文と文の間の無音は一定になっていることから、一応意図通りの修正はできていると思う。
なぜこれに気づいたか?というと、日本語音声での学習を試してみたら、句読点が無視されがちになったため。
- 日本語の場合、学習の前処理で句読点がうまく処理できていない(全部なくなる、英語の学習データを見ていると、句読点を含めたメタデータを作成する必要があるように思える)
- 上記を修正したところ、多少改善したような印象はあるものの(ただし個人の所感)、それでも推論時に句読点がうまく処理されていないようなところが見られた。調べたら上の処理に行き着いた
ということで、ざっと見た感じ、学習・推論共に日本語の場合はコードの改善余地がありそうに思えている。
あまりパラメータ的に指定できるものがない分、シンプルには使えるけども、細かく指定したいみたいな場合は中身を追いかける必要がある印象。
学習についてはこちらでまとめている