camenduru's picture
Update easy-infer.py
2630ece
raw
history blame contribute delete
No virus
15.8 kB
from multiprocessing import cpu_count
import threading, pdb, librosa
from time import sleep
from subprocess import Popen
from time import sleep
import torch, os, traceback, sys, warnings, shutil, numpy as np
import faiss
from random import shuffle
import scipy.io.wavfile as wavfile
now_dir = os.getcwd()
sys.path.append(now_dir)
tmp = os.path.join(now_dir, "TEMP")
shutil.rmtree(tmp, ignore_errors=True)
os.makedirs(tmp, exist_ok=True)
os.makedirs("audios",exist_ok=True)
os.makedirs(os.path.join(now_dir, "logs"), exist_ok=True)
os.makedirs(os.path.join(now_dir, "weights"), exist_ok=True)
os.environ["TEMP"] = tmp
warnings.filterwarnings("ignore")
torch.manual_seed(114514)
from i18n import I18nAuto
import ffmpeg
i18n = I18nAuto()
# 判断是否有能用来训练和加速推理的N卡
ncpu = cpu_count()
ngpu = torch.cuda.device_count()
gpu_infos = []
mem = []
if (not torch.cuda.is_available()) or ngpu == 0:
if_gpu_ok = False
else:
if_gpu_ok = False
for i in range(ngpu):
gpu_name = torch.cuda.get_device_name(i)
if (
"10" in gpu_name
or "16" in gpu_name
or "20" in gpu_name
or "30" in gpu_name
or "40" in gpu_name
or "A2" in gpu_name.upper()
or "A3" in gpu_name.upper()
or "A4" in gpu_name.upper()
or "P4" in gpu_name.upper()
or "A50" in gpu_name.upper()
or "70" in gpu_name
or "80" in gpu_name
or "90" in gpu_name
or "M4" in gpu_name.upper()
or "T4" in gpu_name.upper()
or "TITAN" in gpu_name.upper()
): # A10#A100#V100#A40#P40#M40#K80#A4500
if_gpu_ok = True # 至少有一张能用的N卡
gpu_infos.append("%s\t%s" % (i, gpu_name))
mem.append(
int(
torch.cuda.get_device_properties(i).total_memory
/ 1024
/ 1024
/ 1024
+ 0.4
)
)
if if_gpu_ok == True and len(gpu_infos) > 0:
gpu_info = "\n".join(gpu_infos)
default_batch_size = min(mem) // 2
else:
gpu_info = "很遗憾您这没有能用的显卡来支持您训练"
default_batch_size = 1
gpus = "-".join([i[0] for i in gpu_infos])
from infer_pack.models import SynthesizerTrnMs256NSFsid, SynthesizerTrnMs256NSFsid_nono
from scipy.io import wavfile
from fairseq import checkpoint_utils
import gradio as gr
import logging
from vc_infer_pipeline import VC
from config import (
is_half,
device,
python_cmd,
listen_port,
iscolab,
noparallel,
noautoopen,
)
from infer_uvr5 import _audio_pre_
from my_utils import load_audio
from train.process_ckpt import show_info, change_info, merge, extract_small_model
# from trainset_preprocess_pipeline import PreProcess
logging.getLogger("numba").setLevel(logging.WARNING)
class ToolButton(gr.Button, gr.components.FormComponent):
"""Small button with single emoji as text, fits inside gradio forms"""
def __init__(self, **kwargs):
super().__init__(variant="tool", **kwargs)
def get_block_name(self):
return "button"
hubert_model = None
def load_hubert():
global hubert_model
models, _, _ = checkpoint_utils.load_model_ensemble_and_task(
["hubert_base.pt"],
suffix="",
)
hubert_model = models[0]
hubert_model = hubert_model.to(device)
if is_half:
hubert_model = hubert_model.half()
else:
hubert_model = hubert_model.float()
hubert_model.eval()
weight_root = "weights"
weight_uvr5_root = "uvr5_weights"
names = []
for name in os.listdir(weight_root):
if name.endswith(".pth"):
names.append(name)
uvr5_names = []
for name in os.listdir(weight_uvr5_root):
if name.endswith(".pth"):
uvr5_names.append(name.replace(".pth", ""))
def find_parent(search_dir, file_name):
for dirpath, dirnames, filenames in os.walk(search_dir):
if file_name in filenames:
return os.path.abspath(dirpath)
return None
def vc_single(
sid,
input_audio,
f0_up_key,
f0_file,
f0_method,
file_index,
# file_big_npy,
index_rate,
): # spk_item, input_audio0, vc_transform0,f0_file,f0method0
global tgt_sr, net_g, vc, hubert_model
if input_audio is None:
return "You need to upload an audio", None
f0_up_key = int(f0_up_key)
try:
parent_dir = find_parent(".",input_audio)
audio = load_audio(parent_dir+'/'+input_audio, 16000)
times = [0, 0, 0]
if hubert_model == None:
load_hubert()
if_f0 = cpt.get("f0", 1)
file_index = (
file_index.strip(" ")
.strip('"')
.strip("\n")
.strip('"')
.strip(" ")
.replace("trained", "added")
) # 防止小白写错,自动帮他替换掉
# file_big_npy = (
# file_big_npy.strip(" ").strip('"').strip("\n").strip('"').strip(" ")
# )
audio_opt = vc.pipeline(
hubert_model,
net_g,
sid,
audio,
times,
f0_up_key,
f0_method,
file_index,
# file_big_npy,
index_rate,
if_f0,
f0_file=f0_file,
)
print(
"npy: ", times[0], "s, f0: ", times[1], "s, infer: ", times[2], "s", sep=""
)
return "Success", (tgt_sr, audio_opt)
except:
info = traceback.format_exc()
print(info)
return info, (None, None)
def vc_multi(
sid,
dir_path,
opt_root,
paths,
f0_up_key,
f0_method,
file_index,
# file_big_npy,
index_rate,
):
try:
dir_path = (
dir_path.strip(" ").strip('"').strip("\n").strip('"').strip(" ")
) # 防止小白拷路径头尾带了空格和"和回车
opt_root = opt_root.strip(" ").strip('"').strip("\n").strip('"').strip(" ")
os.makedirs(opt_root, exist_ok=True)
try:
if dir_path != "":
paths = [os.path.join(dir_path, name) for name in os.listdir(dir_path)]
else:
paths = [path.name for path in paths]
except:
traceback.print_exc()
paths = [path.name for path in paths]
infos = []
file_index = (
file_index.strip(" ")
.strip('"')
.strip("\n")
.strip('"')
.strip(" ")
.replace("trained", "added")
) # 防止小白写错,自动帮他替换掉
for path in paths:
info, opt = vc_single(
sid,
path,
f0_up_key,
None,
f0_method,
file_index,
# file_big_npy,
index_rate,
)
if info == "Success":
try:
tgt_sr, audio_opt = opt
wavfile.write(
"%s/%s" % (opt_root, os.path.basename(path)), tgt_sr, audio_opt
)
except:
info = traceback.format_exc()
infos.append("%s->%s" % (os.path.basename(path), info))
yield "\n".join(infos)
yield "\n".join(infos)
except:
yield traceback.format_exc()
# 一个选项卡全局只能有一个音色
def get_vc(sid):
global n_spk, tgt_sr, net_g, vc, cpt
if sid == []:
global hubert_model
if hubert_model != None: # 考虑到轮询, 需要加个判断看是否 sid 是由有模型切换到无模型的
print("clean_empty_cache")
del net_g, n_spk, vc, hubert_model, tgt_sr # ,cpt
hubert_model = net_g = n_spk = vc = hubert_model = tgt_sr = None
if torch.cuda.is_available():
torch.cuda.empty_cache()
###楼下不这么折腾清理不干净
if_f0 = cpt.get("f0", 1)
if if_f0 == 1:
net_g = SynthesizerTrnMs256NSFsid(*cpt["config"], is_half=is_half)
else:
net_g = SynthesizerTrnMs256NSFsid_nono(*cpt["config"])
del net_g, cpt
if torch.cuda.is_available():
torch.cuda.empty_cache()
cpt = None
return {"visible": False, "__type__": "update"}
person = "%s/%s" % (weight_root, sid)
print("loading %s" % person)
cpt = torch.load(person, map_location="cpu")
tgt_sr = cpt["config"][-1]
cpt["config"][-3] = cpt["weight"]["emb_g.weight"].shape[0] # n_spk
if_f0 = cpt.get("f0", 1)
if if_f0 == 1:
net_g = SynthesizerTrnMs256NSFsid(*cpt["config"], is_half=is_half)
else:
net_g = SynthesizerTrnMs256NSFsid_nono(*cpt["config"])
del net_g.enc_q
print(net_g.load_state_dict(cpt["weight"], strict=False)) # 不加这一行清不干净, 真奇葩
net_g.eval().to(device)
if is_half:
net_g = net_g.half()
else:
net_g = net_g.float()
vc = VC(tgt_sr, device, is_half)
n_spk = cpt["config"][-3]
return {"visible": False, "maximum": n_spk, "__type__": "update"}
def change_choices():
names = []
for name in os.listdir(weight_root):
if name.endswith(".pth"):
names.append(name)
return {"choices": sorted(names), "__type__": "update"}
def change_choices2():
audio_files = []
for dirpath, dirnames, filenames in os.walk("."):
for filename in filenames:
if filename.endswith(('.wav', '.mp3')) and filename not in ('mute.wav', 'mute32k.wav', 'mute40k.wav', 'mute48k.wav', 'audio.wav'):
if "tmp" not in filename:
audio_files.append(filename)
return {"choices": sorted(audio_files), "__type__": "update"}
def clean():
return {"value": "", "__type__": "update"}
def change_sr2(sr2, if_f0_3):
if if_f0_3 == "是":
return "pretrained/f0G%s.pth" % sr2, "pretrained/f0D%s.pth" % sr2
else:
return "pretrained/G%s.pth" % sr2, "pretrained/D%s.pth" % sr2
def get_index():
if iscolab:
chosen_model=sorted(names)[0].split(".")[0]
logs_path="/content/Retrieval-based-Voice-Conversion-WebUI/logs/"+chosen_model
for file in os.listdir(logs_path):
if file.endswith(".index"):
return os.path.join(logs_path, file)
return ''
else:
return ''
def get_indexes():
indexes_list=[]
if iscolab:
for dirpath, dirnames, filenames in os.walk("/content/Retrieval-based-Voice-Conversion-WebUI/logs/"):
for filename in filenames:
if filename.endswith(".index"):
indexes_list.append(os.path.join(dirpath,filename))
return indexes_list
else:
return ''
audio_files=[]
for dirpath, dirnames, filenames in os.walk("."):
for filename in filenames:
if filename.endswith(('.wav', '.mp3')) and filename not in ('mute.wav', 'mute32k.wav', 'mute40k.wav', 'mute48k.wav'):
if "tmp" not in filename:
audio_files.append(filename)
def audios():
audio_files = []
for dirpath, dirnames, filenames in os.walk("."):
for filename in filenames:
if filename.endswith(('.wav', '.mp3')) and filename not in ('mute.wav', 'mute32k.wav', 'mute40k.wav', 'mute48k.wav'):
if "tmp" not in filename:
audio_files.append(filename)
return audio_files
def get_name():
if len(audio_files) > 0:
return sorted(audio_files)[0]
else:
return ''
def save_to_wav(record_button):
shutil.move(record_button,'audios/recording.wav')
#with gr.Blocks() as app
with gr.Blocks(theme=gr.themes.Base()) as app:
with gr.Row():
warntext=gr.Markdown("Do not call your audio 'audio.wav' since that is used by the program to keep track of temporary files.")
with gr.Row():
sid0 = gr.Dropdown(label="1.Choose your Model.", choices=sorted(names), value=sorted(names)[0])
get_vc(sorted(names)[0])
vc_transform0 = gr.Number(label="Optional: You can change the pitch here or leave it at 0.", value=0)
#refresh_button = gr.Button("Refresh Voice List", variant="primary")
#refresh_button.click(fn=change_choices, inputs=[], outputs=[sid0])
#clean_button = gr.Button("Unload Voice to Save Memory", variant="primary")
spk_item = gr.Slider(minimum=0,maximum=2333,step=1,label="Please select speaker id",value=0,visible=False,interactive=True)
#clean_button.click(fn=clean, inputs=[], outputs=[sid0])
sid0.change(
fn=get_vc,
inputs=[sid0],
outputs=[],
)
but0 = gr.Button("Convert", variant="primary")
with gr.Row():
with gr.Column():
with gr.Row():
dropbox = gr.File(label="Drop your audio here & hit the Reload button.")
with gr.Row():
record_button=gr.Audio(source="microphone", label="OR Record audio.", type="filepath")
with gr.Row():
#input_audio0 = gr.Textbox(label="Enter the Path to the Audio File to be Processed (e.g. /content/youraudio.wav)",value="/content/youraudio.wav")
input_audio0 = gr.Dropdown(choices=sorted(audio_files), label="2.Choose your audio.", value=get_name())
dropbox.upload(fn=change_choices2, inputs=[], outputs=[input_audio0])
refresh_button2 = gr.Button("Reload Audios", variant="primary")
refresh_button2.click(fn=change_choices2, inputs=[], outputs=[input_audio0])
record_button.change(fn=save_to_wav, inputs=[record_button], outputs=[])
with gr.Column():
file_index1 = gr.Dropdown(
label="3. Path to your added.index file (if it didn't automatically find it.)",
value=get_index(),
choices=get_indexes(),
interactive=True,
)
index_rate1 = gr.Slider(
minimum=0,
maximum=1,
label="Strength:",
value=0.69,
interactive=True,
)
with gr.Row():
vc_output2 = gr.Audio(label="Output Audio (Click on the Three Dots in the Right Corner to Download)")
with gr.Row():
f0method0 = gr.Radio(
label="Optional: Change the Pitch Extraction Algorithm. Use PM for fast results or Harvest for better low range (but it's extremely slow)",
choices=["pm", "harvest"],
value="pm",
interactive=True,
)
with gr.Row():
vc_output1 = gr.Textbox(label="")
with gr.Row():
instructions = gr.Markdown("""
This is simply a modified version of the RVC GUI found here:
https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI
""")
f0_file = gr.File(label="F0 Curve File (Optional, One Pitch Per Line, Replaces Default F0 and Pitch Shift)", visible=False)
but0.click(
vc_single,
[
spk_item,
input_audio0,
vc_transform0,
f0_file,
f0method0,
file_index1,
index_rate1,
],
[vc_output1, vc_output2]
)
if iscolab:
app.queue().launch(share=True)
else:
app.queue(concurrency_count=511, max_size=1022).launch(
server_name="0.0.0.0",
inbrowser=not noautoopen,
server_port=listen_port,
quiet=True,
)