Merge branch 'dev'

This commit is contained in:
litagin02
2024-02-07 16:32:19 +09:00
20 changed files with 196 additions and 192 deletions

2
.gitignore vendored
View File

@@ -25,3 +25,5 @@ venv/
*.csv
*.bak
/mos_results/
safetensors.ipynb

View File

@@ -8,9 +8,9 @@ https://github.com/litagin02/Style-Bert-VITS2/assets/139731664/e853f9a2-db4a-420
- [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://colab.research.google.com/github/litagin02/Style-Bert-VITS2/blob/master/colab.ipynb)
- [🤗 オンラインデモはこちらから](https://huggingface.co/spaces/litagin/Style-Bert-VITS2-JVNV)
- [**リリースページ**](https://github.com/litagin02/Style-Bert-VITS2/releases/tag/2.0)、[更新履歴](docs/CHANGELOG.md)
- [**リリースページ**](https://github.com/litagin02/Style-Bert-VITS2/releases/)、[更新履歴](docs/CHANGELOG.md)
- 2024-02-05: ver 2.0.1
- 2024-02-07: ver 2.1
- 2024-02-03: ver 2.0
- 2024-01-09: ver 1.3
- 2023-12-31: ver 1.2
@@ -25,7 +25,7 @@ This repository is based on [Bert-VITS2](https://github.com/fishaudio/Bert-VITS2
- GitやPythonがない人でもWindowsユーザーなら簡単にインストールでき、学習もできます (多くを[EasyBertVits2](https://github.com/Zuntan03/EasyBertVits2/)からお借りしました)。またGoogle Colabでの学習もサポートしています: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](http://colab.research.google.com/github/litagin02/Style-Bert-VITS2/blob/master/colab.ipynb)
- 音声合成のみに使う場合は、グラボがなくてもCPUで動作します。
- 他との連携に使えるAPIサーバーも同梱しています ([@darai0512](https://github.com/darai0512) 様によるPRです、ありがとうございます)。
- 元々「楽しそうな文章は楽しそうに、悲しそうな文章は悲しそうに」読むのがBert-VITS2の強みですので、このフォークで付加されたスタイル指定を無理に使わずとも感情豊かな音声を生成することができます。
- 元々「楽しそうな文章は楽しそうに、悲しそうな文章は悲しそうに」読むのがBert-VITS2の強みですので、スタイル指定がデフォルトでも感情豊かな音声を生成することができます。
## 使い方
@@ -141,7 +141,7 @@ python speech_mos.py -m <model_name>
- [EasyBertVits2](https://github.com/Zuntan03/EasyBertVits2)のように、PythonやGitを知らない人でも簡単に使える。
- 感情埋め込みのモデルを変更256次元の[wespeaker-voxceleb-resnet34-LM](https://huggingface.co/pyannote/wespeaker-voxceleb-resnet34-LM)へ、感情埋め込みというよりは話者識別のための埋め込み)
- 埋め込みもベクトル量子化を取り払い、単なる全結合層に。
- 感情埋め込みもベクトル量子化を取り払い、単なる全結合層に。
- スタイルベクトルファイル`style_vectors.npy`を作ることで、そのスタイルを使って効果の強さも連続的に指定しつつ音声を生成することができる。
- 各種WebUIを作成
- bf16での学習のサポート
@@ -149,15 +149,12 @@ python speech_mos.py -m <model_name>
- その他軽微なbugfixやリファクタリング
## TODO
- [ ] デフォルトのJVNVモデルにJP-Extra版のものを追加
- [x] デフォルトのJVNVモデルにJP-Extra版のものを追加
- [x] LinuxやWSL等、Windowsの通常環境以外でのサポート ← おそらく問題ないとの報告あり
- [x] 複数話者学習での音声合成対応(学習は現在でも可能)
- [ ] 本家のver 2.1, 2.2, 2.3モデルの推論対応ver 2.1以外は明らかにめんどいのでたぶんやらない)
- [x] `server_fastapi.py`の対応、とくにAPIで使えるようになると嬉しい人が増えるのかもしれない
- [x] モデルのマージで声音と感情表現を混ぜる機能の実装
- [ ] 英語等多言語対応?
- [ ] ONNX対応
## References
In addition to the original reference (written below), I used the following repositories:

11
app.py
View File

@@ -19,6 +19,7 @@ from common.constants import (
DEFAULT_SPLIT_INTERVAL,
DEFAULT_STYLE,
DEFAULT_STYLE_WEIGHT,
LATEST_VERSION,
Languages,
)
from common.log import logger
@@ -36,6 +37,8 @@ languages = [l.value for l in Languages]
def tts_fn(
model_name,
model_path,
text,
language,
reference_audio_path,
@@ -54,7 +57,7 @@ def tts_fn(
use_tone,
speaker,
):
assert model_holder.current_model is not None
model_holder.load_model_gr(model_name, model_path)
wrong_tone_message = ""
kata_tone: Optional[list[tuple[str, int]]] = None
@@ -190,8 +193,8 @@ examples = [
],
]
initial_md = """
# Style-Bert-VITS2 ver 2.0 音声合成
initial_md = f"""
# Style-Bert-VITS2 ver {LATEST_VERSION} 音声合成
注意: 初期からある[jvnvのモデル](https://huggingface.co/litagin/style_bert_vits2_jvnv)は、[JVNVコーパス言語音声と非言語音声を持つ日本語感情音声コーパス](https://sites.google.com/site/shinnosuketakamichi/research-topics/jvnv_corpus)で学習されたモデルです。ライセンスは[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.ja)です。
"""
@@ -418,6 +421,8 @@ if __name__ == "__main__":
tts_button.click(
tts_fn,
inputs=[
model_name,
model_path,
text_input,
language,
ref_audio_path,

View File

@@ -10,6 +10,8 @@
"もっといろいろ足したりいろんなスタイルベクトルを作って遊べると思います。\n",
"ある程度慣れている人向けです。\n",
"\n",
"JVNVコーパスのように**スタイルが既にファイル名等で分かれている場合は、最後の方のセルを使えばそれを利用してスタイルを作ることができます。**\n",
"\n",
"例では[JVNVコーパス](https://sites.google.com/site/shinnosuketakamichi/research-topics/jvnv_corpus)のjvnv-M1を使います。\n",
"\n",
"## そもそもスタイルベクトルとは\n",
@@ -204,6 +206,14 @@
"np.save(\"model_assets/jvnv-M1/style_vectors.npy\", save_vectors)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 正解ラベルがファイル名から分かる場合の作り方\n",
"JVNVコーパス等でファイル名によってスタイルラベルが分かる場合、以下のようにしてスタイルベクトルを作ることができますデフォルトのJVNVモデルのスタイルはこれで作成しています。"
]
},
{
"cell_type": "code",
"execution_count": 8,
@@ -211,24 +221,9 @@
"outputs": [],
"source": [
"# JVNVコーパスの場合は正解はファイル名の最初の方から分かる。それを使って正解ラベルを作成\n",
"y_true = [name[3:6] for name in names]\n",
"label_dict = {\"ang\": 0, \"dis\": 1, \"fea\": 2, \"hap\": 3, \"sad\": 4, \"sur\": 5}\n",
"\n",
"for i, y in enumerate(y_true):\n",
" if y == \"ang\":\n",
" y_true[i] = 0\n",
" elif y == \"dis\":\n",
" y_true[i] = 1\n",
" elif y == \"fea\":\n",
" y_true[i] = 2\n",
" elif y == \"hap\":\n",
" y_true[i] = 3\n",
" elif y == \"sad\":\n",
" y_true[i] = 4\n",
" elif y == \"sur\":\n",
" y_true[i] = 5\n",
" else:\n",
" print(y)\n",
" raise ValueError"
"y_true = [label_dict[name[3:6]] for name in names]"
]
},
{

View File

@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Style-Bert-VITS2 (ver 2.0) のGoogle Colabでの学習\n",
"# Style-Bert-VITS2 (ver 2.1) のGoogle Colabでの学習\n",
"\n",
"Google Colab上でStyle-Bert-VITS2の学習を行うことができます。\n",
"\n",

View File

@@ -1,5 +1,6 @@
import enum
LATEST_VERSION: str = "2.1"
DEFAULT_STYLE: str = "Neutral"
DEFAULT_STYLE_WEIGHT: float = 5.0

View File

@@ -209,6 +209,18 @@ class ModelHolder:
raise ValueError(f"Model `{model_name}` is not found")
if model_path not in self.model_files_dict[model_name]:
raise ValueError(f"Model file `{model_path}` is not found")
if (
self.current_model is not None
and self.current_model.model_path == model_path
):
# Already loaded
speakers = list(self.current_model.spk2id.keys())
styles = list(self.current_model.style2id.keys())
return (
gr.Dropdown(choices=styles, value=styles[0]),
gr.Button(interactive=True, value="音声合成"),
gr.Dropdown(choices=speakers, value=speakers[0]),
)
self.current_model = Model(
model_path=model_path,
config_path=os.path.join(self.root_dir, model_name, "config.json"),

View File

@@ -1,15 +1,19 @@
"""
@Desc: 全局配置文件读取
"""
import argparse
import os
import shutil
from typing import Dict, List
import torch
import yaml
from common.log import logger
# If not cuda available, set possible devices to cpu
cuda_available = torch.cuda.is_available()
class Resample_config:
"""重采样配置"""
@@ -44,13 +48,23 @@ class Preprocess_text_config:
max_val_total: int = 10000,
clean: bool = True,
):
self.transcription_path: str = transcription_path # 原始文本文件路径,文本格式应为{wav_path}|{speaker_name}|{language}|{text}。
self.cleaned_path: str = cleaned_path # 数据清洗后文本路径,可以不填。不填则将在原始文本目录生成
self.train_path: str = train_path # 训练集路径,可以不填。不填则将在原始文本目录生成
self.val_path: str = val_path # 验证集路径,可以不填。不填则将在原始文本目录生成
self.transcription_path: str = (
transcription_path # 原始文本文件路径,文本格式应为{wav_path}|{speaker_name}|{language}|{text}。
)
self.cleaned_path: str = (
cleaned_path # 数据清洗后文本路径,可以不填。不填则将在原始文本目录生成
)
self.train_path: str = (
train_path # 训练集路径,可以不填。不填则将在原始文本目录生成
)
self.val_path: str = (
val_path # 验证集路径,可以不填。不填则将在原始文本目录生成
)
self.config_path: str = config_path # 配置文件路径
self.val_per_lang: int = val_per_lang # 每个speaker的验证集条数
self.max_val_total: int = max_val_total # 验证集最大条数,多于的会被截断并放到训练集中
self.max_val_total: int = (
max_val_total # 验证集最大条数,多于的会被截断并放到训练集中
)
self.clean: bool = clean # 是否进行数据清洗
@classmethod
@@ -83,6 +97,8 @@ class Bert_gen_config:
):
self.config_path = config_path
self.num_processes = num_processes
if not cuda_available:
device = "cpu"
self.device = device
self.use_multi_device = use_multi_device
@@ -104,6 +120,8 @@ class Style_gen_config:
):
self.config_path = config_path
self.num_processes = num_processes
if not cuda_available:
device = "cpu"
self.device = device
@classmethod
@@ -143,7 +161,7 @@ class Train_ms_config:
class Webui_config:
"""webui 配置"""
"""webui 配置 (for webui.py, not supported now)"""
def __init__(
self,
@@ -155,6 +173,8 @@ class Webui_config:
share: bool = False,
debug: bool = False,
):
if not cuda_available:
device = "cpu"
self.device: str = device
self.model: str = model # 端口号
self.config_path: str = config_path # 是否公开部署,对外网开放
@@ -182,6 +202,8 @@ class Server_config:
origins: List[str] = None,
):
self.port: int = port
if not cuda_available:
device = "cpu"
self.device: str = device
self.language: str = language
self.limit: int = limit

View File

@@ -67,5 +67,5 @@
"use_spectral_norm": false,
"gin_channels": 256
},
"version": "2.0.1"
"version": "2.1"
}

View File

@@ -74,5 +74,5 @@
"initial_channel": 64
}
},
"version": "2.0.1-JP-Extra"
"version": "2.1-JP-Extra"
}

View File

@@ -1,5 +1,16 @@
# Changelog
## v2.1 (2024-02-07)
### 変更
- 学習の際、デフォルトではbfloat16オプションを使わないよう変更学習が発散したり質が下がることがある模様
- 学習の際のメモリ使用量を削減しようと頑張った
### バグ修正や改善
- 音声合成やそのAPIにおいて、同時に別の話者が選択され音声合成がリクエストされた場合に発生するエラーを修正
- モデルマージ時に、そのレシピを`recipe.json`ファイルへ保存するように変更
- 「改行で分けて生成」がより感情が乗る旨の明記等、軽微な説明文の改善
## v2.0.1 (2024-02-05)
軽微なバグ修正や改善

View File

@@ -90,17 +90,18 @@ def download_jvnv_models():
)
parser = argparse.ArgumentParser()
parser.add_argument("--skip_jvnv", action="store_true")
args = parser.parse_args()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--skip_jvnv", action="store_true")
args = parser.parse_args()
download_bert_models()
download_bert_models()
download_slm_model()
download_slm_model()
download_pretrained_models()
download_pretrained_models()
download_jp_extra_pretrained_models()
download_jp_extra_pretrained_models()
if not args.skip_jvnv:
download_jvnv_models()
if not args.skip_jvnv:
download_jvnv_models()

View File

@@ -7,7 +7,7 @@ gradio
jaconv
jieba
langid
librosa
librosa==0.9.2
loguru
matplotlib
mecab-python3

View File

@@ -1,115 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"\n",
"model_dir = \"pretrained-Japanese-Extra\"\n",
"step = 0\n",
"\n",
"g_model = torch.load(f\"{model_dir}/G_{step}.pth\", map_location=\"cpu\")\n",
"d_model = torch.load(f\"{model_dir}/D_{step}.pth\", map_location=\"cpu\")\n",
"wd_model = torch.load(f\"{model_dir}/WD_{step}.pth\", map_location=\"cpu\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"skip enc_p.in_feature_net.0.weight\n",
"skip enc_p.in_feature_net.2.weight\n",
"skip enc_p.in_feature_net.2.bias\n",
"skip enc_p.in_feature_net.3.norm.weight\n",
"skip enc_p.in_feature_net.3.norm.bias\n",
"skip enc_p.in_feature_net.3.mlp.c_fc1.weight\n",
"skip enc_p.in_feature_net.3.mlp.c_fc2.weight\n",
"skip enc_p.in_feature_net.3.mlp.c_proj.weight\n",
"skip enc_p.in_feature_net.4.weight\n",
"skip enc_p.emo_vq._codebook.initted\n",
"skip enc_p.emo_vq._codebook.cluster_size\n",
"skip enc_p.emo_vq._codebook.embed_avg\n",
"skip enc_p.emo_vq._codebook.embed\n",
"skip enc_p.out_feature_net.weight\n",
"skip enc_p.out_feature_net.bias\n"
]
}
],
"source": [
"g_dict = {}\n",
"skip_list = [\"enc_p.in_feature_net.\", \"enc_p.emo_vq.\", \"enc_p.out_feature_net.\"]\n",
"for key in g_model[\"model\"].keys():\n",
" if any([key.startswith(s) for s in skip_list]):\n",
" print(f\"skip {key}\")\n",
" continue\n",
" else:\n",
" g_dict[key] = g_model[\"model\"][key]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"d_dict = {}\n",
"for key in d_model[\"model\"].keys():\n",
" d_dict[key] = d_model[\"model\"][key]"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"wd_dict = {}\n",
"for key in wd_model[\"model\"].keys():\n",
" wd_dict[key] = wd_model[\"model\"][key]"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"from safetensors.torch import save_file\n",
"\n",
"\n",
"save_file(g_dict, f\"G_0.safetensors\")\n",
"save_file (d_dict, f\"D_0.safetensors\")\n",
"save_file (wd_dict, f\"WD_0.safetensors\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.11"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@@ -42,7 +42,7 @@ pushd Style-Bert-VITS2
@REM 初期化(必要なモデルのダウンロード)
python initialize.py
echo Style-Bert-VITS2の2.0.1へのアップデートが完了しました。
echo Style-Bert-VITS2の2.xへのアップデートが完了しました。
pause

View File

@@ -79,6 +79,11 @@ def run():
action="store_true",
help="Do not show the progress bar while training.",
)
parser.add_argument(
"--speedup",
action="store_true",
help="Speed up training by disabling logging and evaluation.",
)
args = parser.parse_args()
# Set log file
@@ -115,8 +120,9 @@ def run():
n_gpus = dist.get_world_size()
hps = utils.get_hparams_from_file(args.config)
# This is needed because we have to pass `model_dir` to `train_and_evaluate()`
# This is needed because we have to pass values to `train_and_evaluate()`
hps.model_dir = model_dir
hps.speedup = args.speedup
# 比较路径是否相同
if os.path.realpath(args.config) != os.path.realpath(
@@ -161,7 +167,9 @@ def run():
torch.cuda.set_device(local_rank)
global global_step
if rank == 0:
writer = None
writer_eval = None
if rank == 0 and not args.speedup:
# logger = utils.get_logger(hps.model_dir)
# logger.info(hps)
utils.check_git_hash(model_dir)
@@ -179,17 +187,20 @@ def run():
collate_fn = TextAudioSpeakerCollate()
train_loader = DataLoader(
train_dataset,
# num_workers=min(config.train_ms_config.num_workers, os.cpu_count() - 1),
# Slow and often freezes, so use only half of the cores.
num_workers=min(config.train_ms_config.num_workers, os.cpu_count() // 2),
# メモリ消費量を減らそうとnum_workersを1にしてみる
# num_workers=min(config.train_ms_config.num_workers, os.cpu_count() // 2),
num_workers=1,
shuffle=False,
pin_memory=True,
collate_fn=collate_fn,
batch_sampler=train_sampler,
persistent_workers=True,
prefetch_factor=4,
# これもメモリ消費量を減らそうとしてコメントアウト
# prefetch_factor=4,
) # DataLoader config could be adjusted.
if rank == 0:
eval_dataset = None
eval_loader = None
if rank == 0 and not args.speedup:
eval_dataset = TextAudioSpeakerLoader(hps.data.validation_files, hps.data)
eval_loader = DataLoader(
eval_dataset,
@@ -647,7 +658,7 @@ def train_and_evaluate(
scaler.update()
if rank == 0:
if global_step % hps.train.log_interval == 0:
if global_step % hps.train.log_interval == 0 and not hps.speedup:
lr = optim_g.param_groups[0]["lr"]
losses = [loss_disc, loss_gen, loss_fm, loss_mel, loss_dur, loss_kl]
# logger.info(
@@ -708,7 +719,8 @@ def train_and_evaluate(
and global_step != 0
and initial_step != global_step
):
evaluate(hps, net_g, eval_loader, writer_eval)
if not hps.speedup:
evaluate(hps, net_g, eval_loader, writer_eval)
utils.save_checkpoint(
net_g,
optim_g,

View File

@@ -82,6 +82,11 @@ def run():
action="store_true",
help="Do not show the progress bar while training.",
)
parser.add_argument(
"--speedup",
action="store_true",
help="Speed up training by disabling logging and evaluation.",
)
args = parser.parse_args()
# Set log file
@@ -118,8 +123,9 @@ def run():
n_gpus = dist.get_world_size()
hps = utils.get_hparams_from_file(args.config)
# This is needed because we have to pass `model_dir` to `train_and_evaluate()`
# This is needed because we have to pass values to `train_and_evaluate()
hps.model_dir = model_dir
hps.speedup = args.speedup
# 比较路径是否相同
if os.path.realpath(args.config) != os.path.realpath(
@@ -164,7 +170,9 @@ def run():
torch.cuda.set_device(local_rank)
global global_step
if rank == 0:
writer = None
writer_eval = None
if rank == 0 and not args.speedup:
# logger = utils.get_logger(hps.model_dir)
# logger.info(hps)
utils.check_git_hash(model_dir)
@@ -182,17 +190,20 @@ def run():
collate_fn = TextAudioSpeakerCollate(use_jp_extra=True)
train_loader = DataLoader(
train_dataset,
# num_workers=min(config.train_ms_config.num_workers, os.cpu_count() - 1),
# Slow and often freezes, so use only half of the cores.
num_workers=min(config.train_ms_config.num_workers, os.cpu_count() // 2),
# メモリ消費量を減らそうとnum_workersを1にしてみる
# num_workers=min(config.train_ms_config.num_workers, os.cpu_count() // 2),
num_workers=1,
shuffle=False,
pin_memory=True,
collate_fn=collate_fn,
batch_sampler=train_sampler,
persistent_workers=True,
prefetch_factor=6,
# これもメモリ消費量を減らそうとしてコメントアウト
# prefetch_factor=6,
) # DataLoader config could be adjusted.
if rank == 0:
eval_dataset = None
eval_loader = None
if rank == 0 and not args.speedup:
eval_dataset = TextAudioSpeakerLoader(hps.data.validation_files, hps.data)
eval_loader = DataLoader(
eval_dataset,
@@ -297,16 +308,28 @@ def run():
)
else:
optim_wd = None
net_g = DDP(net_g, device_ids=[local_rank], bucket_cap_mb=512)
net_d = DDP(net_d, device_ids=[local_rank], bucket_cap_mb=512)
net_g = DDP(
net_g,
device_ids=[local_rank],
# bucket_cap_mb=512
)
net_d = DDP(
net_d,
device_ids=[local_rank],
# bucket_cap_mb=512
)
if net_dur_disc is not None:
net_dur_disc = DDP(
net_dur_disc,
device_ids=[local_rank],
bucket_cap_mb=512,
# bucket_cap_mb=512,
)
if net_wd is not None:
net_wd = DDP(net_wd, device_ids=[local_rank], bucket_cap_mb=512)
net_wd = DDP(
net_wd,
device_ids=[local_rank],
# bucket_cap_mb=512
)
if utils.is_resuming(model_dir):
if net_dur_disc is not None:
@@ -748,7 +771,7 @@ def train_and_evaluate(
scaler.update()
if rank == 0:
if global_step % hps.train.log_interval == 0:
if global_step % hps.train.log_interval == 0 and not hps.speedup:
lr = optim_g.param_groups[0]["lr"]
losses = [loss_disc, loss_gen, loss_fm, loss_mel, loss_dur, loss_kl]
# logger.info(
@@ -842,7 +865,8 @@ def train_and_evaluate(
and global_step != 0
and initial_step != global_step
):
evaluate(hps, net_g, eval_loader, writer_eval)
if not hps.speedup:
evaluate(hps, net_g, eval_loader, writer_eval)
utils.save_checkpoint(
net_g,
optim_g,

View File

@@ -52,7 +52,7 @@ def do_transcribe(
return "Error: モデル名を入力してください。"
if initial_prompt == "":
initial_prompt = "こんにちは。元気、ですかー?私は……ふふっ、ちゃんと元気だよ!"
logger.debug(f"initial_prompt: {initial_prompt}")
# logger.debug(f"initial_prompt: {initial_prompt}")
if input_dir == "":
input_dir = os.path.join(dataset_root, model_name, "raw")
output_file = os.path.join(dataset_root, model_name, "esd.list")
@@ -117,7 +117,9 @@ Style-Bert-VITS2の学習用データセットを作成するためのツール
with gr.Blocks(theme="NoCrypt/miku") as app:
gr.Markdown(initial_md)
model_name = gr.Textbox(label="モデル名を入力してください(話者名としても使われます)。")
model_name = gr.Textbox(
label="モデル名を入力してください(話者名としても使われます)。"
)
with gr.Accordion("音声のスライス"):
with gr.Row():
with gr.Column():
@@ -127,10 +129,18 @@ with gr.Blocks(theme="NoCrypt/miku") as app:
info="下記フォルダにwavファイルを入れておいてください",
)
min_sec = gr.Slider(
minimum=0, maximum=10, value=2, step=0.5, label="この秒数未満は切り捨てる"
minimum=0,
maximum=10,
value=2,
step=0.5,
label="この秒数未満は切り捨てる",
)
max_sec = gr.Slider(
minimum=0, maximum=15, value=12, step=0.5, label="この秒数以上は切り捨てる"
minimum=0,
maximum=15,
value=12,
step=0.5,
label="この秒数以上は切り捨てる",
)
min_silence_dur_ms = gr.Slider(
minimum=0,

View File

@@ -87,6 +87,17 @@ def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_li
) as f:
json.dump(new_config, f, indent=2, ensure_ascii=False)
# recipe.jsonを読み込んで、style_triple_listを追記
info_path = os.path.join(assets_root, output_name, "recipe.json")
if os.path.exists(info_path):
with open(info_path, encoding="utf-8") as f:
info = json.load(f)
else:
info = {}
info["style_triple_list"] = style_triple_list
with open(info_path, "w", encoding="utf-8") as f:
json.dump(info, f, indent=2, ensure_ascii=False)
return output_style_path, list(new_style2id.keys())
@@ -133,6 +144,19 @@ def merge_models(
)
os.makedirs(os.path.dirname(merged_model_path), exist_ok=True)
save_file(merged_model_weight, merged_model_path)
info = {
"model_a": model_path_a,
"model_b": model_path_b,
"voice_weight": voice_weight,
"voice_pitch_weight": voice_pitch_weight,
"speech_style_weight": speech_style_weight,
"tempo_weight": tempo_weight,
}
with open(
os.path.join(assets_root, output_name, "recipe.json"), "w", encoding="utf-8"
) as f:
json.dump(info, f, indent=2, ensure_ascii=False)
return merged_model_path
@@ -235,6 +259,8 @@ initial_md = """
以上でマージは完了で、`model_assets/マージ後のモデル名`にマージ後のモデルが保存され、音声合成のときに使えます。
また`model_asses/マージ後のモデル名/recipe.json`には、マージの配合レシピが記録されます(推論にはいらないので配合メモ用です)。
一番下にマージしたモデルによる簡易的な音声合成機能もつけています。
## 注意

View File

@@ -8,6 +8,7 @@ from multiprocessing import cpu_count
import gradio as gr
import yaml
from common.constants import LATEST_VERSION
from common.log import logger
from common.subprocess_utils import run_script_with_log, second_elem_of
@@ -315,8 +316,8 @@ def train(model_name, skip_style=False, use_jp_extra=True, speedup=False):
return True, "Success: 学習が完了しました"
initial_md = """
# Style-Bert-VITS2 ver 2.0 学習用WebUI
initial_md = f"""
# Style-Bert-VITS2 ver {LATEST_VERSION} 学習用WebUI
## 使い方