|
import datetime |
|
import os |
|
|
|
import gradio as gr |
|
import numpy as np |
|
import torch |
|
|
|
from infer import infer |
|
from server_fastapi import Models |
|
|
|
is_hf_spaces = os.getenv("SYSTEM") == "spaces" |
|
limit = 100 |
|
|
|
|
|
root_dir = "weights" |
|
|
|
model_holder = Models() |
|
|
|
|
|
def refresh_model(): |
|
global model_holder |
|
model_holder = Models() |
|
|
|
model_dirs = [ |
|
d for d in os.listdir(root_dir) if os.path.isdir(os.path.join(root_dir, d)) |
|
] |
|
model_names = [] |
|
for model_name in model_dirs: |
|
model_dir = os.path.join(root_dir, model_name) |
|
pth_files = [f for f in os.listdir(model_dir) if f.endswith(".pth")] |
|
if len(pth_files) != 1: |
|
print(f"{root_dir}/{model_name}のpthファイルの数が1つではないので無視します") |
|
continue |
|
model_path = os.path.join(model_dir, pth_files[0]) |
|
config_path = os.path.join(model_dir, "config.json") |
|
try: |
|
model_holder.init_model( |
|
config_path=config_path, |
|
model_path=model_path, |
|
device=device, |
|
language="JP", |
|
) |
|
model_names.append(model_name) |
|
except Exception as e: |
|
print(f"{root_dir}/{model_name}の初期化に失敗しました\n{e}") |
|
continue |
|
return model_names |
|
|
|
|
|
def update_model_dropdown(): |
|
model_names = refresh_model() |
|
return gr.Dropdown(choices=model_names, value=model_names[0]) |
|
|
|
|
|
|
|
def _voice( |
|
model_id: int, |
|
text: str, |
|
language: str = "JP", |
|
emotion: int = 0, |
|
sdp_ratio: float = 0.2, |
|
noise: float = 0.6, |
|
noisew: float = 0.8, |
|
length: float = 1.0, |
|
line_split: bool = True, |
|
split_interval: float = 0.2, |
|
speaker_id: int = 0, |
|
): |
|
if model_id not in model_holder.models.keys(): |
|
return f"エラー、model_id={model_id}は存在しません", None |
|
speaker_name = model_holder.models[model_id].id2spk[speaker_id] |
|
|
|
start_time = datetime.datetime.now() |
|
|
|
print("-----") |
|
print(datetime.datetime.now()) |
|
print( |
|
f"model_id={model_id}, speaker_id={speaker_id}, speaker_name={speaker_name}, language={language}" |
|
) |
|
print(f"text:\n{text}") |
|
|
|
if is_hf_spaces and len(text) > limit: |
|
print(f"Error: 文字数が{limit}文字を超えています") |
|
return f"エラー、文字数が{limit}文字を超えています", None |
|
|
|
try: |
|
if not line_split: |
|
with torch.no_grad(): |
|
audio = infer( |
|
text=text, |
|
sdp_ratio=sdp_ratio, |
|
noise_scale=noise, |
|
noise_scale_w=noisew, |
|
length_scale=length, |
|
sid=speaker_name, |
|
language=language, |
|
hps=model_holder.models[model_id].hps, |
|
net_g=model_holder.models[model_id].net_g, |
|
device=model_holder.models[model_id].device, |
|
emotion=emotion, |
|
) |
|
else: |
|
texts = text.split("\n") |
|
texts = [t for t in texts if t != ""] |
|
audios = [] |
|
with torch.no_grad(): |
|
for i, t in enumerate(texts): |
|
audios.append( |
|
infer( |
|
text=t, |
|
sdp_ratio=sdp_ratio, |
|
noise_scale=noise, |
|
noise_scale_w=noisew, |
|
length_scale=length, |
|
sid=speaker_name, |
|
language=language, |
|
hps=model_holder.models[model_id].hps, |
|
net_g=model_holder.models[model_id].net_g, |
|
device=model_holder.models[model_id].device, |
|
emotion=emotion, |
|
) |
|
) |
|
if i != len(texts) - 1: |
|
audios.append(np.zeros(int(44100 * split_interval))) |
|
audio = np.concatenate(audios) |
|
end_time = datetime.datetime.now() |
|
duration = (end_time - start_time).total_seconds() |
|
print(f"{end_time}: Done, {duration} seconds.") |
|
return f"Success, time: {duration} seconds.", ( |
|
model_holder.models[model_id].hps.data.sampling_rate, |
|
audio, |
|
) |
|
except Exception as e: |
|
print(f"Error: {e}") |
|
return f"エラー\n{e}", None |
|
|
|
|
|
initial_text = "この電車は、急行、調布ゆきです。" |
|
|
|
example_local = [ |
|
[initial_text, "JP"], |
|
[ |
|
"""私、ずっと前からあなたのことを見てきました。あなたの笑顔、優しさ、強さに、心惹かれていたんです。 |
|
友達として過ごす中で、あなたのことがだんだんと特別な存在になっていくのがわかりました。 |
|
えっと、私、あなたのことが好きです!もしよければ、私と付き合ってくれませんか?""", |
|
"JP", |
|
], |
|
[ |
|
"""吾輩は猫である。名前はまだ無い。 |
|
どこで生れたかとんと見当がつかぬ。なんでも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。 |
|
吾輩はここで始めて人間というものを見た。しかもあとで聞くと、それは書生という、人間中で一番獰悪な種族であったそうだ。 |
|
この書生というのは時々我々を捕まえて煮て食うという話である。""", |
|
"JP", |
|
], |
|
[ |
|
"""桜の樹の下には屍体が埋まっている!これは信じていいことなんだよ。 |
|
何故って、桜の花があんなにも見事に咲くなんて信じられないことじゃないか。俺はあの美しさが信じられないので、このにさんにち不安だった。 |
|
しかしいま、やっとわかるときが来た。桜の樹の下には屍体が埋まっている。これは信じていいことだ。""", |
|
"JP", |
|
], |
|
[ |
|
"""やったー!テストで満点取れたよ!私とっても嬉しいな! |
|
どうして私の意見を無視するの?許せない!ムカつく!あんたなんか死ねばいいのに。 |
|
あはははっ!この漫画めっちゃ笑える、見てよこれ、ふふふ、あはは。 |
|
あなたがいなくなって、私は一人になっちゃって、泣いちゃいそうなほど悲しい。""", |
|
"JP", |
|
], |
|
[ |
|
"""やりました!テストで満点取れましたよ!私とっても嬉しいです! |
|
どうして私の意見を無視するんですか?許せません!ムカつきます!あんたなんか死んでください。 |
|
あはははっ!この漫画めっちゃ笑えます、見てくださいこれ、ふふふ、あはは。 |
|
あなたがいなくなって、私は一人になっちゃって、泣いちゃいそうなほど悲しいです。""", |
|
"JP", |
|
], |
|
[ |
|
"""音声合成は、機械学習を活用して、テキストから人の声を再現する技術です。この技術は、言語の構造を解析し、それに基づいて音声を生成します。 |
|
この分野の最新の研究成果を使うと、より自然で表現豊かな音声の生成が可能である。深層学習の応用により、感情やアクセントを含む声質の微妙な変化も再現することが出来る。""", |
|
"JP", |
|
], |
|
[ |
|
"Speech synthesis is the artificial production of human speech. A computer system used for this purpose is called a speech synthesizer, and can be implemented in software or hardware products.", |
|
"EN", |
|
], |
|
] |
|
|
|
example_hf_spaces = [ |
|
[initial_text, "JP"], |
|
["えっと、私、あなたのことが好きです!もしよければ付き合ってくれませんか?", "JP"], |
|
["吾輩は猫である。名前はまだ無い。", "JP"], |
|
["どこで生れたかとんと見当がつかぬ。なんでも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。", "JP"], |
|
["やったー!テストで満点取れたよ!私とっても嬉しいな!", "JP"], |
|
["どうして私の意見を無視するの?許せない!ムカつく!あんたなんか死ねばいいのに。", "JP"], |
|
["あはははっ!この漫画めっちゃ笑える、見てよこれ、ふふふ、あはは。", "JP"], |
|
["あなたがいなくなって、私は一人になっちゃって、泣いちゃいそうなほど悲しい。", "JP"], |
|
["深層学習の応用により、感情やアクセントを含む声質の微妙な変化も再現されている。", "JP"], |
|
] |
|
|
|
initial_md = """ |
|
# 京王線自動放送メーカー |
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
|
|
|
if __name__ == "__main__": |
|
device = "cuda" if torch.cuda.is_available() else "cpu" |
|
|
|
|
|
languages = ["JP", "EN", "ZH"] |
|
examples = example_hf_spaces if is_hf_spaces else example_local |
|
|
|
model_names = refresh_model() |
|
|
|
with gr.Blocks() as app: |
|
gr.Markdown(initial_md) |
|
with gr.Row(): |
|
with gr.Column(): |
|
with gr.Row(): |
|
model_input = gr.Dropdown( |
|
label="モデル一覧", |
|
choices=model_names, |
|
type="index", |
|
scale=3, |
|
) |
|
if not is_hf_spaces: |
|
refresh_button = gr.Button("モデル一覧を更新", scale=1) |
|
refresh_button.click( |
|
update_model_dropdown, outputs=[model_input] |
|
) |
|
text_input = gr.TextArea(label="テキスト", value=initial_text) |
|
language = gr.Dropdown(choices=languages, value="JP", label="言語") |
|
line_split = gr.Checkbox(label="改行で分けて生成", value=not is_hf_spaces) |
|
split_interval = gr.Slider( |
|
minimum=0.1, maximum=2, value=0.5, step=0.1, label="分けた場合に挟む無音の長さ" |
|
) |
|
|
|
with gr.Accordion(label="詳細設定", open=False): |
|
emotion = gr.Slider( |
|
minimum=0, maximum=9, value=0, step=1, label="Emotion" |
|
) |
|
sdp_ratio = gr.Slider( |
|
minimum=0, maximum=1, value=0.2, step=0.1, label="SDP Ratio" |
|
) |
|
noise_scale = gr.Slider( |
|
minimum=0.1, maximum=2, value=0.6, step=0.1, label="Noise" |
|
) |
|
noise_scale_w = gr.Slider( |
|
minimum=0.1, maximum=2, value=0.8, step=0.1, label="Noise_W" |
|
) |
|
length_scale = gr.Slider( |
|
minimum=0.1, maximum=2, value=1.0, step=0.1, label="Length" |
|
) |
|
with gr.Column(): |
|
button = gr.Button("実行", variant="primary") |
|
text_output = gr.Textbox(label="情報") |
|
audio_output = gr.Audio(label="結果") |
|
|
|
button.click( |
|
_voice, |
|
inputs=[ |
|
model_input, |
|
text_input, |
|
language, |
|
emotion, |
|
sdp_ratio, |
|
noise_scale, |
|
noise_scale_w, |
|
length_scale, |
|
line_split, |
|
split_interval, |
|
], |
|
outputs=[text_output, audio_output], |
|
) |
|
|
|
app.launch(inbrowser=True) |
|
|