Feat: make style vec by subdirs (for webui)
This commit is contained in:
@@ -11,6 +11,7 @@ from sklearn.manifold import TSNE
|
|||||||
from umap import UMAP
|
from umap import UMAP
|
||||||
|
|
||||||
from config import get_path_config
|
from config import get_path_config
|
||||||
|
from default_style import save_styles_by_dirs
|
||||||
from style_bert_vits2.constants import DEFAULT_STYLE, GRADIO_THEME
|
from style_bert_vits2.constants import DEFAULT_STYLE, GRADIO_THEME
|
||||||
from style_bert_vits2.logging import logger
|
from style_bert_vits2.logging import logger
|
||||||
|
|
||||||
@@ -213,7 +214,7 @@ def save_style_vectors_from_clustering(model_name: str, style_names_str: str):
|
|||||||
|
|
||||||
logger.info(f"Backup {config_path} to {config_path}.bak")
|
logger.info(f"Backup {config_path} to {config_path}.bak")
|
||||||
shutil.copy(config_path, f"{config_path}.bak")
|
shutil.copy(config_path, f"{config_path}.bak")
|
||||||
with open(config_path, "r", encoding="utf-8") as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
json_dict = json.load(f)
|
json_dict = json.load(f)
|
||||||
json_dict["data"]["num_styles"] = len(style_name_list)
|
json_dict["data"]["num_styles"] = len(style_name_list)
|
||||||
style_dict = {name: i for i, name in enumerate(style_name_list)}
|
style_dict = {name: i for i, name in enumerate(style_name_list)}
|
||||||
@@ -265,7 +266,7 @@ def save_style_vectors_from_files(
|
|||||||
logger.info(f"Backup {config_path} to {config_path}.bak")
|
logger.info(f"Backup {config_path} to {config_path}.bak")
|
||||||
shutil.copy(config_path, f"{config_path}.bak")
|
shutil.copy(config_path, f"{config_path}.bak")
|
||||||
|
|
||||||
with open(config_path, "r", encoding="utf-8") as f:
|
with open(config_path, encoding="utf-8") as f:
|
||||||
json_dict = json.load(f)
|
json_dict = json.load(f)
|
||||||
json_dict["data"]["num_styles"] = len(style_name_list)
|
json_dict["data"]["num_styles"] = len(style_name_list)
|
||||||
style_dict = {name: i for i, name in enumerate(style_name_list)}
|
style_dict = {name: i for i, name in enumerate(style_name_list)}
|
||||||
@@ -276,20 +277,98 @@ def save_style_vectors_from_files(
|
|||||||
return f"成功!\n{style_vector_path}に保存し{config_path}を更新しました。"
|
return f"成功!\n{style_vector_path}に保存し{config_path}を更新しました。"
|
||||||
|
|
||||||
|
|
||||||
|
def save_style_vectors_by_dirs(model_name: str, audio_dir_str: str):
|
||||||
|
import sys
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from multiprocessing import cpu_count
|
||||||
|
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
|
||||||
|
from style_gen import save_style_vector
|
||||||
|
|
||||||
|
# First generate style vectors for each audio file
|
||||||
|
|
||||||
|
audio_dir = Path(audio_dir_str)
|
||||||
|
audio_suffixes = [".wav", ".flac", ".mp3", ".ogg", ".opus", ".m4a"]
|
||||||
|
audio_files = [f for f in audio_dir.rglob("*") if f.suffix in audio_suffixes]
|
||||||
|
|
||||||
|
def process(file: Path):
|
||||||
|
# f: `test.wav` -> search `test.wav.npy`
|
||||||
|
if (file.with_name(file.name + ".npy")).exists():
|
||||||
|
return file, None
|
||||||
|
try:
|
||||||
|
save_style_vector(str(file))
|
||||||
|
except Exception as e:
|
||||||
|
return file, e
|
||||||
|
return file, None
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=cpu_count() // 2) as executor:
|
||||||
|
_ = list(
|
||||||
|
tqdm(
|
||||||
|
executor.map(
|
||||||
|
process,
|
||||||
|
audio_files,
|
||||||
|
),
|
||||||
|
total=len(audio_files),
|
||||||
|
file=SAFE_STDOUT,
|
||||||
|
desc="Generating style vectors",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
result_dir = assets_root / model_name
|
||||||
|
config_path = result_dir / "config.json"
|
||||||
|
if not config_path.exists():
|
||||||
|
return f"{config_path}が存在しません。"
|
||||||
|
logger.info(f"Backup {config_path} to {config_path}.bak")
|
||||||
|
shutil.copy(config_path, f"{config_path}.bak")
|
||||||
|
|
||||||
|
style_vector_path = result_dir / "style_vectors.npy"
|
||||||
|
if style_vector_path.exists():
|
||||||
|
logger.info(f"Backup {style_vector_path} to {style_vector_path}.bak")
|
||||||
|
shutil.copy(style_vector_path, f"{style_vector_path}.bak")
|
||||||
|
save_styles_by_dirs(audio_dir, result_dir)
|
||||||
|
return f"成功!\n{result_dir}にスタイルベクトルを保存しました。"
|
||||||
|
|
||||||
|
|
||||||
how_to_md = f"""
|
how_to_md = f"""
|
||||||
Style-Bert-VITS2でこまかくスタイルを指定して音声合成するには、モデルごとにスタイルベクトルのファイル`style_vectors.npy`を手動で作成する必要があります。
|
Style-Bert-VITS2でこまかくスタイルを指定して音声合成するには、モデルごとにスタイルベクトルのファイル`style_vectors.npy`を作成する必要があります。
|
||||||
|
|
||||||
ただし、学習の過程で自動的に平均スタイル「{DEFAULT_STYLE}」のみは作成されるので、それをそのまま使うこともできます(その場合はこのWebUIは使いません)。
|
ただし、学習の過程では自動的に、平均スタイル「{DEFAULT_STYLE}」と、(**Ver 2.5.0以降からは**)音声をサブフォルダに分けていた場合はそのサブフォルダごとのスタイルが保存されています。
|
||||||
|
|
||||||
このプロセスは学習とは全く関係がないので、何回でも独立して繰り返して試せます。また学習中にもたぶん軽いので動くはずです。
|
|
||||||
|
|
||||||
## 方法
|
## 方法
|
||||||
|
|
||||||
|
- 方法0: 音声を作りたいスタイルごとのサブフォルダに分け、そのフォルダごとにスタイルベクトルを作成
|
||||||
- 方法1: 音声ファイルを自動でスタイル別に分け、その各スタイルの平均を取って保存
|
- 方法1: 音声ファイルを自動でスタイル別に分け、その各スタイルの平均を取って保存
|
||||||
- 方法2: スタイルを代表する音声ファイルを手動で選んで、その音声のスタイルベクトルを保存
|
- 方法2: スタイルを代表する音声ファイルを手動で選んで、その音声のスタイルベクトルを保存
|
||||||
- 方法3: 自分でもっと頑張ってこだわって作る(JVNVコーパスなど、もともとスタイルラベル等が利用可能な場合はこれがよいかも)
|
- 方法3: 自分でもっと頑張ってこだわって作る(JVNVコーパスなど、もともとスタイルラベル等が利用可能な場合はこれがよいかも)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
method0 = """
|
||||||
|
音声をスタイルごとにサブフォルダを作り、その中に音声ファイルを入れてください。
|
||||||
|
|
||||||
|
**注意**
|
||||||
|
|
||||||
|
- Ver 2.5.0以降では、raw/フォルダにサブディレクトリに分けて音声ファイルを入れるだけで、スタイルベクトルが自動で作成されるので、この手順は不要です。
|
||||||
|
- それ未満のバージョンで学習したモデルに新しくスタイルベクトルをつけたい場合や、学習に使ったのとは別の音声でスタイルベクトルを作成したい場合に使います。
|
||||||
|
- 学習との整合性のため、もし**現在学習中や、今後学習する予定がある場合は**、音声ファイルは、`Data/{モデル名}/wavs`フォルダではなく**新しい別のディレクトリに保存してください**。
|
||||||
|
|
||||||
|
例:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
audio_dir
|
||||||
|
├── style1
|
||||||
|
│ ├── audio1.wav
|
||||||
|
│ ├── audio2.wav
|
||||||
|
│ └── ...
|
||||||
|
├── style2
|
||||||
|
│ ├── audio1.wav
|
||||||
|
│ ├── audio2.wav
|
||||||
|
│ └── ...
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
method1 = f"""
|
method1 = f"""
|
||||||
学習の時に取り出したスタイルベクトルを読み込んで、可視化を見ながらスタイルを分けていきます。
|
学習の時に取り出したスタイルベクトルを読み込んで、可視化を見ながらスタイルを分けていきます。
|
||||||
|
|
||||||
@@ -325,8 +404,23 @@ def create_style_vectors_app():
|
|||||||
with gr.Blocks(theme=GRADIO_THEME) as app:
|
with gr.Blocks(theme=GRADIO_THEME) as app:
|
||||||
with gr.Accordion("使い方", open=False):
|
with gr.Accordion("使い方", open=False):
|
||||||
gr.Markdown(how_to_md)
|
gr.Markdown(how_to_md)
|
||||||
with gr.Row():
|
|
||||||
model_name = gr.Textbox(placeholder="your_model_name", label="モデル名")
|
model_name = gr.Textbox(placeholder="your_model_name", label="モデル名")
|
||||||
|
with gr.Tab("方法0: サブフォルダごとにスタイルベクトルを作成"):
|
||||||
|
gr.Markdown(method0)
|
||||||
|
audio_dir = gr.Textbox(
|
||||||
|
placeholder="path/to/audio_dir",
|
||||||
|
label="音声が入っているフォルダ",
|
||||||
|
info="音声ファイルをスタイルごとにサブフォルダに分けて保存してください。",
|
||||||
|
)
|
||||||
|
method0_btn = gr.Button("スタイルベクトルを作成", variant="primary")
|
||||||
|
method0_info = gr.Textbox(label="結果")
|
||||||
|
method0_btn.click(
|
||||||
|
save_style_vectors_by_dirs,
|
||||||
|
inputs=[model_name, audio_dir],
|
||||||
|
outputs=[method0_info],
|
||||||
|
)
|
||||||
|
with gr.Tab("その他の方法"):
|
||||||
|
with gr.Row():
|
||||||
reduction_method = gr.Radio(
|
reduction_method = gr.Radio(
|
||||||
choices=["UMAP", "t-SNE"],
|
choices=["UMAP", "t-SNE"],
|
||||||
label="次元削減方法",
|
label="次元削減方法",
|
||||||
@@ -335,7 +429,9 @@ def create_style_vectors_app():
|
|||||||
)
|
)
|
||||||
load_button = gr.Button("スタイルベクトルを読み込む", variant="primary")
|
load_button = gr.Button("スタイルベクトルを読み込む", variant="primary")
|
||||||
output = gr.Plot(label="音声スタイルの可視化")
|
output = gr.Plot(label="音声スタイルの可視化")
|
||||||
load_button.click(load, inputs=[model_name, reduction_method], outputs=[output])
|
load_button.click(
|
||||||
|
load, inputs=[model_name, reduction_method], outputs=[output]
|
||||||
|
)
|
||||||
with gr.Tab("方法1: スタイル分けを自動で行う"):
|
with gr.Tab("方法1: スタイル分けを自動で行う"):
|
||||||
with gr.Tab("スタイル分け1"):
|
with gr.Tab("スタイル分け1"):
|
||||||
n_clusters = gr.Slider(
|
n_clusters = gr.Slider(
|
||||||
@@ -404,7 +500,9 @@ def create_style_vectors_app():
|
|||||||
with gr.Row():
|
with gr.Row():
|
||||||
audio_list = []
|
audio_list = []
|
||||||
for i in range(MAX_AUDIO_NUM):
|
for i in range(MAX_AUDIO_NUM):
|
||||||
audio_list.append(gr.Audio(visible=False, show_label=True))
|
audio_list.append(
|
||||||
|
gr.Audio(visible=False, show_label=True)
|
||||||
|
)
|
||||||
c_button.click(
|
c_button.click(
|
||||||
do_clustering_gradio,
|
do_clustering_gradio,
|
||||||
inputs=[n_clusters, c_method],
|
inputs=[n_clusters, c_method],
|
||||||
@@ -413,7 +511,8 @@ def create_style_vectors_app():
|
|||||||
dbscan_button.click(
|
dbscan_button.click(
|
||||||
do_dbscan_gradio,
|
do_dbscan_gradio,
|
||||||
inputs=[eps, min_samples],
|
inputs=[eps, min_samples],
|
||||||
outputs=[gr_plot, cluster_index, num_styles_result] + audio_list,
|
outputs=[gr_plot, cluster_index, num_styles_result]
|
||||||
|
+ audio_list,
|
||||||
)
|
)
|
||||||
get_audios_button.click(
|
get_audios_button.click(
|
||||||
representative_wav_files_gradio,
|
representative_wav_files_gradio,
|
||||||
@@ -427,7 +526,9 @@ def create_style_vectors_app():
|
|||||||
info=f"スタイルの名前を`,`で区切って入力してください(日本語可)。例: `Angry, Sad, Happy`や`怒り, 悲しみ, 喜び`など。平均音声は{DEFAULT_STYLE}として自動的に保存されます。",
|
info=f"スタイルの名前を`,`で区切って入力してください(日本語可)。例: `Angry, Sad, Happy`や`怒り, 悲しみ, 喜び`など。平均音声は{DEFAULT_STYLE}として自動的に保存されます。",
|
||||||
)
|
)
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
save_button1 = gr.Button("スタイルベクトルを保存", variant="primary")
|
save_button1 = gr.Button(
|
||||||
|
"スタイルベクトルを保存", variant="primary"
|
||||||
|
)
|
||||||
info2 = gr.Textbox(label="保存結果")
|
info2 = gr.Textbox(label="保存結果")
|
||||||
|
|
||||||
save_button1.click(
|
save_button1.click(
|
||||||
@@ -445,13 +546,16 @@ def create_style_vectors_app():
|
|||||||
)
|
)
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
audio_files_text = gr.Textbox(
|
audio_files_text = gr.Textbox(
|
||||||
label="音声ファイル名", placeholder="angry.wav, sad.wav, happy.wav"
|
label="音声ファイル名",
|
||||||
|
placeholder="angry.wav, sad.wav, happy.wav",
|
||||||
)
|
)
|
||||||
style_names_text = gr.Textbox(
|
style_names_text = gr.Textbox(
|
||||||
label="スタイル名", placeholder="Angry, Sad, Happy"
|
label="スタイル名", placeholder="Angry, Sad, Happy"
|
||||||
)
|
)
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
save_button2 = gr.Button("スタイルベクトルを保存", variant="primary")
|
save_button2 = gr.Button(
|
||||||
|
"スタイルベクトルを保存", variant="primary"
|
||||||
|
)
|
||||||
info2 = gr.Textbox(label="保存結果")
|
info2 = gr.Textbox(label="保存結果")
|
||||||
save_button2.click(
|
save_button2.click(
|
||||||
save_style_vectors_from_files,
|
save_style_vectors_from_files,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ inference.to(device)
|
|||||||
class NaNValueError(ValueError):
|
class NaNValueError(ValueError):
|
||||||
"""カスタム例外クラス。NaN値が見つかった場合に使用されます。"""
|
"""カスタム例外クラス。NaN値が見つかった場合に使用されます。"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# 推論時にインポートするために短いが関数を書く
|
# 推論時にインポートするために短いが関数を書く
|
||||||
@@ -74,7 +73,7 @@ if __name__ == "__main__":
|
|||||||
device = config.style_gen_config.device
|
device = config.style_gen_config.device
|
||||||
|
|
||||||
training_lines: list[str] = []
|
training_lines: list[str] = []
|
||||||
with open(hps.data.training_files, "r", encoding="utf-8") as f:
|
with open(hps.data.training_files, encoding="utf-8") as f:
|
||||||
training_lines.extend(f.readlines())
|
training_lines.extend(f.readlines())
|
||||||
with ThreadPoolExecutor(max_workers=num_processes) as executor:
|
with ThreadPoolExecutor(max_workers=num_processes) as executor:
|
||||||
training_results = list(
|
training_results = list(
|
||||||
@@ -95,7 +94,7 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
|
|
||||||
val_lines: list[str] = []
|
val_lines: list[str] = []
|
||||||
with open(hps.data.validation_files, "r", encoding="utf-8") as f:
|
with open(hps.data.validation_files, encoding="utf-8") as f:
|
||||||
val_lines.extend(f.readlines())
|
val_lines.extend(f.readlines())
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=num_processes) as executor:
|
with ThreadPoolExecutor(max_workers=num_processes) as executor:
|
||||||
|
|||||||
Reference in New Issue
Block a user