soiz1/Retrieval-based-Voice-Conversion-WebUI
0
1import os2import sys3from dotenv import load_dotenv4 5now_dir = os.getcwd()6sys.path.append(now_dir)7load_dotenv()8from infer.modules.vc.modules import VC9from infer.modules.uvr5.modules import uvr10from infer.lib.train.process_ckpt import (11 change_info,12 extract_small_model,13 merge,14 show_info,15)16from i18n.i18n import I18nAuto17from configs.config import Config18from sklearn.cluster import MiniBatchKMeans19import torch, platform20import numpy as np21import gradio as gr22import faiss23import fairseq24import pathlib25import json26from time import sleep27from subprocess import Popen28from random import shuffle29import warnings30import traceback31import threading32import shutil33import logging34 35 36logging.getLogger("numba").setLevel(logging.WARNING)37logging.getLogger("httpx").setLevel(logging.WARNING)38 39logger = logging.getLogger(__name__)40 41tmp = os.path.join(now_dir, "TEMP")42shutil.rmtree(tmp, ignore_errors=True)43shutil.rmtree("%s/runtime/Lib/site-packages/infer_pack" % (now_dir), ignore_errors=True)44shutil.rmtree("%s/runtime/Lib/site-packages/uvr5_pack" % (now_dir), ignore_errors=True)45os.makedirs(tmp, exist_ok=True)46os.makedirs(os.path.join(now_dir, "logs"), exist_ok=True)47os.makedirs(os.path.join(now_dir, "assets/weights"), exist_ok=True)48os.environ["TEMP"] = tmp49warnings.filterwarnings("ignore")50torch.manual_seed(114514)51 52 53config = Config()54vc = VC(config)55 56 57if config.dml == True:58 59 def forward_dml(ctx, x, scale):60 ctx.scale = scale61 res = x.clone().detach()62 return res63 64 fairseq.modules.grad_multiply.GradMultiply.forward = forward_dml65i18n = I18nAuto()66logger.info(i18n)67# 判断是否有能用来训练和加速推理的N卡68ngpu = torch.cuda.device_count()69gpu_infos = []70mem = []71if_gpu_ok = False72 73if torch.cuda.is_available() or ngpu != 0:74 for i in range(ngpu):75 gpu_name = torch.cuda.get_device_name(i)76 if any(77 value in gpu_name.upper()78 for value in [79 "10",80 "16",81 "20",82 "30",83 "40",84 "A2",85 "A3",86 "A4",87 "P4",88 "A50",89 "500",90 "A60",91 "70",92 "80",93 "90",94 "M4",95 "T4",96 "TITAN",97 "4060",98 "L",99 "6000",100 ]101 ):102 # A10#A100#V100#A40#P40#M40#K80#A4500103 if_gpu_ok = True # 至少有一张能用的N卡104 gpu_infos.append("%s\t%s" % (i, gpu_name))105 mem.append(106 int(107 torch.cuda.get_device_properties(i).total_memory108 / 1024109 / 1024110 / 1024111 + 0.4112 )113 )114if if_gpu_ok and len(gpu_infos) > 0:115 gpu_info = "\n".join(gpu_infos)116 default_batch_size = min(mem) // 2117else:118 gpu_info = i18n("很遗憾您这没有能用的显卡来支持您训练")119 default_batch_size = 1120gpus = "-".join([i[0] for i in gpu_infos])121 122 123class ToolButton(gr.Button, gr.components.FormComponent):124 """Small button with single emoji as text, fits inside gradio forms"""125 126 def __init__(self, **kwargs):127 super().__init__(variant="tool", **kwargs)128 129 def get_block_name(self):130 return "button"131 132 133weight_root = os.getenv("weight_root")134weight_uvr5_root = os.getenv("weight_uvr5_root")135index_root = os.getenv("index_root")136outside_index_root = os.getenv("outside_index_root")137 138names = []139for name in os.listdir(weight_root):140 if name.endswith(".pth"):141 names.append(name)142index_paths = []143 144 145def lookup_indices(index_root):146 global index_paths147 for root, dirs, files in os.walk(index_root, topdown=False):148 for name in files:149 if name.endswith(".index") and "trained" not in name:150 index_paths.append("%s/%s" % (root, name))151 152 153lookup_indices(index_root)154lookup_indices(outside_index_root)155uvr5_names = []156for name in os.listdir(weight_uvr5_root):157 if name.endswith(".pth") or "onnx" in name:158 uvr5_names.append(name.replace(".pth", ""))159 160 161def change_choices():162 names = []163 for name in os.listdir(weight_root):164 if name.endswith(".pth"):165 names.append(name)166 index_paths = []167 for root, dirs, files in os.walk(index_root, topdown=False):168 for name in files:169 if name.endswith(".index") and "trained" not in name:170 index_paths.append("%s/%s" % (root, name))171 return {"choices": sorted(names), "__type__": "update"}, {172 "choices": sorted(index_paths),173 "__type__": "update",174 }175 176 177def clean():178 return {"value": "", "__type__": "update"}179 180 181def export_onnx(ModelPath, ExportedPath):182 from infer.modules.onnx.export import export_onnx as eo183 184 eo(ModelPath, ExportedPath)185 186 187sr_dict = {188 "32k": 32000,189 "40k": 40000,190 "48k": 48000,191}192 193 194def if_done(done, p):195 while 1:196 if p.poll() is None:197 sleep(0.5)198 else:199 break200 done[0] = True201 202 203def if_done_multi(done, ps):204 while 1:205 # poll==None代表进程未结束206 # 只要有一个进程未结束都不停207 flag = 1208 for p in ps:209 if p.poll() is None:210 flag = 0211 sleep(0.5)212 break213 if flag == 1:214 break215 done[0] = True216 217 218def preprocess_dataset(trainset_dir, exp_dir, sr, n_p):219 sr = sr_dict[sr]220 os.makedirs("%s/logs/%s" % (now_dir, exp_dir), exist_ok=True)221 f = open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir), "w")222 f.close()223 cmd = '"%s" infer/modules/train/preprocess.py "%s" %s %s "%s/logs/%s" %s %.1f' % (224 config.python_cmd,225 trainset_dir,226 sr,227 n_p,228 now_dir,229 exp_dir,230 config.noparallel,231 config.preprocess_per,232 )233 logger.info("Execute: " + cmd)234 # , stdin=PIPE, stdout=PIPE,stderr=PIPE,cwd=now_dir235 p = Popen(cmd, shell=True)236 # 煞笔gr, popen read都非得全跑完了再一次性读取, 不用gr就正常读一句输出一句;只能额外弄出一个文本流定时读237 done = [False]238 threading.Thread(239 target=if_done,240 args=(241 done,242 p,243 ),244 ).start()245 while 1:246 with open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir), "r") as f:247 yield (f.read())248 sleep(1)249 if done[0]:250 break251 with open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir), "r") as f:252 log = f.read()253 logger.info(log)254 yield log255 256 257# but2.click(extract_f0,[gpus6,np7,f0method8,if_f0_3,trainset_dir4],[info2])258def extract_f0_feature(gpus, n_p, f0method, if_f0, exp_dir, version19, gpus_rmvpe):259 gpus = gpus.split("-")260 os.makedirs("%s/logs/%s" % (now_dir, exp_dir), exist_ok=True)261 f = open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "w")262 f.close()263 if if_f0:264 if f0method != "rmvpe_gpu":265 cmd = (266 '"%s" infer/modules/train/extract/extract_f0_print.py "%s/logs/%s" %s %s'267 % (268 config.python_cmd,269 now_dir,270 exp_dir,271 n_p,272 f0method,273 )274 )275 logger.info("Execute: " + cmd)276 p = Popen(277 cmd, shell=True, cwd=now_dir278 ) # , stdin=PIPE, stdout=PIPE,stderr=PIPE279 # 煞笔gr, popen read都非得全跑完了再一次性读取, 不用gr就正常读一句输出一句;只能额外弄出一个文本流定时读280 done = [False]281 threading.Thread(282 target=if_done,283 args=(284 done,285 p,286 ),287 ).start()288 else:289 if gpus_rmvpe != "-":290 gpus_rmvpe = gpus_rmvpe.split("-")291 leng = len(gpus_rmvpe)292 ps = []293 for idx, n_g in enumerate(gpus_rmvpe):294 cmd = (295 '"%s" infer/modules/train/extract/extract_f0_rmvpe.py %s %s %s "%s/logs/%s" %s '296 % (297 config.python_cmd,298 leng,299 idx,300 n_g,301 now_dir,302 exp_dir,303 config.is_half,304 )305 )306 logger.info("Execute: " + cmd)307 p = Popen(308 cmd, shell=True, cwd=now_dir309 ) # , shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=now_dir310 ps.append(p)311 # 煞笔gr, popen read都非得全跑完了再一次性读取, 不用gr就正常读一句输出一句;只能额外弄出一个文本流定时读312 done = [False]313 threading.Thread(314 target=if_done_multi, #315 args=(316 done,317 ps,318 ),319 ).start()320 else:321 cmd = (322 config.python_cmd323 + ' infer/modules/train/extract/extract_f0_rmvpe_dml.py "%s/logs/%s" '324 % (325 now_dir,326 exp_dir,327 )328 )329 logger.info("Execute: " + cmd)330 p = Popen(331 cmd, shell=True, cwd=now_dir332 ) # , shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=now_dir333 p.wait()334 done = [True]335 while 1:336 with open(337 "%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r"338 ) as f:339 yield (f.read())340 sleep(1)341 if done[0]:342 break343 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r") as f:344 log = f.read()345 logger.info(log)346 yield log347 # 对不同part分别开多进程348 """349 n_part=int(sys.argv[1])350 i_part=int(sys.argv[2])351 i_gpu=sys.argv[3]352 exp_dir=sys.argv[4]353 os.environ["CUDA_VISIBLE_DEVICES"]=str(i_gpu)354 """355 leng = len(gpus)356 ps = []357 for idx, n_g in enumerate(gpus):358 cmd = (359 '"%s" infer/modules/train/extract_feature_print.py %s %s %s %s "%s/logs/%s" %s %s'360 % (361 config.python_cmd,362 config.device,363 leng,364 idx,365 n_g,366 now_dir,367 exp_dir,368 version19,369 config.is_half,370 )371 )372 logger.info("Execute: " + cmd)373 p = Popen(374 cmd, shell=True, cwd=now_dir375 ) # , shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=now_dir376 ps.append(p)377 # 煞笔gr, popen read都非得全跑完了再一次性读取, 不用gr就正常读一句输出一句;只能额外弄出一个文本流定时读378 done = [False]379 threading.Thread(380 target=if_done_multi,381 args=(382 done,383 ps,384 ),385 ).start()386 while 1:387 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r") as f:388 yield (f.read())389 sleep(1)390 if done[0]:391 break392 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r") as f:393 log = f.read()394 logger.info(log)395 yield log396 397 398def get_pretrained_models(path_str, f0_str, sr2):399 if_pretrained_generator_exist = os.access(400 "assets/pretrained%s/%sG%s.pth" % (path_str, f0_str, sr2), os.F_OK401 )402 if_pretrained_discriminator_exist = os.access(403 "assets/pretrained%s/%sD%s.pth" % (path_str, f0_str, sr2), os.F_OK404 )405 if not if_pretrained_generator_exist:406 logger.warning(407 "assets/pretrained%s/%sG%s.pth not exist, will not use pretrained model",408 path_str,409 f0_str,410 sr2,411 )412 if not if_pretrained_discriminator_exist:413 logger.warning(414 "assets/pretrained%s/%sD%s.pth not exist, will not use pretrained model",415 path_str,416 f0_str,417 sr2,418 )419 return (420 (421 "assets/pretrained%s/%sG%s.pth" % (path_str, f0_str, sr2)422 if if_pretrained_generator_exist423 else ""424 ),425 (426 "assets/pretrained%s/%sD%s.pth" % (path_str, f0_str, sr2)427 if if_pretrained_discriminator_exist428 else ""429 ),430 )431 432 433def change_sr2(sr2, if_f0_3, version19):434 path_str = "" if version19 == "v1" else "_v2"435 f0_str = "f0" if if_f0_3 else ""436 return get_pretrained_models(path_str, f0_str, sr2)437 438 439def change_version19(sr2, if_f0_3, version19):440 path_str = "" if version19 == "v1" else "_v2"441 if sr2 == "32k" and version19 == "v1":442 sr2 = "40k"443 to_return_sr2 = (444 {"choices": ["40k", "48k"], "__type__": "update", "value": sr2}445 if version19 == "v1"446 else {"choices": ["40k", "48k", "32k"], "__type__": "update", "value": sr2}447 )448 f0_str = "f0" if if_f0_3 else ""449 return (450 *get_pretrained_models(path_str, f0_str, sr2),451 to_return_sr2,452 )453 454 455def change_f0(if_f0_3, sr2, version19): # f0method8,pretrained_G14,pretrained_D15456 path_str = "" if version19 == "v1" else "_v2"457 return (458 {"visible": if_f0_3, "__type__": "update"},459 {"visible": if_f0_3, "__type__": "update"},460 *get_pretrained_models(path_str, "f0" if if_f0_3 == True else "", sr2),461 )462 463 464# but3.click(click_train,[exp_dir1,sr2,if_f0_3,save_epoch10,total_epoch11,batch_size12,if_save_latest13,pretrained_G14,pretrained_D15,gpus16])465def click_train(466 exp_dir1,467 sr2,468 if_f0_3,469 spk_id5,470 save_epoch10,471 total_epoch11,472 batch_size12,473 if_save_latest13,474 pretrained_G14,475 pretrained_D15,476 gpus16,477 if_cache_gpu17,478 if_save_every_weights18,479 version19,480):481 # 生成filelist482 exp_dir = "%s/logs/%s" % (now_dir, exp_dir1)483 os.makedirs(exp_dir, exist_ok=True)484 gt_wavs_dir = "%s/0_gt_wavs" % (exp_dir)485 feature_dir = (486 "%s/3_feature256" % (exp_dir)487 if version19 == "v1"488 else "%s/3_feature768" % (exp_dir)489 )490 if if_f0_3:491 f0_dir = "%s/2a_f0" % (exp_dir)492 f0nsf_dir = "%s/2b-f0nsf" % (exp_dir)493 names = (494 set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)])495 & set([name.split(".")[0] for name in os.listdir(feature_dir)])496 & set([name.split(".")[0] for name in os.listdir(f0_dir)])497 & set([name.split(".")[0] for name in os.listdir(f0nsf_dir)])498 )499 else:500 names = set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)]) & set(501 [name.split(".")[0] for name in os.listdir(feature_dir)]502 )503 opt = []504 for name in names:505 if if_f0_3:506 opt.append(507 "%s/%s.wav|%s/%s.npy|%s/%s.wav.npy|%s/%s.wav.npy|%s"508 % (509 gt_wavs_dir.replace("\\", "\\\\"),510 name,511 feature_dir.replace("\\", "\\\\"),512 name,513 f0_dir.replace("\\", "\\\\"),514 name,515 f0nsf_dir.replace("\\", "\\\\"),516 name,517 spk_id5,518 )519 )520 else:521 opt.append(522 "%s/%s.wav|%s/%s.npy|%s"523 % (524 gt_wavs_dir.replace("\\", "\\\\"),525 name,526 feature_dir.replace("\\", "\\\\"),527 name,528 spk_id5,529 )530 )531 fea_dim = 256 if version19 == "v1" else 768532 if if_f0_3:533 for _ in range(2):534 opt.append(535 "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature%s/mute.npy|%s/logs/mute/2a_f0/mute.wav.npy|%s/logs/mute/2b-f0nsf/mute.wav.npy|%s"536 % (now_dir, sr2, now_dir, fea_dim, now_dir, now_dir, spk_id5)537 )538 else:539 for _ in range(2):540 opt.append(541 "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature%s/mute.npy|%s"542 % (now_dir, sr2, now_dir, fea_dim, spk_id5)543 )544 shuffle(opt)545 with open("%s/filelist.txt" % exp_dir, "w") as f:546 f.write("\n".join(opt))547 logger.debug("Write filelist done")548 # 生成config#无需生成config549 # cmd = python_cmd + " train_nsf_sim_cache_sid_load_pretrain.py -e mi-test -sr 40k -f0 1 -bs 4 -g 0 -te 10 -se 5 -pg pretrained/f0G40k.pth -pd pretrained/f0D40k.pth -l 1 -c 0"550 logger.info("Use gpus: %s", str(gpus16))551 if pretrained_G14 == "":552 logger.info("No pretrained Generator")553 if pretrained_D15 == "":554 logger.info("No pretrained Discriminator")555 if version19 == "v1" or sr2 == "40k":556 config_path = "v1/%s.json" % sr2557 else:558 config_path = "v2/%s.json" % sr2559 config_save_path = os.path.join(exp_dir, "config.json")560 if not pathlib.Path(config_save_path).exists():561 with open(config_save_path, "w", encoding="utf-8") as f:562 json.dump(563 config.json_config[config_path],564 f,565 ensure_ascii=False,566 indent=4,567 sort_keys=True,568 )569 f.write("\n")570 if gpus16:571 cmd = (572 '"%s" infer/modules/train/train.py -e "%s" -sr %s -f0 %s -bs %s -g %s -te %s -se %s %s %s -l %s -c %s -sw %s -v %s'573 % (574 config.python_cmd,575 exp_dir1,576 sr2,577 1 if if_f0_3 else 0,578 batch_size12,579 gpus16,580 total_epoch11,581 save_epoch10,582 "-pg %s" % pretrained_G14 if pretrained_G14 != "" else "",583 "-pd %s" % pretrained_D15 if pretrained_D15 != "" else "",584 1 if if_save_latest13 == i18n("是") else 0,585 1 if if_cache_gpu17 == i18n("是") else 0,586 1 if if_save_every_weights18 == i18n("是") else 0,587 version19,588 )589 )590 else:591 cmd = (592 '"%s" infer/modules/train/train.py -e "%s" -sr %s -f0 %s -bs %s -te %s -se %s %s %s -l %s -c %s -sw %s -v %s'593 % (594 config.python_cmd,595 exp_dir1,596 sr2,597 1 if if_f0_3 else 0,598 batch_size12,599 total_epoch11,600 save_epoch10,601 "-pg %s" % pretrained_G14 if pretrained_G14 != "" else "",602 "-pd %s" % pretrained_D15 if pretrained_D15 != "" else "",603 1 if if_save_latest13 == i18n("是") else 0,604 1 if if_cache_gpu17 == i18n("是") else 0,605 1 if if_save_every_weights18 == i18n("是") else 0,606 version19,607 )608 )609 logger.info("Execute: " + cmd)610 p = Popen(cmd, shell=True, cwd=now_dir)611 p.wait()612 return "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log"613 614 615# but4.click(train_index, [exp_dir1], info3)616def train_index(exp_dir1, version19):617 # exp_dir = "%s/logs/%s" % (now_dir, exp_dir1)618 exp_dir = "logs/%s" % (exp_dir1)619 os.makedirs(exp_dir, exist_ok=True)620 feature_dir = (621 "%s/3_feature256" % (exp_dir)622 if version19 == "v1"623 else "%s/3_feature768" % (exp_dir)624 )625 if not os.path.exists(feature_dir):626 return "请先进行特征提取!"627 listdir_res = list(os.listdir(feature_dir))628 if len(listdir_res) == 0:629 return "请先进行特征提取!"630 infos = []631 npys = []632 for name in sorted(listdir_res):633 phone = np.load("%s/%s" % (feature_dir, name))634 npys.append(phone)635 big_npy = np.concatenate(npys, 0)636 big_npy_idx = np.arange(big_npy.shape[0])637 np.random.shuffle(big_npy_idx)638 big_npy = big_npy[big_npy_idx]639 if big_npy.shape[0] > 2e5:640 infos.append("Trying doing kmeans %s shape to 10k centers." % big_npy.shape[0])641 yield "\n".join(infos)642 try:643 big_npy = (644 MiniBatchKMeans(645 n_clusters=10000,646 verbose=True,647 batch_size=256 * config.n_cpu,648 compute_labels=False,649 init="random",650 )651 .fit(big_npy)652 .cluster_centers_653 )654 except:655 info = traceback.format_exc()656 logger.info(info)657 infos.append(info)658 yield "\n".join(infos)659 660 np.save("%s/total_fea.npy" % exp_dir, big_npy)661 n_ivf = min(int(16 * np.sqrt(big_npy.shape[0])), big_npy.shape[0] // 39)662 infos.append("%s,%s" % (big_npy.shape, n_ivf))663 yield "\n".join(infos)664 index = faiss.index_factory(256 if version19 == "v1" else 768, "IVF%s,Flat" % n_ivf)665 # index = faiss.index_factory(256if version19=="v1"else 768, "IVF%s,PQ128x4fs,RFlat"%n_ivf)666 infos.append("training")667 yield "\n".join(infos)668 index_ivf = faiss.extract_index_ivf(index) #669 index_ivf.nprobe = 1670 index.train(big_npy)671 faiss.write_index(672 index,673 "%s/trained_IVF%s_Flat_nprobe_%s_%s_%s.index"674 % (exp_dir, n_ivf, index_ivf.nprobe, exp_dir1, version19),675 )676 infos.append("adding")677 yield "\n".join(infos)678 batch_size_add = 8192679 for i in range(0, big_npy.shape[0], batch_size_add):680 index.add(big_npy[i : i + batch_size_add])681 faiss.write_index(682 index,683 "%s/added_IVF%s_Flat_nprobe_%s_%s_%s.index"684 % (exp_dir, n_ivf, index_ivf.nprobe, exp_dir1, version19),685 )686 infos.append(687 "成功构建索引 added_IVF%s_Flat_nprobe_%s_%s_%s.index"688 % (n_ivf, index_ivf.nprobe, exp_dir1, version19)689 )690 try:691 link = os.link if platform.system() == "Windows" else os.symlink692 link(693 "%s/added_IVF%s_Flat_nprobe_%s_%s_%s.index"694 % (exp_dir, n_ivf, index_ivf.nprobe, exp_dir1, version19),695 "%s/%s_IVF%s_Flat_nprobe_%s_%s_%s.index"696 % (697 outside_index_root,698 exp_dir1,699 n_ivf,700 index_ivf.nprobe,701 exp_dir1,702 version19,703 ),704 )705 infos.append("链接索引到外部-%s" % (outside_index_root))706 except:707 infos.append("链接索引到外部-%s失败" % (outside_index_root))708 709 # faiss.write_index(index, '%s/added_IVF%s_Flat_FastScan_%s.index'%(exp_dir,n_ivf,version19))710 # infos.append("成功构建索引,added_IVF%s_Flat_FastScan_%s.index"%(n_ivf,version19))711 yield "\n".join(infos)712 713 714# but5.click(train1key, [exp_dir1, sr2, if_f0_3, trainset_dir4, spk_id5, gpus6, np7, f0method8, save_epoch10, total_epoch11, batch_size12, if_save_latest13, pretrained_G14, pretrained_D15, gpus16, if_cache_gpu17], info3)715def train1key(716 exp_dir1,717 sr2,718 if_f0_3,719 trainset_dir4,720 spk_id5,721 np7,722 f0method8,723 save_epoch10,724 total_epoch11,725 batch_size12,726 if_save_latest13,727 pretrained_G14,728 pretrained_D15,729 gpus16,730 if_cache_gpu17,731 if_save_every_weights18,732 version19,733 gpus_rmvpe,734):735 infos = []736 737 def get_info_str(strr):738 infos.append(strr)739 return "\n".join(infos)740 741 # step1:处理数据742 yield get_info_str(i18n("step1:正在处理数据"))743 [get_info_str(_) for _ in preprocess_dataset(trainset_dir4, exp_dir1, sr2, np7)]744 745 # step2a:提取音高746 yield get_info_str(i18n("step2:正在提取音高&正在提取特征"))747 [748 get_info_str(_)749 for _ in extract_f0_feature(750 gpus16, np7, f0method8, if_f0_3, exp_dir1, version19, gpus_rmvpe751 )752 ]753 754 # step3a:训练模型755 yield get_info_str(i18n("step3a:正在训练模型"))756 click_train(757 exp_dir1,758 sr2,759 if_f0_3,760 spk_id5,761 save_epoch10,762 total_epoch11,763 batch_size12,764 if_save_latest13,765 pretrained_G14,766 pretrained_D15,767 gpus16,768 if_cache_gpu17,769 if_save_every_weights18,770 version19,771 )772 yield get_info_str(773 i18n("训练结束, 您可查看控制台训练日志或实验文件夹下的train.log")774 )775 776 # step3b:训练索引777 [get_info_str(_) for _ in train_index(exp_dir1, version19)]778 yield get_info_str(i18n("全流程结束!"))779 780 781# ckpt_path2.change(change_info_,[ckpt_path2],[sr__,if_f0__])782def change_info_(ckpt_path):783 if not os.path.exists(ckpt_path.replace(os.path.basename(ckpt_path), "train.log")):784 return {"__type__": "update"}, {"__type__": "update"}, {"__type__": "update"}785 try:786 with open(787 ckpt_path.replace(os.path.basename(ckpt_path), "train.log"), "r"788 ) as f:789 info = eval(f.read().strip("\n").split("\n")[0].split("\t")[-1])790 sr, f0 = info["sample_rate"], info["if_f0"]791 version = "v2" if ("version" in info and info["version"] == "v2") else "v1"792 return sr, str(f0), version793 except:794 traceback.print_exc()795 return {"__type__": "update"}, {"__type__": "update"}, {"__type__": "update"}796 797 798F0GPUVisible = config.dml == False799 800 801def change_f0_method(f0method8):802 if f0method8 == "rmvpe_gpu":803 visible = F0GPUVisible804 else:805 visible = False806 return {"visible": visible, "__type__": "update"}807 808 809with gr.Blocks(title="RVC WebUI") as app:810 gr.Markdown("## RVC WebUI")811 gr.Markdown(812 value=i18n(813 "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责. <br>如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录<b>LICENSE</b>."814 )815 )816 with gr.Tabs():817 with gr.TabItem(i18n("模型推理")):818 with gr.Row():819 sid0 = gr.Dropdown(label=i18n("推理音色"), choices=sorted(names))820 with gr.Column():821 refresh_button = gr.Button(822 i18n("刷新音色列表和索引路径"), variant="primary"823 )824 clean_button = gr.Button(i18n("卸载音色省显存"), variant="primary")825 spk_item = gr.Slider(826 minimum=0,827 maximum=2333,828 step=1,829 label=i18n("请选择说话人id"),830 value=0,831 visible=False,832 interactive=True,833 )834 clean_button.click(835 fn=clean, inputs=[], outputs=[sid0], api_name="infer_clean"836 )837 with gr.TabItem(i18n("单次推理")):838 with gr.Group():839 with gr.Row():840 with gr.Column():841 vc_transform0 = gr.Number(842 label=i18n("变调(整数, 半音数量, 升八度12降八度-12)"),843 value=0,844 )845 input_audio0 = gr.Textbox(846 label=i18n(847 "输入待处理音频文件路径(默认是正确格式示例)"848 ),849 placeholder="C:\\Users\\Desktop\\audio_example.wav",850 )851 file_index1 = gr.Textbox(852 label=i18n(853 "特征检索库文件路径,为空则使用下拉的选择结果"854 ),855 placeholder="C:\\Users\\Desktop\\model_example.index",856 interactive=True,857 )858 file_index2 = gr.Dropdown(859 label=i18n("自动检测index路径,下拉式选择(dropdown)"),860 choices=sorted(index_paths),861 interactive=True,862 )863 f0method0 = gr.Radio(864 label=i18n(865 "选择音高提取算法,输入歌声可用pm提速,harvest低音好但巨慢无比,crepe效果好但吃GPU,rmvpe效果最好且微吃GPU"866 ),867 choices=(868 ["pm", "harvest", "crepe", "rmvpe"]869 if config.dml == False870 else ["pm", "harvest", "rmvpe"]871 ),872 value="rmvpe",873 interactive=True,874 )875 876 with gr.Column():877 resample_sr0 = gr.Slider(878 minimum=0,879 maximum=48000,880 label=i18n("后处理重采样至最终采样率,0为不进行重采样"),881 value=0,882 step=1,883 interactive=True,884 )885 rms_mix_rate0 = gr.Slider(886 minimum=0,887 maximum=1,888 label=i18n(889 "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络"890 ),891 value=0.25,892 interactive=True,893 )894 protect0 = gr.Slider(895 minimum=0,896 maximum=0.5,897 label=i18n(898 "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果"899 ),900 value=0.33,901 step=0.01,902 interactive=True,903 )904 filter_radius0 = gr.Slider(905 minimum=0,906 maximum=7,907 label=i18n(908 ">=3则使用对harvest音高识别的结果使用中值滤波,数值为滤波半径,使用可以削弱哑音"909 ),910 value=3,911 step=1,912 interactive=True,913 )914 index_rate1 = gr.Slider(915 minimum=0,916 maximum=1,917 label=i18n("检索特征占比"),918 value=0.75,919 interactive=True,920 )921 f0_file = gr.File(922 label=i18n(923 "F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调"924 ),925 visible=False,926 )927 928 refresh_button.click(929 fn=change_choices,930 inputs=[],931 outputs=[sid0, file_index2],932 api_name="infer_refresh",933 )934 # file_big_npy1 = gr.Textbox(935 # label=i18n("特征文件路径"),936 # value="E:\\codes\py39\\vits_vc_gpu_train\\logs\\mi-test-1key\\total_fea.npy",937 # interactive=True,938 # )939 with gr.Group():940 with gr.Column():941 but0 = gr.Button(i18n("转换"), variant="primary")942 with gr.Row():943 vc_output1 = gr.Textbox(label=i18n("输出信息"))944 vc_output2 = gr.Audio(945 label=i18n("输出音频(右下角三个点,点了可以下载)")946 )947 948 but0.click(949 vc.vc_single,950 [951 spk_item,952 input_audio0,953 vc_transform0,954 f0_file,955 f0method0,956 file_index1,957 file_index2,958 # file_big_npy1,959 index_rate1,960 filter_radius0,961 resample_sr0,962 rms_mix_rate0,963 protect0,964 ],965 [vc_output1, vc_output2],966 api_name="infer_convert",967 )968 with gr.TabItem(i18n("批量推理")):969 gr.Markdown(970 value=i18n(971 "批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. "972 )973 )974 with gr.Row():975 with gr.Column():976 vc_transform1 = gr.Number(977 label=i18n("变调(整数, 半音数量, 升八度12降八度-12)"),978 value=0,979 )980 opt_input = gr.Textbox(981 label=i18n("指定输出文件夹"), value="opt"982 )983 file_index3 = gr.Textbox(984 label=i18n("特征检索库文件路径,为空则使用下拉的选择结果"),985 value="",986 interactive=True,987 )988 file_index4 = gr.Dropdown(989 label=i18n("自动检测index路径,下拉式选择(dropdown)"),990 choices=sorted(index_paths),991 interactive=True,992 )993 f0method1 = gr.Radio(994 label=i18n(995 "选择音高提取算法,输入歌声可用pm提速,harvest低音好但巨慢无比,crepe效果好但吃GPU,rmvpe效果最好且微吃GPU"996 ),997 choices=(998 ["pm", "harvest", "crepe", "rmvpe"]999 if config.dml == False1000 else ["pm", "harvest", "rmvpe"]1001 ),1002 value="rmvpe",1003 interactive=True,1004 )1005 format1 = gr.Radio(1006 label=i18n("导出文件格式"),1007 choices=["wav", "flac", "mp3", "m4a"],1008 value="wav",1009 interactive=True,1010 )1011 1012 refresh_button.click(1013 fn=lambda: change_choices()[1],1014 inputs=[],1015 outputs=file_index4,1016 api_name="infer_refresh_batch",1017 )1018 # file_big_npy2 = gr.Textbox(1019 # label=i18n("特征文件路径"),1020 # value="E:\\codes\\py39\\vits_vc_gpu_train\\logs\\mi-test-1key\\total_fea.npy",1021 # interactive=True,1022 # )1023 1024 with gr.Column():1025 resample_sr1 = gr.Slider(1026 minimum=0,1027 maximum=48000,1028 label=i18n("后处理重采样至最终采样率,0为不进行重采样"),1029 value=0,1030 step=1,1031 interactive=True,1032 )1033 rms_mix_rate1 = gr.Slider(1034 minimum=0,1035 maximum=1,1036 label=i18n(1037 "输入源音量包络替换输出音量包络融合比例,越靠近1越使用输出包络"1038 ),1039 value=1,1040 interactive=True,1041 )1042 protect1 = gr.Slider(1043 minimum=0,1044 maximum=0.5,1045 label=i18n(1046 "保护清辅音和呼吸声,防止电音撕裂等artifact,拉满0.5不开启,调低加大保护力度但可能降低索引效果"1047 ),1048 value=0.33,1049 step=0.01,1050 interactive=True,1051 )1052 filter_radius1 = gr.Slider(1053 minimum=0,1054 maximum=7,1055 label=i18n(1056 ">=3则使用对harvest音高识别的结果使用中值滤波,数值为滤波半径,使用可以削弱哑音"1057 ),1058 value=3,1059 step=1,1060 interactive=True,1061 )1062 index_rate2 = gr.Slider(1063 minimum=0,1064 maximum=1,1065 label=i18n("检索特征占比"),1066 value=1,1067 interactive=True,1068 )1069 with gr.Row():1070 dir_input = gr.Textbox(1071 label=i18n(1072 "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)"1073 ),1074 placeholder="C:\\Users\\Desktop\\input_vocal_dir",1075 )1076 inputs = gr.File(1077 file_count="multiple",1078 label=i18n("也可批量输入音频文件, 二选一, 优先读文件夹"),1079 )1080 1081 with gr.Row():1082 but1 = gr.Button(i18n("转换"), variant="primary")1083 vc_output3 = gr.Textbox(label=i18n("输出信息"))1084 1085 but1.click(1086 vc.vc_multi,1087 [1088 spk_item,1089 dir_input,1090 opt_input,1091 inputs,1092 vc_transform1,1093 f0method1,1094 file_index3,1095 file_index4,1096 # file_big_npy2,1097 index_rate2,1098 filter_radius1,1099 resample_sr1,1100 rms_mix_rate1,1101 protect1,1102 format1,1103 ],1104 [vc_output3],1105 api_name="infer_convert_batch",1106 )1107 sid0.change(1108 fn=vc.get_vc,1109 inputs=[sid0, protect0, protect1],1110 outputs=[spk_item, protect0, protect1, file_index2, file_index4],1111 api_name="infer_change_voice",1112 )1113 with gr.TabItem(i18n("伴奏人声分离&去混响&去回声")):1114 with gr.Group():1115 gr.Markdown(1116 value=i18n(1117 "人声伴奏分离批量处理, 使用UVR5模型。 <br>合格的文件夹路径格式举例: E:\\codes\\py39\\vits_vc_gpu\\白鹭霜华测试样例(去文件管理器地址栏拷就行了)。 <br>模型分为三类: <br>1、保留人声:不带和声的音频选这个,对主人声保留比HP5更好。内置HP2和HP3两个模型,HP3可能轻微漏伴奏但对主人声保留比HP2稍微好一丁点; <br>2、仅保留主人声:带和声的音频选这个,对主人声可能有削弱。内置HP5一个模型; <br> 3、去混响、去延迟模型(by FoxJoy):<br> (1)MDX-Net(onnx_dereverb):对于双通道混响是最好的选择,不能去除单通道混响;<br> (234)DeEcho:去除延迟效果。Aggressive比Normal去除得更彻底,DeReverb额外去除混响,可去除单声道混响,但是对高频重的板式混响去不干净。<br>去混响/去延迟,附:<br>1、DeEcho-DeReverb模型的耗时是另外2个DeEcho模型的接近2倍;<br>2、MDX-Net-Dereverb模型挺慢的;<br>3、个人推荐的最干净的配置是先MDX-Net再DeEcho-Aggressive。"1118 )1119 )1120 with gr.Row():1121 with gr.Column():1122 dir_wav_input = gr.Textbox(1123 label=i18n("输入待处理音频文件夹路径"),1124 placeholder="C:\\Users\\Desktop\\todo-songs",1125 )1126 wav_inputs = gr.File(1127 file_count="multiple",1128 label=i18n("也可批量输入音频文件, 二选一, 优先读文件夹"),1129 )1130 with gr.Column():1131 model_choose = gr.Dropdown(1132 label=i18n("模型"), choices=uvr5_names1133 )1134 agg = gr.Slider(1135 minimum=0,1136 maximum=20,1137 step=1,1138 label="人声提取激进程度",1139 value=10,1140 interactive=True,1141 visible=False, # 先不开放调整1142 )1143 opt_vocal_root = gr.Textbox(1144 label=i18n("指定输出主人声文件夹"), value="opt"1145 )1146 opt_ins_root = gr.Textbox(1147 label=i18n("指定输出非主人声文件夹"), value="opt"1148 )1149 format0 = gr.Radio(1150 label=i18n("导出文件格式"),1151 choices=["wav", "flac", "mp3", "m4a"],1152 value="flac",1153 interactive=True,1154 )1155 but2 = gr.Button(i18n("转换"), variant="primary")1156 vc_output4 = gr.Textbox(label=i18n("输出信息"))1157 but2.click(1158 uvr,1159 [1160 model_choose,1161 dir_wav_input,1162 opt_vocal_root,1163 wav_inputs,1164 opt_ins_root,1165 agg,1166 format0,1167 ],1168 [vc_output4],1169 api_name="uvr_convert",1170 )1171 with gr.TabItem(i18n("训练")):1172 gr.Markdown(1173 value=i18n(1174 "step1: 填写实验配置. 实验数据放在logs下, 每个实验一个文件夹, 需手工输入实验名路径, 内含实验配置, 日志, 训练得到的模型文件. "1175 )1176 )1177 with gr.Row():1178 exp_dir1 = gr.Textbox(label=i18n("输入实验名"), value="mi-test")1179 sr2 = gr.Radio(1180 label=i18n("目标采样率"),1181 choices=["40k", "48k"],1182 value="40k",1183 interactive=True,1184 )1185 if_f0_3 = gr.Radio(1186 label=i18n("模型是否带音高指导(唱歌一定要, 语音可以不要)"),1187 choices=[True, False],1188 value=True,1189 interactive=True,1190 )1191 version19 = gr.Radio(1192 label=i18n("版本"),1193 choices=["v1", "v2"],1194 value="v2",1195 interactive=True,1196 visible=True,1197 )1198 np7 = gr.Slider(1199 minimum=0,1200 maximum=config.n_cpu,