Unify webui to single webui

This commit is contained in:
litagin02
2024-03-08 13:00:15 +09:00
parent 1cbe9648b0
commit 9f01e54d2d
7 changed files with 172 additions and 276 deletions

61
app.py
View File

@@ -1,30 +1,51 @@
import pyopenjtalk import argparse
import gradio as gr
from webui import (
create_dataset_app,
create_train_app,
create_merge_app,
create_style_vectors_app,
)
from pathlib import Path from pathlib import Path
pyopenjtalk.unset_user_dict() import gradio as gr
import torch
import yaml
setting_json = Path("webui/setting.json") from common.constants import GRADIO_THEME, LATEST_VERSION
from common.tts_model import ModelHolder
from webui import (
create_dataset_app,
create_inference_app,
create_merge_app,
create_style_vectors_app,
create_train_app,
)
with gr.Blocks() as app: # Get path settings
with Path("configs/paths.yml").open("r", encoding="utf-8") as f:
path_config: dict[str, str] = yaml.safe_load(f.read())
# dataset_root = path_config["dataset_root"]
assets_root = path_config["assets_root"]
parser = argparse.ArgumentParser()
parser.add_argument("--device", type=str, default="cuda")
parser.add_argument("--no_autolaunch", action="store_true")
parser.add_argument("--share", action="store_true")
args = parser.parse_args()
device = args.device
if device == "cuda" and not torch.cuda.is_available():
device = "cpu"
model_holder = ModelHolder(Path(assets_root), device)
with gr.Blocks(theme=GRADIO_THEME) as app:
gr.Markdown(f"# Style-Bert-VITS2 WebUI (version {LATEST_VERSION})")
with gr.Tabs(): with gr.Tabs():
with gr.Tab("Hello"): with gr.Tab("音声合成"):
gr.Markdown("## Hello, Gradio!") create_inference_app(model_holder=model_holder)
gr.Textbox("input", label="Input Text") with gr.Tab("データセット作成"):
with gr.Tab("Dataset"):
create_dataset_app() create_dataset_app()
with gr.Tab("Train"): with gr.Tab("学習"):
create_train_app() create_train_app()
with gr.Tab("Merge"): with gr.Tab("スタイル作成"):
create_merge_app()
with gr.Tab("Create Style Vectors"):
create_style_vectors_app() create_style_vectors_app()
with gr.Tab("マージ"):
create_merge_app(model_holder=model_holder)
app.launch(inbrowser=True) app.launch(inbrowser=not args.no_autolaunch, share=args.share)

View File

@@ -4,7 +4,7 @@ import enum
# See https://huggingface.co/spaces/gradio/theme-gallery for more themes # See https://huggingface.co/spaces/gradio/theme-gallery for more themes
GRADIO_THEME: str = "NoCrypt/miku" GRADIO_THEME: str = "NoCrypt/miku"
LATEST_VERSION: str = "2.3.1" LATEST_VERSION: str = "2.4"
USER_DICT_DIR = "dict_data" USER_DICT_DIR = "dict_data"

View File

@@ -8,12 +8,6 @@ from common.constants import GRADIO_THEME
from common.log import logger from common.log import logger
from common.subprocess_utils import run_script_with_log from common.subprocess_utils import run_script_with_log
# Get path settings
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f:
path_config: dict[str, str] = yaml.safe_load(f.read())
dataset_root = path_config["dataset_root"]
# assets_root = path_config["assets_root"]
def do_slice( def do_slice(
model_name: str, model_name: str,
@@ -69,13 +63,11 @@ def do_transcribe(
] ]
) )
if not success: if not success:
return f"Error: {message}" return f"Error: {message}. しかし何故かエラーが起きても正常に終了している場合がほとんどなので、書き起こし結果を確認して問題なければ学習に使えます。"
return "音声の文字起こしが完了しました。" return "音声の文字起こしが完了しました。"
initial_md = """ how_to_md = """
# 簡易学習用データセット作成ツール
Style-Bert-VITS2の学習用データセットを作成するためのツールです。以下の2つからなります。 Style-Bert-VITS2の学習用データセットを作成するためのツールです。以下の2つからなります。
- 与えられた音声からちょうどいい長さの発話区間を切り取りスライス - 与えられた音声からちょうどいい長さの発話区間を切り取りスライス
@@ -107,10 +99,10 @@ Style-Bert-VITS2の学習用データセットを作成するためのツール
""" """
def create_dataset_app(): def create_dataset_app() -> gr.Blocks:
with gr.Blocks() as app:
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Accordion("使い方", open=False):
gr.Markdown(initial_md) gr.Markdown(how_to_md)
model_name = gr.Textbox( model_name = gr.Textbox(
label="モデル名を入力してください(話者名としても使われます)。" label="モデル名を入力してください(話者名としても使われます)。"
) )
@@ -118,8 +110,8 @@ def create_dataset_app():
with gr.Row(): with gr.Row():
with gr.Column(): with gr.Column():
input_dir = gr.Textbox( input_dir = gr.Textbox(
label="入力フォルダ名デフォルトはinputs", label="元音声の入っているフォルダパス",
placeholder="inputs", value="inputs",
info="下記フォルダにwavファイルを入れておいてください", info="下記フォルダにwavファイルを入れておいてください",
) )
min_sec = gr.Slider( min_sec = gr.Slider(
@@ -201,20 +193,4 @@ def create_dataset_app():
outputs=[result2], outputs=[result2],
) )
parser = argparse.ArgumentParser()
parser.add_argument(
"--server-name",
type=str,
default=None,
help="Server name for Gradio app",
)
parser.add_argument(
"--no-autolaunch",
action="store_true",
default=False,
help="Do not launch app automatically",
)
args = parser.parse_args()
# app.launch(inbrowser=not args.no_autolaunch, server_name=args.server_name)
return app return app

View File

@@ -1,14 +1,9 @@
import argparse import argparse
import datetime import datetime
import json import json
import os
import sys
from pathlib import Path
from typing import Optional from typing import Optional
import gradio as gr import gradio as gr
import torch
import yaml
from common.constants import ( from common.constants import (
DEFAULT_ASSIST_TEXT_WEIGHT, DEFAULT_ASSIST_TEXT_WEIGHT,
@@ -21,7 +16,6 @@ from common.constants import (
DEFAULT_STYLE, DEFAULT_STYLE,
DEFAULT_STYLE_WEIGHT, DEFAULT_STYLE_WEIGHT,
GRADIO_THEME, GRADIO_THEME,
LATEST_VERSION,
Languages, Languages,
) )
from common.log import logger from common.log import logger
@@ -29,119 +23,9 @@ from common.tts_model import ModelHolder
from infer import InvalidToneError from infer import InvalidToneError
from text.japanese import g2kata_tone, kata_tone2phone_tone, text_normalize from text.japanese import g2kata_tone, kata_tone2phone_tone, text_normalize
# Get path settings
with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f:
path_config: dict[str, str] = yaml.safe_load(f.read())
# dataset_root = path_config["dataset_root"]
assets_root = path_config["assets_root"]
languages = [l.value for l in Languages] languages = [l.value for l in Languages]
def tts_fn(
model_name,
model_path,
text,
language,
reference_audio_path,
sdp_ratio,
noise_scale,
noise_scale_w,
length_scale,
line_split,
split_interval,
assist_text,
assist_text_weight,
use_assist_text,
style,
style_weight,
kata_tone_json_str,
use_tone,
speaker,
pitch_scale,
intonation_scale,
):
model_holder.load_model_gr(model_name, model_path)
wrong_tone_message = ""
kata_tone: Optional[list[tuple[str, int]]] = None
if use_tone and kata_tone_json_str != "":
if language != "JP":
logger.warning("Only Japanese is supported for tone generation.")
wrong_tone_message = "アクセント指定は現在日本語のみ対応しています。"
if line_split:
logger.warning("Tone generation is not supported for line split.")
wrong_tone_message = (
"アクセント指定は改行で分けて生成を使わない場合のみ対応しています。"
)
try:
kata_tone = []
json_data = json.loads(kata_tone_json_str)
# tupleを使うように変換
for kana, tone in json_data:
assert isinstance(kana, str) and tone in (0, 1), f"{kana}, {tone}"
kata_tone.append((kana, tone))
except Exception as e:
logger.warning(f"Error occurred when parsing kana_tone_json: {e}")
wrong_tone_message = f"アクセント指定が不正です: {e}"
kata_tone = None
# toneは実際に音声合成に代入される際のみnot Noneになる
tone: Optional[list[int]] = None
if kata_tone is not None:
phone_tone = kata_tone2phone_tone(kata_tone)
tone = [t for _, t in phone_tone]
speaker_id = model_holder.current_model.spk2id[speaker]
start_time = datetime.datetime.now()
assert model_holder.current_model is not None
try:
sr, audio = model_holder.current_model.infer(
text=text,
language=language,
reference_audio_path=reference_audio_path,
sdp_ratio=sdp_ratio,
noise=noise_scale,
noisew=noise_scale_w,
length=length_scale,
line_split=line_split,
split_interval=split_interval,
assist_text=assist_text,
assist_text_weight=assist_text_weight,
use_assist_text=use_assist_text,
style=style,
style_weight=style_weight,
given_tone=tone,
sid=speaker_id,
pitch_scale=pitch_scale,
intonation_scale=intonation_scale,
)
except InvalidToneError as e:
logger.error(f"Tone error: {e}")
return f"Error: アクセント指定が不正です:\n{e}", None, kata_tone_json_str
except ValueError as e:
logger.error(f"Value error: {e}")
return f"Error: {e}", None, kata_tone_json_str
end_time = datetime.datetime.now()
duration = (end_time - start_time).total_seconds()
if tone is None and language == "JP":
# アクセント指定に使えるようにアクセント情報を返す
norm_text = text_normalize(text)
kata_tone = g2kata_tone(norm_text)
kata_tone_json_str = json.dumps(kata_tone, ensure_ascii=False)
elif tone is None:
kata_tone_json_str = ""
message = f"Success, time: {duration} seconds."
if wrong_tone_message != "":
message = wrong_tone_message + "\n" + message
return message, (sr, audio), kata_tone_json_str
initial_text = "こんにちは、初めまして。あなたの名前はなんていうの?" initial_text = "こんにちは、初めまして。あなたの名前はなんていうの?"
examples = [ examples = [
@@ -202,9 +86,7 @@ examples = [
] ]
initial_md = f""" initial_md = f"""
# Style-Bert-VITS2 ver {LATEST_VERSION} 音声合成 - Ver 2.3で追加されたエディターのほうが実際に読み上げさせるには使いやすいかもしれません。`Editor.bat`か`python server_editor.py --inbrowser`で起動できます。
- Ver 2.3で追加されたエディターのほうが実際に読み上げさせるには使いやすいかもしれません。`Editor.bat`か`python server_editor.py`で起動できます。
- 初期からある[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)です。 - 初期からある[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)です。
""" """
@@ -254,43 +136,119 @@ def gr_util(item):
return (gr.update(visible=False), gr.update(visible=True)) return (gr.update(visible=False), gr.update(visible=True))
def create_inference_app(): def create_inference_app(model_holder: ModelHolder) -> gr.Blocks:
parser = argparse.ArgumentParser() def tts_fn(
parser.add_argument("--cpu", action="store_true", help="Use CPU instead of GPU") model_name,
parser.add_argument( model_path,
"--dir", "-d", type=str, help="Model directory", default=assets_root text,
) language,
parser.add_argument( reference_audio_path,
"--share", action="store_true", help="Share this app publicly", default=False sdp_ratio,
) noise_scale,
parser.add_argument( noise_scale_w,
"--server-name", length_scale,
type=str, line_split,
default=None, split_interval,
help="Server name for Gradio app", assist_text,
) assist_text_weight,
parser.add_argument( use_assist_text,
"--no-autolaunch", style,
action="store_true", style_weight,
default=False, kata_tone_json_str,
help="Do not launch app automatically", use_tone,
) speaker,
args = parser.parse_args() pitch_scale,
model_dir = Path(args.dir) intonation_scale,
):
model_holder.load_model(model_name, model_path)
assert model_holder.current_model is not None
if args.cpu: wrong_tone_message = ""
device = "cpu" kata_tone: Optional[list[tuple[str, int]]] = None
else: if use_tone and kata_tone_json_str != "":
device = "cuda" if torch.cuda.is_available() else "cpu" if language != "JP":
logger.warning("Only Japanese is supported for tone generation.")
wrong_tone_message = "アクセント指定は現在日本語のみ対応しています。"
if line_split:
logger.warning("Tone generation is not supported for line split.")
wrong_tone_message = (
"アクセント指定は改行で分けて生成を使わない場合のみ対応しています。"
)
try:
kata_tone = []
json_data = json.loads(kata_tone_json_str)
# tupleを使うように変換
for kana, tone in json_data:
assert isinstance(kana, str) and tone in (0, 1), f"{kana}, {tone}"
kata_tone.append((kana, tone))
except Exception as e:
logger.warning(f"Error occurred when parsing kana_tone_json: {e}")
wrong_tone_message = f"アクセント指定が不正です: {e}"
kata_tone = None
model_holder = ModelHolder(model_dir, device) # toneは実際に音声合成に代入される際のみnot Noneになる
tone: Optional[list[int]] = None
if kata_tone is not None:
phone_tone = kata_tone2phone_tone(kata_tone)
tone = [t for _, t in phone_tone]
speaker_id = model_holder.current_model.spk2id[speaker]
start_time = datetime.datetime.now()
try:
sr, audio = model_holder.current_model.infer(
text=text,
language=language,
reference_audio_path=reference_audio_path,
sdp_ratio=sdp_ratio,
noise=noise_scale,
noisew=noise_scale_w,
length=length_scale,
line_split=line_split,
split_interval=split_interval,
assist_text=assist_text,
assist_text_weight=assist_text_weight,
use_assist_text=use_assist_text,
style=style,
style_weight=style_weight,
given_tone=tone,
sid=speaker_id,
pitch_scale=pitch_scale,
intonation_scale=intonation_scale,
)
except InvalidToneError as e:
logger.error(f"Tone error: {e}")
return f"Error: アクセント指定が不正です:\n{e}", None, kata_tone_json_str
except ValueError as e:
logger.error(f"Value error: {e}")
return f"Error: {e}", None, kata_tone_json_str
end_time = datetime.datetime.now()
duration = (end_time - start_time).total_seconds()
if tone is None and language == "JP":
# アクセント指定に使えるようにアクセント情報を返す
norm_text = text_normalize(text)
kata_tone = g2kata_tone(norm_text)
kata_tone_json_str = json.dumps(kata_tone, ensure_ascii=False)
elif tone is None:
kata_tone_json_str = ""
message = f"Success, time: {duration} seconds."
if wrong_tone_message != "":
message = wrong_tone_message + "\n" + message
return message, (sr, audio), kata_tone_json_str
model_names = model_holder.model_names model_names = model_holder.model_names
if len(model_names) == 0: if len(model_names) == 0:
logger.error( logger.error(
f"モデルが見つかりませんでした。{model_dir}にモデルを置いてください。" f"モデルが見つかりませんでした。{model_holder.root_dir}にモデルを置いてください。"
) )
sys.exit(1) with gr.Blocks() as app:
gr.Markdown(
f"Error: モデルが見つかりませんでした。{model_holder.root_dir}にモデルを置いてください。"
)
return app
initial_id = 0 initial_id = 0
initial_pth_files = model_holder.model_files_dict[model_names[initial_id]] initial_pth_files = model_holder.model_files_dict[model_names[initial_id]]
@@ -497,8 +455,4 @@ def create_inference_app():
outputs=[style, ref_audio_path], outputs=[style, ref_audio_path],
) )
# app.launch(
# inbrowser=not args.no_autolaunch, share=args.share, server_name=args.server_name
# )
return app return app

View File

@@ -1,7 +1,6 @@
import argparse import argparse
import json import json
import os import os
import sys
from pathlib import Path from pathlib import Path
import gradio as gr import gradio as gr
@@ -28,8 +27,6 @@ with open(os.path.join("configs", "paths.yml"), "r", encoding="utf-8") as f:
# dataset_root = path_config["dataset_root"] # dataset_root = path_config["dataset_root"]
assets_root = path_config["assets_root"] assets_root = path_config["assets_root"]
model_holder = ModelHolder(Path(assets_root), device)
def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_list): def merge_style(model_name_a, model_name_b, weight, output_name, style_triple_list):
""" """
@@ -331,13 +328,17 @@ Happy, Surprise, HappySurprise
""" """
def create_merge_app(): def create_merge_app(model_holder: ModelHolder) -> gr.Blocks:
model_names = model_holder.model_names model_names = model_holder.model_names
if len(model_names) == 0: if len(model_names) == 0:
logger.error( logger.error(
f"モデルが見つかりませんでした。{assets_root}にモデルを置いてください。" f"モデルが見つかりませんでした。{assets_root}にモデルを置いてください。"
) )
sys.exit(1) with gr.Blocks() as app:
gr.Markdown(
f"Error: モデルが見つかりませんでした。{assets_root}にモデルを置いてください。"
)
return app
initial_id = 0 initial_id = 0
initial_model_files = model_holder.model_files_dict[model_names[initial_id]] initial_model_files = model_holder.model_files_dict[model_names[initial_id]]
@@ -499,23 +500,4 @@ def create_merge_app():
outputs=[audio_output], outputs=[audio_output],
) )
parser = argparse.ArgumentParser()
parser.add_argument(
"--server-name",
type=str,
default=None,
help="Server name for Gradio app",
)
parser.add_argument(
"--no-autolaunch",
action="store_true",
default=False,
help="Do not launch app automatically",
)
parser.add_argument("--share", action="store_true", default=False)
args = parser.parse_args()
# app.launch(
# inbrowser=not args.no_autolaunch, server_name=args.server_name, share=args.share
# )
return app return app

View File

@@ -277,9 +277,7 @@ def save_style_vectors_from_files(
return f"成功!\n{style_vector_path}に保存し{config_path}を更新しました。" return f"成功!\n{style_vector_path}に保存し{config_path}を更新しました。"
initial_md = f""" how_to_md = f"""
# Style Bert-VITS2 スタイルベクトルの作成
Style-Bert-VITS2でこまかくスタイルを指定して音声合成するには、モデルごとにスタイルベクトルのファイル`style_vectors.npy`を手動で作成する必要があります。 Style-Bert-VITS2でこまかくスタイルを指定して音声合成するには、モデルごとにスタイルベクトルのファイル`style_vectors.npy`を手動で作成する必要があります。
ただし、学習の過程で自動的に平均スタイル「{DEFAULT_STYLE}」のみは作成されるので、それをそのまま使うこともできますその場合はこのWebUIは使いません ただし、学習の過程で自動的に平均スタイル「{DEFAULT_STYLE}」のみは作成されるので、それをそのまま使うこともできますその場合はこのWebUIは使いません
@@ -326,7 +324,8 @@ https://ja.wikipedia.org/wiki/DBSCAN
def create_style_vectors_app(): def create_style_vectors_app():
with gr.Blocks(theme=GRADIO_THEME) as app: with gr.Blocks(theme=GRADIO_THEME) as app:
gr.Markdown(initial_md) with gr.Accordion("使い方", open=False):
gr.Markdown(how_to_md)
with gr.Row(): with gr.Row():
model_name = gr.Textbox(placeholder="your_model_name", label="モデル名") model_name = gr.Textbox(placeholder="your_model_name", label="モデル名")
reduction_method = gr.Radio( reduction_method = gr.Radio(
@@ -461,23 +460,4 @@ def create_style_vectors_app():
outputs=[info2], outputs=[info2],
) )
parser = argparse.ArgumentParser()
parser.add_argument(
"--server-name",
type=str,
default=None,
help="Server name for Gradio app",
)
parser.add_argument(
"--no-autolaunch",
action="store_true",
default=False,
help="Do not launch app automatically",
)
parser.add_argument("--share", action="store_true", default=False)
args = parser.parse_args()
# app.launch(
# inbrowser=not args.no_autolaunch, server_name=args.server_name, share=args.share
# )
return app return app

View File

@@ -14,7 +14,6 @@ from pathlib import Path
import gradio as gr import gradio as gr
import yaml import yaml
from common.constants import GRADIO_THEME, LATEST_VERSION
from common.log import logger from common.log import logger
from common.stdout_wrapper import SAFE_STDOUT from common.stdout_wrapper import SAFE_STDOUT
from common.subprocess_utils import run_script_with_log, second_elem_of from common.subprocess_utils import run_script_with_log, second_elem_of
@@ -398,9 +397,7 @@ def run_tensorboard(model_name):
yield gr.Button("Tensorboardを開く") yield gr.Button("Tensorboardを開く")
initial_md = f""" how_to_md = f"""
# Style-Bert-VITS2 ver {LATEST_VERSION} 学習用WebUI
## 使い方 ## 使い方
- データを準備して、モデル名を入力して、必要なら設定を調整してから、「自動前処理を実行」ボタンを押してください。進捗状況等はターミナルに表示されます。 - データを準備して、モデル名を入力して、必要なら設定を調整してから、「自動前処理を実行」ボタンを押してください。進捗状況等はターミナルに表示されます。
@@ -452,8 +449,9 @@ english_teacher.wav|Mary|EN|How are you? I'm fine, thank you, and you?
def create_train_app(): def create_train_app():
with gr.Blocks(theme=GRADIO_THEME).queue() as app: with gr.Blocks().queue() as app:
gr.Markdown(initial_md) with gr.Accordion("使い方", open=False):
gr.Markdown(how_to_md)
with gr.Accordion(label="データの前準備", open=False): with gr.Accordion(label="データの前準備", open=False):
gr.Markdown(prepare_md) gr.Markdown(prepare_md)
model_name = gr.Textbox(label="モデル名") model_name = gr.Textbox(label="モデル名")
@@ -794,20 +792,5 @@ def create_train_app():
outputs=[use_jp_extra_train], outputs=[use_jp_extra_train],
) )
parser = argparse.ArgumentParser()
parser.add_argument(
"--server-name",
type=str,
default=None,
help="Server name for Gradio app",
)
parser.add_argument(
"--no-autolaunch",
action="store_true",
default=False,
help="Do not launch app automatically",
)
args = parser.parse_args()
# app.launch(inbrowser=not args.no_autolaunch, server_name=args.server_name) # app.launch(inbrowser=not args.no_autolaunch, server_name=args.server_name)
return app return app