Refactor: make tts_model.py independent of style_gen.py
This commit is contained in:
@@ -4,6 +4,7 @@ from typing import Any, Optional, Union
|
|||||||
|
|
||||||
import gradio as gr
|
import gradio as gr
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import pyannote.audio
|
||||||
import torch
|
import torch
|
||||||
from gradio.processing_utils import convert_to_16_bit_wav
|
from gradio.processing_utils import convert_to_16_bit_wav
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
@@ -30,8 +31,8 @@ from style_bert_vits2.voice import adjust_voice
|
|||||||
|
|
||||||
class Model:
|
class Model:
|
||||||
"""
|
"""
|
||||||
Style-Bert-Vits2 の音声合成モデルを操作するためのクラス
|
Style-Bert-Vits2 の音声合成モデルを操作するためのクラス。
|
||||||
モデル/ハイパーパラメータ/スタイルベクトルのパスとデバイスを指定して初期化し、model.infer() メソッドを呼び出すと音声合成を行える
|
モデル/ハイパーパラメータ/スタイルベクトルのパスとデバイスを指定して初期化し、model.infer() メソッドを呼び出すと音声合成を行える。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -46,50 +47,81 @@ class Model:
|
|||||||
self.config_path: Path = config_path
|
self.config_path: Path = config_path
|
||||||
self.style_vec_path: Path = style_vec_path
|
self.style_vec_path: Path = style_vec_path
|
||||||
self.device: str = device
|
self.device: str = device
|
||||||
self.hps: HyperParameters = HyperParameters.load_from_json(self.config_path)
|
self.hyper_parameters: HyperParameters = HyperParameters.load_from_json(self.config_path)
|
||||||
self.spk2id: dict[str, int] = self.hps.data.spk2id
|
self.spk2id: dict[str, int] = self.hyper_parameters.data.spk2id
|
||||||
self.id2spk: dict[int, str] = {v: k for k, v in self.spk2id.items()}
|
self.id2spk: dict[int, str] = {v: k for k, v in self.spk2id.items()}
|
||||||
|
|
||||||
self.num_styles: int = self.hps.data.num_styles
|
num_styles: int = self.hyper_parameters.data.num_styles
|
||||||
if hasattr(self.hps.data, "style2id"):
|
if hasattr(self.hyper_parameters.data, "style2id"):
|
||||||
self.style2id: dict[str, int] = self.hps.data.style2id
|
self.style2id: dict[str, int] = self.hyper_parameters.data.style2id
|
||||||
else:
|
else:
|
||||||
self.style2id: dict[str, int] = {str(i): i for i in range(self.num_styles)}
|
self.style2id: dict[str, int] = {str(i): i for i in range(num_styles)}
|
||||||
if len(self.style2id) != self.num_styles:
|
if len(self.style2id) != num_styles:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Number of styles ({self.num_styles}) does not match the number of style2id ({len(self.style2id)})"
|
f"Number of styles ({num_styles}) does not match the number of style2id ({len(self.style2id)})"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.style_vectors: NDArray[Any] = np.load(self.style_vec_path)
|
self.__style_vector_inference: Optional[pyannote.audio.Inference] = None
|
||||||
if self.style_vectors.shape[0] != self.num_styles:
|
self.__style_vectors: NDArray[Any] = np.load(self.style_vec_path)
|
||||||
|
if self.__style_vectors.shape[0] != num_styles:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"The number of styles ({self.num_styles}) does not match the number of style vectors ({self.style_vectors.shape[0]})"
|
f"The number of styles ({num_styles}) does not match the number of style vectors ({self.__style_vectors.shape[0]})"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.net_g: Union[SynthesizerTrn, SynthesizerTrnJPExtra, None] = None
|
self.__net_g: Union[SynthesizerTrn, SynthesizerTrnJPExtra, None] = None
|
||||||
|
|
||||||
|
|
||||||
def load_net_g(self) -> None:
|
def load_net_g(self) -> None:
|
||||||
self.net_g = get_net_g(
|
"""
|
||||||
|
net_g をロードする。
|
||||||
|
"""
|
||||||
|
self.__net_g = get_net_g(
|
||||||
model_path=str(self.model_path),
|
model_path=str(self.model_path),
|
||||||
version=self.hps.version,
|
version=self.hyper_parameters.version,
|
||||||
device=self.device,
|
device=self.device,
|
||||||
hps=self.hps,
|
hps=self.hyper_parameters,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_style_vector(self, style_id: int, weight: float = 1.0) -> NDArray[Any]:
|
def get_style_vector(self, style_id: int, weight: float = 1.0) -> NDArray[Any]:
|
||||||
mean = self.style_vectors[0]
|
"""
|
||||||
style_vec = self.style_vectors[style_id]
|
スタイルベクトルを取得する。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
style_id (int): スタイル ID
|
||||||
|
weight (float, optional): スタイルベクトルの重み. Defaults to 1.0.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
NDArray[Any]: スタイルベクトル
|
||||||
|
"""
|
||||||
|
mean = self.__style_vectors[0]
|
||||||
|
style_vec = self.__style_vectors[style_id]
|
||||||
style_vec = mean + (style_vec - mean) * weight
|
style_vec = mean + (style_vec - mean) * weight
|
||||||
return style_vec
|
return style_vec
|
||||||
|
|
||||||
|
|
||||||
def get_style_vector_from_audio(self, audio_path: str, weight: float = 1.0) -> NDArray[Any]:
|
def get_style_vector_from_audio(self, audio_path: str, weight: float = 1.0) -> NDArray[Any]:
|
||||||
from style_gen import get_style_vector
|
"""
|
||||||
|
音声からスタイルベクトルを推論する。
|
||||||
|
|
||||||
xvec = get_style_vector(audio_path)
|
Args:
|
||||||
mean = self.style_vectors[0]
|
audio_path (str): 音声ファイルのパス
|
||||||
|
weight (float, optional): スタイルベクトルの重み. Defaults to 1.0.
|
||||||
|
Returns:
|
||||||
|
NDArray[Any]: スタイルベクトル
|
||||||
|
"""
|
||||||
|
|
||||||
|
# スタイルベクトルを取得するための推論モデルを初期化
|
||||||
|
if self.__style_vector_inference is None:
|
||||||
|
self.__style_vector_inference = pyannote.audio.Inference(
|
||||||
|
model = pyannote.audio.Model.from_pretrained("pyannote/wespeaker-voxceleb-resnet34-LM"),
|
||||||
|
window = "whole",
|
||||||
|
)
|
||||||
|
self.__style_vector_inference.to(torch.device(self.device))
|
||||||
|
|
||||||
|
# 音声からスタイルベクトルを推論
|
||||||
|
xvec = self.__style_vector_inference(audio_path)
|
||||||
|
mean = self.__style_vectors[0]
|
||||||
xvec = mean + (xvec - mean) * weight
|
xvec = mean + (xvec - mean) * weight
|
||||||
return xvec
|
return xvec
|
||||||
|
|
||||||
@@ -116,7 +148,7 @@ class Model:
|
|||||||
intonation_scale: float = 1.0,
|
intonation_scale: float = 1.0,
|
||||||
) -> tuple[int, NDArray[Any]]:
|
) -> tuple[int, NDArray[Any]]:
|
||||||
logger.info(f"Start generating audio data from text:\n{text}")
|
logger.info(f"Start generating audio data from text:\n{text}")
|
||||||
if language != "JP" and self.hps.version.endswith("JP-Extra"):
|
if language != "JP" and self.hyper_parameters.version.endswith("JP-Extra"):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"The model is trained with JP-Extra, but the language is not JP"
|
"The model is trained with JP-Extra, but the language is not JP"
|
||||||
)
|
)
|
||||||
@@ -125,9 +157,9 @@ class Model:
|
|||||||
if assist_text == "" or not use_assist_text:
|
if assist_text == "" or not use_assist_text:
|
||||||
assist_text = None
|
assist_text = None
|
||||||
|
|
||||||
if self.net_g is None:
|
if self.__net_g is None:
|
||||||
self.load_net_g()
|
self.load_net_g()
|
||||||
assert self.net_g is not None
|
assert self.__net_g is not None
|
||||||
if reference_audio_path is None:
|
if reference_audio_path is None:
|
||||||
style_id = self.style2id[style]
|
style_id = self.style2id[style]
|
||||||
style_vector = self.get_style_vector(style_id, style_weight)
|
style_vector = self.get_style_vector(style_id, style_weight)
|
||||||
@@ -145,8 +177,8 @@ class Model:
|
|||||||
length_scale = length,
|
length_scale = length,
|
||||||
sid = sid,
|
sid = sid,
|
||||||
language = language,
|
language = language,
|
||||||
hps = self.hps,
|
hps = self.hyper_parameters,
|
||||||
net_g = self.net_g,
|
net_g = self.__net_g,
|
||||||
device = self.device,
|
device = self.device,
|
||||||
assist_text = assist_text,
|
assist_text = assist_text,
|
||||||
assist_text_weight = assist_text_weight,
|
assist_text_weight = assist_text_weight,
|
||||||
@@ -168,8 +200,8 @@ class Model:
|
|||||||
length_scale = length,
|
length_scale = length,
|
||||||
sid = sid,
|
sid = sid,
|
||||||
language = language,
|
language = language,
|
||||||
hps = self.hps,
|
hps = self.hyper_parameters,
|
||||||
net_g = self.net_g,
|
net_g = self.__net_g,
|
||||||
device = self.device,
|
device = self.device,
|
||||||
assist_text = assist_text,
|
assist_text = assist_text,
|
||||||
assist_text_weight = assist_text_weight,
|
assist_text_weight = assist_text_weight,
|
||||||
@@ -182,7 +214,7 @@ class Model:
|
|||||||
logger.info("Audio data generated successfully")
|
logger.info("Audio data generated successfully")
|
||||||
if not (pitch_scale == 1.0 and intonation_scale == 1.0):
|
if not (pitch_scale == 1.0 and intonation_scale == 1.0):
|
||||||
_, audio = adjust_voice(
|
_, audio = adjust_voice(
|
||||||
fs = self.hps.data.sampling_rate,
|
fs = self.hyper_parameters.data.sampling_rate,
|
||||||
wave = audio,
|
wave = audio,
|
||||||
pitch_scale = pitch_scale,
|
pitch_scale = pitch_scale,
|
||||||
intonation_scale = intonation_scale,
|
intonation_scale = intonation_scale,
|
||||||
@@ -190,12 +222,12 @@ class Model:
|
|||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.simplefilter("ignore")
|
warnings.simplefilter("ignore")
|
||||||
audio = convert_to_16_bit_wav(audio)
|
audio = convert_to_16_bit_wav(audio)
|
||||||
return (self.hps.data.sampling_rate, audio)
|
return (self.hyper_parameters.data.sampling_rate, audio)
|
||||||
|
|
||||||
|
|
||||||
class ModelHolder:
|
class ModelHolder:
|
||||||
"""
|
"""
|
||||||
Style-Bert-Vits2 の音声合成モデルを管理するためのクラス
|
Style-Bert-Vits2 の音声合成モデルを管理するためのクラス。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -234,10 +266,10 @@ class ModelHolder:
|
|||||||
continue
|
continue
|
||||||
self.model_files_dict[model_dir.name] = model_files
|
self.model_files_dict[model_dir.name] = model_files
|
||||||
self.model_names.append(model_dir.name)
|
self.model_names.append(model_dir.name)
|
||||||
hps = HyperParameters.load_from_json(config_path)
|
hyper_parameters = HyperParameters.load_from_json(config_path)
|
||||||
style2id: dict[str, int] = hps.data.style2id
|
style2id: dict[str, int] = hyper_parameters.data.style2id
|
||||||
styles = list(style2id.keys())
|
styles = list(style2id.keys())
|
||||||
spk2id: dict[str, int] = hps.data.spk2id
|
spk2id: dict[str, int] = hyper_parameters.data.spk2id
|
||||||
speakers = list(spk2id.keys())
|
speakers = list(spk2id.keys())
|
||||||
self.models_info.append({
|
self.models_info.append({
|
||||||
"name": model_dir.name,
|
"name": model_dir.name,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
|
|||||||
|
|
||||||
def run_script_with_log(cmd: list[str], ignore_warning: bool = False) -> tuple[bool, str]:
|
def run_script_with_log(cmd: list[str], ignore_warning: bool = False) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
指定されたコマンドを実行し、そのログを記録する
|
指定されたコマンドを実行し、そのログを記録する。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmd: 実行するコマンドのリスト
|
cmd: 実行するコマンドのリスト
|
||||||
@@ -39,7 +39,7 @@ def run_script_with_log(cmd: list[str], ignore_warning: bool = False) -> tuple[b
|
|||||||
|
|
||||||
def second_elem_of(original_function: Callable[..., tuple[Any, Any]]) -> Callable[..., Any]:
|
def second_elem_of(original_function: Callable[..., tuple[Any, Any]]) -> Callable[..., Any]:
|
||||||
"""
|
"""
|
||||||
与えられた関数をラップし、その戻り値の 2 番目の要素のみを返す関数を生成する
|
与えられた関数をラップし、その戻り値の 2 番目の要素のみを返す関数を生成する。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
original_function (Callable[..., tuple[Any, Any]])): ラップする元の関数
|
original_function (Callable[..., tuple[Any, Any]])): ラップする元の関数
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ import numpy as np
|
|||||||
import torch
|
import torch
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from config import config
|
||||||
from style_bert_vits2.logging import logger
|
from style_bert_vits2.logging import logger
|
||||||
from style_bert_vits2.models import utils
|
|
||||||
from style_bert_vits2.models.hyper_parameters import HyperParameters
|
from style_bert_vits2.models.hyper_parameters import HyperParameters
|
||||||
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
|
from style_bert_vits2.utils.stdout_wrapper import SAFE_STDOUT
|
||||||
from config import config
|
|
||||||
|
|
||||||
warnings.filterwarnings("ignore", category=UserWarning)
|
warnings.filterwarnings("ignore", category=UserWarning)
|
||||||
from pyannote.audio import Inference, Model
|
from pyannote.audio import Inference, Model
|
||||||
|
|||||||
Reference in New Issue
Block a user