Closed8

シンプルかつMITライセンスで使いやすいTTS「MeloTTS」を試す

kun432kun432

GitHubレポジトリ

https://github.com/myshell-ai/MeloTTS

MeloTTS

はじめに

MeloTTSは、MITおよびMyShell.aiによる高品質な多言語対応のテキスト音声変換ライブラリです。対応言語は以下の通りです:

言語 サンプル
英語(アメリカ) リンク
英語(イギリス) リンク
英語(インド) リンク
英語(オーストラリア) リンク
英語(デフォルト) リンク
スペイン語 リンク
フランス語 リンク
中国語(英語混在) リンク
日本語 リンク
韓国語 リンク

その他の特徴:

  • 中国語の話者は中国語と英語の混在をサポートします。
  • CPUリアルタイム推論に十分な高速性を持っています。

使用方法

Python APIおよびモデルカードは、このリポジトリまたはHuggingFaceで確認できます。


ライセンスはMITライセンス

kun432kun432

インストール

https://github.com/myshell-ai/MeloTTS/tree/main/docs/install.md

ローカルの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。

kun432kun432

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]

生成されたものはこちら。

https://audio.com/kun432/audio/melo-tts-jp-1

あとは発話スピードを指定できる。

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を直接書き換える。

melo/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

sample.py
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]
kun432kun432

まとめ

シンプルでMITライセンスなので使いやすいと思う。日本語の発話については、発話自体は自然できれいだけどイントネーションとかはやや変なところもあるかなというところ。

そのあたりは、自分でデータセット用意してトレーニングするのがいいのかなと思う。

kun432kun432
> Text split to sentences.
おはようございます. 今日はいいお天気ですね.
競馬観戦にはもってこいですね.
> ===========================
100%|██████████████████████████████████████████████████████| 2/2 [00:01<00:00,  1.36it/s]

元の文章は、

  • おはようございます。
  • 今日はいいお天気ですね。
  • 競馬観戦にはもってこいですね。

の3文なのだが、再生してみると1つめと2つめの文章の間の無音が少ないように感じる。

tts_to_fileを見ると、split_sentences_into_piecesを使って文章を分割、そして分割した文章ごとに音声を生成して最後にそれを全部出力しているように見える。

https://github.com/myshell-ai/MeloTTS/blob/main/melo/api.py#L74-L85

split_sentences_into_piecesは以下を呼んでいる。

https://github.com/myshell-ai/MeloTTS/blob/main/melo/split_utils.py#L9-L14

日本語の場合付けはないので、split_sentences_zhが実行される。

https://github.com/myshell-ai/MeloTTS/blob/main/melo/split_utils.py#L9-L48

ここで文を句読点などで分割して、文字数が最小値(min_len)に満たない場合は2つの文を結合するということをやっている様子。

https://github.com/myshell-ai/MeloTTS/blob/main/melo/split_utils.py#L26-L48

https://github.com/myshell-ai/MeloTTS/blob/main/melo/split_utils.py#L77-L101

日本語向けに用意された処理ではないのでしょうがないのだけども、最初に上げた例文だと

  • おはようございます。
  • 今日はいいお天気ですね。
  • 競馬観戦にはもってこいですね。

の最初の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.
おはようございます.
今日はいいお天気ですね.
競馬観戦にはもってこいですね.

分割されているのがわかる。実際に生成されたものは以下。

https://audio.com/kun432/audio/melo-tts-jp-2

「おはようございます」が「オワ ヨウゴザイマス」になっているが、これは現在のモデルの学習がそうなっているため(「おはようございます」単体で生成させると再現する。)。文と文の間の無音は一定になっていることから、一応意図通りの修正はできていると思う。


なぜこれに気づいたか?というと、日本語音声での学習を試してみたら、句読点が無視されがちになったため。

  • 日本語の場合、学習の前処理で句読点がうまく処理できていない(全部なくなる、英語の学習データを見ていると、句読点を含めたメタデータを作成する必要があるように思える)
  • 上記を修正したところ、多少改善したような印象はあるものの(ただし個人の所感)、それでも推論時に句読点がうまく処理されていないようなところが見られた。調べたら上の処理に行き着いた

ということで、ざっと見た感じ、学習・推論共に日本語の場合はコードの改善余地がありそうに思えている。

あまりパラメータ的に指定できるものがない分、シンプルには使えるけども、細かく指定したいみたいな場合は中身を追いかける必要がある印象。

このスクラップは2024/12/26にクローズされました