ChazzyG/Retrieval-based-Voice-Conversion-WebUI
0
1from multiprocessing import cpu_count2import threading, pdb, librosa3from time import sleep4from subprocess import Popen5from time import sleep6import torch, os, traceback, sys, warnings, shutil, numpy as np7import faiss8from random import shuffle9 10now_dir = os.getcwd()11sys.path.append(now_dir)12tmp = os.path.join(now_dir, "TEMP")13shutil.rmtree(tmp, ignore_errors=True)14os.makedirs(tmp, exist_ok=True)15os.makedirs(os.path.join(now_dir, "logs"), exist_ok=True)16os.makedirs(os.path.join(now_dir, "weights"), exist_ok=True)17os.environ["TEMP"] = tmp18warnings.filterwarnings("ignore")19torch.manual_seed(114514)20from i18n import I18nAuto21import ffmpeg22 23i18n = I18nAuto()24# 判断是否有能用来训练和加速推理的N卡25ncpu = cpu_count()26ngpu = torch.cuda.device_count()27gpu_infos = []28mem = []29if (not torch.cuda.is_available()) or ngpu == 0:30 if_gpu_ok = False31else:32 if_gpu_ok = False33 for i in range(ngpu):34 gpu_name = torch.cuda.get_device_name(i)35 if (36 "10" in gpu_name37 or "16" in gpu_name38 or "20" in gpu_name39 or "30" in gpu_name40 or "40" in gpu_name41 or "A2" in gpu_name.upper()42 or "A3" in gpu_name.upper()43 or "A4" in gpu_name.upper()44 or "P4" in gpu_name.upper()45 or "A50" in gpu_name.upper()46 or "70" in gpu_name47 or "80" in gpu_name48 or "90" in gpu_name49 or "M4" in gpu_name.upper()50 or "T4" in gpu_name.upper()51 or "TITAN" in gpu_name.upper()52 ): # A10#A100#V100#A40#P40#M40#K80#A450053 if_gpu_ok = True # 至少有一张能用的N卡54 gpu_infos.append("%s\t%s" % (i, gpu_name))55 mem.append(56 int(57 torch.cuda.get_device_properties(i).total_memory58 / 102459 / 102460 / 102461 + 0.462 )63 )64if if_gpu_ok == True and len(gpu_infos) > 0:65 gpu_info = "\n".join(gpu_infos)66 default_batch_size = min(mem) // 267else:68 gpu_info = i18n("很遗憾您这没有能用的显卡来支持您训练")69 default_batch_size = 170gpus = "-".join([i[0] for i in gpu_infos])71from infer_pack.models import SynthesizerTrnMs256NSFsid, SynthesizerTrnMs256NSFsid_nono72from scipy.io import wavfile73from fairseq import checkpoint_utils74import gradio as gr75import logging76from vc_infer_pipeline import VC77from config import Config78from infer_uvr5 import _audio_pre_79from my_utils import load_audio80from train.process_ckpt import show_info, change_info, merge, extract_small_model81 82config = Config()83# from trainset_preprocess_pipeline import PreProcess84logging.getLogger("numba").setLevel(logging.WARNING)85 86 87class ToolButton(gr.Button, gr.components.FormComponent):88 """Small button with single emoji as text, fits inside gradio forms"""89 90 def __init__(self, **kwargs):91 super().__init__(variant="tool", **kwargs)92 93 def get_block_name(self):94 return "button"95 96 97hubert_model = None98 99 100def load_hubert():101 global hubert_model102 models, _, _ = checkpoint_utils.load_model_ensemble_and_task(103 ["hubert_base.pt"],104 suffix="",105 )106 hubert_model = models[0]107 hubert_model = hubert_model.to(config.device)108 if config.is_half:109 hubert_model = hubert_model.half()110 else:111 hubert_model = hubert_model.float()112 hubert_model.eval()113 114 115weight_root = "weights"116weight_uvr5_root = "uvr5_weights"117names = []118for name in os.listdir(weight_root):119 if name.endswith(".pth"):120 names.append(name)121uvr5_names = []122for name in os.listdir(weight_uvr5_root):123 if name.endswith(".pth"):124 uvr5_names.append(name.replace(".pth", ""))125 126 127def vc_single(128 sid,129 input_audio,130 f0_up_key,131 f0_file,132 f0_method,133 file_index,134 # file_big_npy,135 index_rate,136): # spk_item, input_audio0, vc_transform0,f0_file,f0method0137 global tgt_sr, net_g, vc, hubert_model138 if input_audio is None:139 return "You need to upload an audio", None140 f0_up_key = int(f0_up_key)141 try:142 audio = load_audio(input_audio, 16000)143 times = [0, 0, 0]144 if hubert_model == None:145 load_hubert()146 if_f0 = cpt.get("f0", 1)147 file_index = (148 file_index.strip(" ")149 .strip('"')150 .strip("\n")151 .strip('"')152 .strip(" ")153 .replace("trained", "added")154 ) # 防止小白写错,自动帮他替换掉155 # file_big_npy = (156 # file_big_npy.strip(" ").strip('"').strip("\n").strip('"').strip(" ")157 # )158 audio_opt = vc.pipeline(159 hubert_model,160 net_g,161 sid,162 audio,163 times,164 f0_up_key,165 f0_method,166 file_index,167 # file_big_npy,168 index_rate,169 if_f0,170 f0_file=f0_file,171 )172 print(173 "npy: ", times[0], "s, f0: ", times[1], "s, infer: ", times[2], "s", sep=""174 )175 return "Success", (tgt_sr, audio_opt)176 except:177 info = traceback.format_exc()178 print(info)179 return info, (None, None)180 181 182def vc_multi(183 sid,184 dir_path,185 opt_root,186 paths,187 f0_up_key,188 f0_method,189 file_index,190 # file_big_npy,191 index_rate,192):193 try:194 dir_path = (195 dir_path.strip(" ").strip('"').strip("\n").strip('"').strip(" ")196 ) # 防止小白拷路径头尾带了空格和"和回车197 opt_root = opt_root.strip(" ").strip('"').strip("\n").strip('"').strip(" ")198 os.makedirs(opt_root, exist_ok=True)199 try:200 if dir_path != "":201 paths = [os.path.join(dir_path, name) for name in os.listdir(dir_path)]202 else:203 paths = [path.name for path in paths]204 except:205 traceback.print_exc()206 paths = [path.name for path in paths]207 infos = []208 file_index = (209 file_index.strip(" ")210 .strip('"')211 .strip("\n")212 .strip('"')213 .strip(" ")214 .replace("trained", "added")215 ) # 防止小白写错,自动帮他替换掉216 for path in paths:217 info, opt = vc_single(218 sid,219 path,220 f0_up_key,221 None,222 f0_method,223 file_index,224 # file_big_npy,225 index_rate,226 )227 if info == "Success":228 try:229 tgt_sr, audio_opt = opt230 wavfile.write(231 "%s/%s" % (opt_root, os.path.basename(path)), tgt_sr, audio_opt232 )233 except:234 info = traceback.format_exc()235 infos.append("%s->%s" % (os.path.basename(path), info))236 yield "\n".join(infos)237 yield "\n".join(infos)238 except:239 yield traceback.format_exc()240 241 242def uvr(model_name, inp_root, save_root_vocal, paths, save_root_ins, agg):243 infos = []244 try:245 inp_root = inp_root.strip(" ").strip('"').strip("\n").strip('"').strip(" ")246 save_root_vocal = (247 save_root_vocal.strip(" ").strip('"').strip("\n").strip('"').strip(" ")248 )249 save_root_ins = (250 save_root_ins.strip(" ").strip('"').strip("\n").strip('"').strip(" ")251 )252 pre_fun = _audio_pre_(253 agg=int(agg),254 model_path=os.path.join(weight_uvr5_root, model_name + ".pth"),255 device=config.device,256 is_half=config.is_half,257 )258 if inp_root != "":259 paths = [os.path.join(inp_root, name) for name in os.listdir(inp_root)]260 else:261 paths = [path.name for path in paths]262 for path in paths:263 inp_path = os.path.join(inp_root, path)264 need_reformat = 1265 done = 0266 try:267 info = ffmpeg.probe(inp_path, cmd="ffprobe")268 if (269 info["streams"][0]["channels"] == 2270 and info["streams"][0]["sample_rate"] == "44100"271 ):272 need_reformat = 0273 pre_fun._path_audio_(inp_path, save_root_ins, save_root_vocal)274 done = 1275 except:276 need_reformat = 1277 traceback.print_exc()278 if need_reformat == 1:279 tmp_path = "%s/%s.reformatted.wav" % (tmp, os.path.basename(inp_path))280 os.system(281 "ffmpeg -i %s -vn -acodec pcm_s16le -ac 2 -ar 44100 %s -y"282 % (inp_path, tmp_path)283 )284 inp_path = tmp_path285 try:286 if done == 0:287 pre_fun._path_audio_(inp_path, save_root_ins, save_root_vocal)288 infos.append("%s->Success" % (os.path.basename(inp_path)))289 yield "\n".join(infos)290 except:291 infos.append(292 "%s->%s" % (os.path.basename(inp_path), traceback.format_exc())293 )294 yield "\n".join(infos)295 except:296 infos.append(traceback.format_exc())297 yield "\n".join(infos)298 finally:299 try:300 del pre_fun.model301 del pre_fun302 except:303 traceback.print_exc()304 print("clean_empty_cache")305 if torch.cuda.is_available():306 torch.cuda.empty_cache()307 yield "\n".join(infos)308 309 310# 一个选项卡全局只能有一个音色311def get_vc(sid):312 global n_spk, tgt_sr, net_g, vc, cpt313 if sid == []:314 global hubert_model315 if hubert_model != None: # 考虑到轮询, 需要加个判断看是否 sid 是由有模型切换到无模型的316 print("clean_empty_cache")317 del net_g, n_spk, vc, hubert_model, tgt_sr # ,cpt318 hubert_model = net_g = n_spk = vc = hubert_model = tgt_sr = None319 if torch.cuda.is_available():320 torch.cuda.empty_cache()321 ###楼下不这么折腾清理不干净322 if_f0 = cpt.get("f0", 1)323 if if_f0 == 1:324 net_g = SynthesizerTrnMs256NSFsid(325 *cpt["config"], is_half=config.is_half326 )327 else:328 net_g = SynthesizerTrnMs256NSFsid_nono(*cpt["config"])329 del net_g, cpt330 if torch.cuda.is_available():331 torch.cuda.empty_cache()332 cpt = None333 return {"visible": False, "__type__": "update"}334 person = "%s/%s" % (weight_root, sid)335 print("loading %s" % person)336 cpt = torch.load(person, map_location="cpu")337 tgt_sr = cpt["config"][-1]338 cpt["config"][-3] = cpt["weight"]["emb_g.weight"].shape[0] # n_spk339 if_f0 = cpt.get("f0", 1)340 if if_f0 == 1:341 net_g = SynthesizerTrnMs256NSFsid(*cpt["config"], is_half=config.is_half)342 else:343 net_g = SynthesizerTrnMs256NSFsid_nono(*cpt["config"])344 del net_g.enc_q345 print(net_g.load_state_dict(cpt["weight"], strict=False)) # 不加这一行清不干净, 真奇葩346 net_g.eval().to(config.device)347 if config.is_half:348 net_g = net_g.half()349 else:350 net_g = net_g.float()351 vc = VC(tgt_sr, config)352 n_spk = cpt["config"][-3]353 return {"visible": True, "maximum": n_spk, "__type__": "update"}354 355 356def change_choices():357 names = []358 for name in os.listdir(weight_root):359 if name.endswith(".pth"):360 names.append(name)361 return {"choices": sorted(names), "__type__": "update"}362 363 364def clean():365 return {"value": "", "__type__": "update"}366 367 368def change_f0(if_f0_3, sr2): # np7, f0method8,pretrained_G14,pretrained_D15369 if if_f0_3 == i18n("是"):370 return (371 {"visible": True, "__type__": "update"},372 {"visible": True, "__type__": "update"},373 "pretrained/f0G%s.pth" % sr2,374 "pretrained/f0D%s.pth" % sr2,375 )376 return (377 {"visible": False, "__type__": "update"},378 {"visible": False, "__type__": "update"},379 "pretrained/G%s.pth" % sr2,380 "pretrained/D%s.pth" % sr2,381 )382 383 384sr_dict = {385 "32k": 32000,386 "40k": 40000,387 "48k": 48000,388}389 390 391def if_done(done, p):392 while 1:393 if p.poll() == None:394 sleep(0.5)395 else:396 break397 done[0] = True398 399 400def if_done_multi(done, ps):401 while 1:402 # poll==None代表进程未结束403 # 只要有一个进程未结束都不停404 flag = 1405 for p in ps:406 if p.poll() == None:407 flag = 0408 sleep(0.5)409 break410 if flag == 1:411 break412 done[0] = True413 414 415def preprocess_dataset(trainset_dir, exp_dir, sr, n_p=ncpu):416 sr = sr_dict[sr]417 os.makedirs("%s/logs/%s" % (now_dir, exp_dir), exist_ok=True)418 f = open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir), "w")419 f.close()420 cmd = (421 config.python_cmd422 + " trainset_preprocess_pipeline_print.py %s %s %s %s/logs/%s "423 % (trainset_dir, sr, n_p, now_dir, exp_dir)424 + str(config.noparallel)425 )426 print(cmd)427 p = Popen(cmd, shell=True) # , stdin=PIPE, stdout=PIPE,stderr=PIPE,cwd=now_dir428 ###煞笔gr, popen read都非得全跑完了再一次性读取, 不用gr就正常读一句输出一句;只能额外弄出一个文本流定时读429 done = [False]430 threading.Thread(431 target=if_done,432 args=(433 done,434 p,435 ),436 ).start()437 while 1:438 with open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir), "r") as f:439 yield (f.read())440 sleep(1)441 if done[0] == True:442 break443 with open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir), "r") as f:444 log = f.read()445 print(log)446 yield log447 448 449# but2.click(extract_f0,[gpus6,np7,f0method8,if_f0_3,trainset_dir4],[info2])450def extract_f0_feature(gpus, n_p, f0method, if_f0, exp_dir):451 gpus = gpus.split("-")452 os.makedirs("%s/logs/%s" % (now_dir, exp_dir), exist_ok=True)453 f = open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "w")454 f.close()455 if if_f0 == i18n("是"):456 cmd = config.python_cmd + " extract_f0_print.py %s/logs/%s %s %s" % (457 now_dir,458 exp_dir,459 n_p,460 f0method,461 )462 print(cmd)463 p = Popen(cmd, shell=True, cwd=now_dir) # , stdin=PIPE, stdout=PIPE,stderr=PIPE464 ###煞笔gr, popen read都非得全跑完了再一次性读取, 不用gr就正常读一句输出一句;只能额外弄出一个文本流定时读465 done = [False]466 threading.Thread(467 target=if_done,468 args=(469 done,470 p,471 ),472 ).start()473 while 1:474 with open(475 "%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r"476 ) as f:477 yield (f.read())478 sleep(1)479 if done[0] == True:480 break481 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r") as f:482 log = f.read()483 print(log)484 yield log485 ####对不同part分别开多进程486 """487 n_part=int(sys.argv[1])488 i_part=int(sys.argv[2])489 i_gpu=sys.argv[3]490 exp_dir=sys.argv[4]491 os.environ["CUDA_VISIBLE_DEVICES"]=str(i_gpu)492 """493 leng = len(gpus)494 ps = []495 for idx, n_g in enumerate(gpus):496 cmd = config.python_cmd + " extract_feature_print.py %s %s %s %s %s/logs/%s" % (497 config.device,498 leng,499 idx,500 n_g,501 now_dir,502 exp_dir,503 )504 print(cmd)505 p = Popen(506 cmd, shell=True, cwd=now_dir507 ) # , shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=now_dir508 ps.append(p)509 ###煞笔gr, popen read都非得全跑完了再一次性读取, 不用gr就正常读一句输出一句;只能额外弄出一个文本流定时读510 done = [False]511 threading.Thread(512 target=if_done_multi,513 args=(514 done,515 ps,516 ),517 ).start()518 while 1:519 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r") as f:520 yield (f.read())521 sleep(1)522 if done[0] == True:523 break524 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir), "r") as f:525 log = f.read()526 print(log)527 yield log528 529 530def change_sr2(sr2, if_f0_3):531 if if_f0_3 == i18n("是"):532 return "pretrained/f0G%s.pth" % sr2, "pretrained/f0D%s.pth" % sr2533 else:534 return "pretrained/G%s.pth" % sr2, "pretrained/D%s.pth" % sr2535 536 537# but3.click(click_train,[exp_dir1,sr2,if_f0_3,save_epoch10,total_epoch11,batch_size12,if_save_latest13,pretrained_G14,pretrained_D15,gpus16])538def click_train(539 exp_dir1,540 sr2,541 if_f0_3,542 spk_id5,543 save_epoch10,544 total_epoch11,545 batch_size12,546 if_save_latest13,547 pretrained_G14,548 pretrained_D15,549 gpus16,550 if_cache_gpu17,551):552 # 生成filelist553 exp_dir = "%s/logs/%s" % (now_dir, exp_dir1)554 os.makedirs(exp_dir, exist_ok=True)555 gt_wavs_dir = "%s/0_gt_wavs" % (exp_dir)556 co256_dir = "%s/3_feature256" % (exp_dir)557 if if_f0_3 == i18n("是"):558 f0_dir = "%s/2a_f0" % (exp_dir)559 f0nsf_dir = "%s/2b-f0nsf" % (exp_dir)560 names = (561 set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)])562 & set([name.split(".")[0] for name in os.listdir(co256_dir)])563 & set([name.split(".")[0] for name in os.listdir(f0_dir)])564 & set([name.split(".")[0] for name in os.listdir(f0nsf_dir)])565 )566 else:567 names = set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)]) & set(568 [name.split(".")[0] for name in os.listdir(co256_dir)]569 )570 opt = []571 for name in names:572 if if_f0_3 == i18n("是"):573 opt.append(574 "%s/%s.wav|%s/%s.npy|%s/%s.wav.npy|%s/%s.wav.npy|%s"575 % (576 gt_wavs_dir.replace("\\", "\\\\"),577 name,578 co256_dir.replace("\\", "\\\\"),579 name,580 f0_dir.replace("\\", "\\\\"),581 name,582 f0nsf_dir.replace("\\", "\\\\"),583 name,584 spk_id5,585 )586 )587 else:588 opt.append(589 "%s/%s.wav|%s/%s.npy|%s"590 % (591 gt_wavs_dir.replace("\\", "\\\\"),592 name,593 co256_dir.replace("\\", "\\\\"),594 name,595 spk_id5,596 )597 )598 if if_f0_3 == i18n("是"):599 for _ in range(2):600 opt.append(601 "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature256/mute.npy|%s/logs/mute/2a_f0/mute.wav.npy|%s/logs/mute/2b-f0nsf/mute.wav.npy|%s"602 % (now_dir, sr2, now_dir, now_dir, now_dir, spk_id5)603 )604 else:605 for _ in range(2):606 opt.append(607 "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature256/mute.npy|%s"608 % (now_dir, sr2, now_dir, spk_id5)609 )610 shuffle(opt)611 with open("%s/filelist.txt" % exp_dir, "w") as f:612 f.write("\n".join(opt))613 print("write filelist done")614 # 生成config#无需生成config615 # 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"616 print("use gpus:", gpus16)617 if gpus16:618 cmd = (619 config.python_cmd620 + " train_nsf_sim_cache_sid_load_pretrain.py -e %s -sr %s -f0 %s -bs %s -g %s -te %s -se %s -pg %s -pd %s -l %s -c %s"621 % (622 exp_dir1,623 sr2,624 1 if if_f0_3 == i18n("是") else 0,625 batch_size12,626 gpus16,627 total_epoch11,628 save_epoch10,629 pretrained_G14,630 pretrained_D15,631 1 if if_save_latest13 == i18n("是") else 0,632 1 if if_cache_gpu17 == i18n("是") else 0,633 )634 )635 else:636 cmd = (637 config.python_cmd638 + " train_nsf_sim_cache_sid_load_pretrain.py -e %s -sr %s -f0 %s -bs %s -te %s -se %s -pg %s -pd %s -l %s -c %s"639 % (640 exp_dir1,641 sr2,642 1 if if_f0_3 == i18n("是") else 0,643 batch_size12,644 total_epoch11,645 save_epoch10,646 pretrained_G14,647 pretrained_D15,648 1 if if_save_latest13 == i18n("是") else 0,649 1 if if_cache_gpu17 == i18n("是") else 0,650 )651 )652 print(cmd)653 p = Popen(cmd, shell=True, cwd=now_dir)654 p.wait()655 return "训练结束, 您可查看控制台训练日志或实验文件夹下的train.log"656 657 658# but4.click(train_index, [exp_dir1], info3)659def train_index(exp_dir1):660 exp_dir = "%s/logs/%s" % (now_dir, exp_dir1)661 os.makedirs(exp_dir, exist_ok=True)662 feature_dir = "%s/3_feature256" % (exp_dir)663 if os.path.exists(feature_dir) == False:664 return "请先进行特征提取!"665 listdir_res = list(os.listdir(feature_dir))666 if len(listdir_res) == 0:667 return "请先进行特征提取!"668 npys = []669 for name in sorted(listdir_res):670 phone = np.load("%s/%s" % (feature_dir, name))671 npys.append(phone)672 big_npy = np.concatenate(npys, 0)673 big_npy_idx = np.arange(big_npy.shape[0])674 np.random.shuffle(big_npy_idx)675 big_npy = big_npy[big_npy_idx]676 np.save("%s/total_fea.npy" % exp_dir, big_npy)677 # n_ivf = big_npy.shape[0] // 39678 n_ivf = min(int(16 * np.sqrt(big_npy.shape[0])), big_npy.shape[0] // 39)679 infos = []680 infos.append("%s,%s" % (big_npy.shape, n_ivf))681 yield "\n".join(infos)682 index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)683 # index = faiss.index_factory(256, "IVF%s,PQ128x4fs,RFlat"%n_ivf)684 infos.append("training")685 yield "\n".join(infos)686 index_ivf = faiss.extract_index_ivf(index) #687 # index_ivf.nprobe = int(np.power(n_ivf,0.3))688 index_ivf.nprobe = 1689 index.train(big_npy)690 faiss.write_index(691 index,692 "%s/trained_IVF%s_Flat_nprobe_%s.index" % (exp_dir, n_ivf, index_ivf.nprobe),693 )694 # faiss.write_index(index, '%s/trained_IVF%s_Flat_FastScan.index'%(exp_dir,n_ivf))695 infos.append("adding")696 yield "\n".join(infos)697 batch_size_add = 8192698 for i in range(0, big_npy.shape[0], batch_size_add):699 index.add(big_npy[i : i + batch_size_add])700 faiss.write_index(701 index,702 "%s/added_IVF%s_Flat_nprobe_%s.index" % (exp_dir, n_ivf, index_ivf.nprobe),703 )704 infos.append("成功构建索引,added_IVF%s_Flat_nprobe_%s.index" % (n_ivf, index_ivf.nprobe))705 # faiss.write_index(index, '%s/added_IVF%s_Flat_FastScan.index'%(exp_dir,n_ivf))706 # infos.append("成功构建索引,added_IVF%s_Flat_FastScan.index"%(n_ivf))707 yield "\n".join(infos)708 709 710# 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)711def train1key(712 exp_dir1,713 sr2,714 if_f0_3,715 trainset_dir4,716 spk_id5,717 gpus6,718 np7,719 f0method8,720 save_epoch10,721 total_epoch11,722 batch_size12,723 if_save_latest13,724 pretrained_G14,725 pretrained_D15,726 gpus16,727 if_cache_gpu17,728):729 infos = []730 731 def get_info_str(strr):732 infos.append(strr)733 return "\n".join(infos)734 735 os.makedirs("%s/logs/%s" % (now_dir, exp_dir1), exist_ok=True)736 #########step1:处理数据737 open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir1), "w").close()738 cmd = (739 config.python_cmd740 + " trainset_preprocess_pipeline_print.py %s %s %s %s/logs/%s "741 % (trainset_dir4, sr_dict[sr2], ncpu, now_dir, exp_dir1)742 + str(config.noparallel)743 )744 yield get_info_str(i18n("step1:正在处理数据"))745 yield get_info_str(cmd)746 p = Popen(cmd, shell=True)747 p.wait()748 with open("%s/logs/%s/preprocess.log" % (now_dir, exp_dir1), "r") as f:749 print(f.read())750 #########step2a:提取音高751 open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir1), "w")752 if if_f0_3 == i18n("是"):753 yield get_info_str("step2a:正在提取音高")754 cmd = config.python_cmd + " extract_f0_print.py %s/logs/%s %s %s" % (755 now_dir,756 exp_dir1,757 np7,758 f0method8,759 )760 yield get_info_str(cmd)761 p = Popen(cmd, shell=True, cwd=now_dir)762 p.wait()763 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir1), "r") as f:764 print(f.read())765 else:766 yield get_info_str(i18n("step2a:无需提取音高"))767 #######step2b:提取特征768 yield get_info_str(i18n("step2b:正在提取特征"))769 gpus = gpus16.split("-")770 leng = len(gpus)771 ps = []772 for idx, n_g in enumerate(gpus):773 cmd = config.python_cmd + " extract_feature_print.py %s %s %s %s %s/logs/%s" % (774 config.device,775 leng,776 idx,777 n_g,778 now_dir,779 exp_dir1,780 )781 yield get_info_str(cmd)782 p = Popen(783 cmd, shell=True, cwd=now_dir784 ) # , shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=now_dir785 ps.append(p)786 for p in ps:787 p.wait()788 with open("%s/logs/%s/extract_f0_feature.log" % (now_dir, exp_dir1), "r") as f:789 print(f.read())790 #######step3a:训练模型791 yield get_info_str(i18n("step3a:正在训练模型"))792 # 生成filelist793 exp_dir = "%s/logs/%s" % (now_dir, exp_dir1)794 gt_wavs_dir = "%s/0_gt_wavs" % (exp_dir)795 co256_dir = "%s/3_feature256" % (exp_dir)796 if if_f0_3 == i18n("是"):797 f0_dir = "%s/2a_f0" % (exp_dir)798 f0nsf_dir = "%s/2b-f0nsf" % (exp_dir)799 names = (800 set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)])801 & set([name.split(".")[0] for name in os.listdir(co256_dir)])802 & set([name.split(".")[0] for name in os.listdir(f0_dir)])803 & set([name.split(".")[0] for name in os.listdir(f0nsf_dir)])804 )805 else:806 names = set([name.split(".")[0] for name in os.listdir(gt_wavs_dir)]) & set(807 [name.split(".")[0] for name in os.listdir(co256_dir)]808 )809 opt = []810 for name in names:811 if if_f0_3 == i18n("是"):812 opt.append(813 "%s/%s.wav|%s/%s.npy|%s/%s.wav.npy|%s/%s.wav.npy|%s"814 % (815 gt_wavs_dir.replace("\\", "\\\\"),816 name,817 co256_dir.replace("\\", "\\\\"),818 name,819 f0_dir.replace("\\", "\\\\"),820 name,821 f0nsf_dir.replace("\\", "\\\\"),822 name,823 spk_id5,824 )825 )826 else:827 opt.append(828 "%s/%s.wav|%s/%s.npy|%s"829 % (830 gt_wavs_dir.replace("\\", "\\\\"),831 name,832 co256_dir.replace("\\", "\\\\"),833 name,834 spk_id5,835 )836 )837 if if_f0_3 == i18n("是"):838 for _ in range(2):839 opt.append(840 "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature256/mute.npy|%s/logs/mute/2a_f0/mute.wav.npy|%s/logs/mute/2b-f0nsf/mute.wav.npy|%s"841 % (now_dir, sr2, now_dir, now_dir, now_dir, spk_id5)842 )843 else:844 for _ in range(2):845 opt.append(846 "%s/logs/mute/0_gt_wavs/mute%s.wav|%s/logs/mute/3_feature256/mute.npy|%s"847 % (now_dir, sr2, now_dir, spk_id5)848 )849 shuffle(opt)850 with open("%s/filelist.txt" % exp_dir, "w") as f:851 f.write("\n".join(opt))852 yield get_info_str("write filelist done")853 if gpus16:854 cmd = (855 config.python_cmd856 + " train_nsf_sim_cache_sid_load_pretrain.py -e %s -sr %s -f0 %s -bs %s -g %s -te %s -se %s -pg %s -pd %s -l %s -c %s"857 % (858 exp_dir1,859 sr2,860 1 if if_f0_3 == i18n("是") else 0,861 batch_size12,862 gpus16,863 total_epoch11,864 save_epoch10,865 pretrained_G14,866 pretrained_D15,867 1 if if_save_latest13 == i18n("是") else 0,868 1 if if_cache_gpu17 == i18n("是") else 0,869 )870 )871 else:872 cmd = (873 config.python_cmd874 + " train_nsf_sim_cache_sid_load_pretrain.py -e %s -sr %s -f0 %s -bs %s -te %s -se %s -pg %s -pd %s -l %s -c %s"875 % (876 exp_dir1,877 sr2,878 1 if if_f0_3 == i18n("是") else 0,879 batch_size12,880 total_epoch11,881 save_epoch10,882 pretrained_G14,883 pretrained_D15,884 1 if if_save_latest13 == i18n("是") else 0,885 1 if if_cache_gpu17 == i18n("是") else 0,886 )887 )888 yield get_info_str(cmd)889 p = Popen(cmd, shell=True, cwd=now_dir)890 p.wait()891 yield get_info_str(i18n("训练结束, 您可查看控制台训练日志或实验文件夹下的train.log"))892 #######step3b:训练索引893 feature_dir = "%s/3_feature256" % (exp_dir)894 npys = []895 listdir_res = list(os.listdir(feature_dir))896 for name in sorted(listdir_res):897 phone = np.load("%s/%s" % (feature_dir, name))898 npys.append(phone)899 big_npy = np.concatenate(npys, 0)900 big_npy_idx = np.arange(big_npy.shape[0])901 np.random.shuffle(big_npy_idx)902 big_npy = big_npy[big_npy_idx]903 np.save("%s/total_fea.npy" % exp_dir, big_npy)904 # n_ivf = big_npy.shape[0] // 39905 n_ivf = min(int(16 * np.sqrt(big_npy.shape[0])), big_npy.shape[0] // 39)906 yield get_info_str("%s,%s" % (big_npy.shape, n_ivf))907 index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)908 yield get_info_str("training index")909 index_ivf = faiss.extract_index_ivf(index) #910 # index_ivf.nprobe = int(np.power(n_ivf,0.3))911 index_ivf.nprobe = 1912 index.train(big_npy)913 faiss.write_index(914 index,915 "%s/trained_IVF%s_Flat_nprobe_%s.index" % (exp_dir, n_ivf, index_ivf.nprobe),916 )917 yield get_info_str("adding index")918 batch_size_add = 8192919 for i in range(0, big_npy.shape[0], batch_size_add):920 index.add(big_npy[i : i + batch_size_add])921 faiss.write_index(922 index,923 "%s/added_IVF%s_Flat_nprobe_%s.index" % (exp_dir, n_ivf, index_ivf.nprobe),924 )925 yield get_info_str(926 "成功构建索引, added_IVF%s_Flat_nprobe_%s.index" % (n_ivf, index_ivf.nprobe)927 )928 yield get_info_str(i18n("全流程结束!"))929 930 931# ckpt_path2.change(change_info_,[ckpt_path2],[sr__,if_f0__])932def change_info_(ckpt_path):933 if (934 os.path.exists(ckpt_path.replace(os.path.basename(ckpt_path), "train.log"))935 == False936 ):937 return {"__type__": "update"}, {"__type__": "update"}938 try:939 with open(940 ckpt_path.replace(os.path.basename(ckpt_path), "train.log"), "r"941 ) as f:942 info = eval(f.read().strip("\n").split("\n")[0].split("\t")[-1])943 sr, f0 = info["sample_rate"], info["if_f0"]944 return sr, str(f0)945 except:946 traceback.print_exc()947 return {"__type__": "update"}, {"__type__": "update"}948 949 950from infer_pack.models_onnx_moess import SynthesizerTrnMs256NSFsidM951from infer_pack.models_onnx import SynthesizerTrnMs256NSFsidO952 953 954def export_onnx(ModelPath, ExportedPath, MoeVS=True):955 hidden_channels = 256 # hidden_channels,为768Vec做准备956 cpt = torch.load(ModelPath, map_location="cpu")957 cpt["config"][-3] = cpt["weight"]["emb_g.weight"].shape[0] # n_spk958 print(*cpt["config"])959 960 test_phone = torch.rand(1, 200, hidden_channels) # hidden unit961 test_phone_lengths = torch.tensor([200]).long() # hidden unit 长度(貌似没啥用)962 test_pitch = torch.randint(size=(1, 200), low=5, high=255) # 基频(单位赫兹)963 test_pitchf = torch.rand(1, 200) # nsf基频964 test_ds = torch.LongTensor([0]) # 说话人ID965 test_rnd = torch.rand(1, 192, 200) # 噪声(加入随机因子)966 967 device = "cpu" # 导出时设备(不影响使用模型)968 969 if MoeVS:970 net_g = SynthesizerTrnMs256NSFsidM(971 *cpt["config"], is_half=False972 ) # fp32导出(C++要支持fp16必须手动将内存重新排列所以暂时不用fp16)973 net_g.load_state_dict(cpt["weight"], strict=False)974 input_names = ["phone", "phone_lengths", "pitch", "pitchf", "ds", "rnd"]975 output_names = [976 "audio",977 ]978 torch.onnx.export(979 net_g,980 (981 test_phone.to(device),982 test_phone_lengths.to(device),983 test_pitch.to(device),984 test_pitchf.to(device),985 test_ds.to(device),986 test_rnd.to(device),987 ),988 ExportedPath,989 dynamic_axes={990 "phone": [1],991 "pitch": [1],992 "pitchf": [1],993 "rnd": [2],994 },995 do_constant_folding=False,996 opset_version=16,997 verbose=False,998 input_names=input_names,999 output_names=output_names,1000 )1001 else:1002 net_g = SynthesizerTrnMs256NSFsidO(1003 *cpt["config"], is_half=False1004 ) # fp32导出(C++要支持fp16必须手动将内存重新排列所以暂时不用fp16)1005 net_g.load_state_dict(cpt["weight"], strict=False)1006 input_names = ["phone", "phone_lengths", "pitch", "pitchf", "ds"]1007 output_names = [1008 "audio",1009 ]1010 torch.onnx.export(1011 net_g,1012 (1013 test_phone.to(device),1014 test_phone_lengths.to(device),1015 test_pitch.to(device),1016 test_pitchf.to(device),1017 test_ds.to(device),1018 ),1019 ExportedPath,1020 dynamic_axes={1021 "phone": [1],1022 "pitch": [1],1023 "pitchf": [1],1024 },1025 do_constant_folding=False,1026 opset_version=16,1027 verbose=False,1028 input_names=input_names,1029 output_names=output_names,1030 )1031 return "Finished"1032 1033 1034with gr.Blocks() as app:1035 gr.Markdown(1036 value=i18n(1037 "本软件以MIT协议开源, 作者不对软件具备任何控制力, 使用软件者、传播软件导出的声音者自负全责. <br>如不认可该条款, 则不能使用或引用软件包内任何代码和文件. 详见根目录<b>使用需遵守的协议-LICENSE.txt</b>."1038 )1039 )1040 with gr.Tabs():1041 with gr.TabItem(i18n("模型推理")):1042 with gr.Row():1043 sid0 = gr.Dropdown(label=i18n("推理音色"), choices=sorted(names))1044 refresh_button = gr.Button(i18n("刷新音色列表"), variant="primary")1045 refresh_button.click(fn=change_choices, inputs=[], outputs=[sid0])1046 clean_button = gr.Button(i18n("卸载音色省显存"), variant="primary")1047 spk_item = gr.Slider(1048 minimum=0,1049 maximum=2333,1050 step=1,1051 label=i18n("请选择说话人id"),1052 value=0,1053 visible=False,1054 interactive=True,1055 )1056 clean_button.click(fn=clean, inputs=[], outputs=[sid0])1057 sid0.change(1058 fn=get_vc,1059 inputs=[sid0],1060 outputs=[spk_item],1061 )1062 with gr.Group():1063 gr.Markdown(1064 value=i18n("男转女推荐+12key, 女转男推荐-12key, 如果音域爆炸导致音色失真也可以自己调整到合适音域. ")1065 )1066 with gr.Row():1067 with gr.Column():1068 vc_transform0 = gr.Number(1069 label=i18n("变调(整数, 半音数量, 升八度12降八度-12)"), value=01070 )1071 input_audio0 = gr.Textbox(1072 label=i18n("输入待处理音频文件路径(默认是正确格式示例)"),1073 value="E:\\codes\\py39\\vits_vc_gpu_train\\todo-songs\\冬之花clip1.wav",1074 )1075 f0method0 = gr.Radio(1076 label=i18n("选择音高提取算法,输入歌声可用pm提速,harvest低音好但巨慢无比"),1077 choices=["pm", "harvest"],1078 value="pm",1079 interactive=True,1080 )1081 with gr.Column():1082 file_index1 = gr.Textbox(1083 label=i18n("特征检索库文件路径"),1084 value="E:\\codes\\py39\\vits_vc_gpu_train\\logs\\mi-test-1key\\added_IVF677_Flat_nprobe_7.index",1085 interactive=True,1086 )1087 # file_big_npy1 = gr.Textbox(1088 # label=i18n("特征文件路径"),1089 # value="E:\\codes\py39\\vits_vc_gpu_train\\logs\\mi-test-1key\\total_fea.npy",1090 # interactive=True,1091 # )1092 index_rate1 = gr.Slider(1093 minimum=0,1094 maximum=1,1095 label=i18n("检索特征占比"),1096 value=0.76,1097 interactive=True,1098 )1099 f0_file = gr.File(label=i18n("F0曲线文件, 可选, 一行一个音高, 代替默认F0及升降调"))1100 but0 = gr.Button(i18n("转换"), variant="primary")1101 with gr.Column():1102 vc_output1 = gr.Textbox(label=i18n("输出信息"))1103 vc_output2 = gr.Audio(label=i18n("输出音频(右下角三个点,点了可以下载)"))1104 but0.click(1105 vc_single,1106 [1107 spk_item,1108 input_audio0,1109 vc_transform0,1110 f0_file,1111 f0method0,1112 file_index1,1113 # file_big_npy1,1114 index_rate1,1115 ],1116 [vc_output1, vc_output2],1117 )1118 with gr.Group():1119 gr.Markdown(1120 value=i18n("批量转换, 输入待转换音频文件夹, 或上传多个音频文件, 在指定文件夹(默认opt)下输出转换的音频. ")1121 )1122 with gr.Row():1123 with gr.Column():1124 vc_transform1 = gr.Number(1125 label=i18n("变调(整数, 半音数量, 升八度12降八度-12)"), value=01126 )1127 opt_input = gr.Textbox(label=i18n("指定输出文件夹"), value="opt")1128 f0method1 = gr.Radio(1129 label=i18n("选择音高提取算法,输入歌声可用pm提速,harvest低音好但巨慢无比"),1130 choices=["pm", "harvest"],1131 value="pm",1132 interactive=True,1133 )1134 with gr.Column():1135 file_index2 = gr.Textbox(1136 label=i18n("特征检索库文件路径"),1137 value="E:\\codes\\py39\\vits_vc_gpu_train\\logs\\mi-test-1key\\added_IVF677_Flat_nprobe_7.index",1138 interactive=True,1139 )1140 # file_big_npy2 = gr.Textbox(1141 # label=i18n("特征文件路径"),1142 # value="E:\\codes\\py39\\vits_vc_gpu_train\\logs\\mi-test-1key\\total_fea.npy",1143 # interactive=True,1144 # )1145 index_rate2 = gr.Slider(1146 minimum=0,1147 maximum=1,1148 label=i18n("检索特征占比"),1149 value=1,1150 interactive=True,1151 )1152 with gr.Column():1153 dir_input = gr.Textbox(1154 label=i18n("输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)"),1155 value="E:\codes\py39\\vits_vc_gpu_train\\todo-songs",1156 )1157 inputs = gr.File(1158 file_count="multiple", label=i18n("也可批量输入音频文件, 二选一, 优先读文件夹")1159 )1160 but1 = gr.Button(i18n("转换"), variant="primary")1161 vc_output3 = gr.Textbox(label=i18n("输出信息"))1162 but1.click(1163 vc_multi,1164 [1165 spk_item,1166 dir_input,1167 opt_input,1168 inputs,1169 vc_transform1,1170 f0method1,1171 file_index2,1172 # file_big_npy2,1173 index_rate2,1174 ],1175 [vc_output3],1176 )1177 with gr.TabItem(i18n("伴奏人声分离")):1178 with gr.Group():1179 gr.Markdown(1180 value=i18n(1181 "人声伴奏分离批量处理, 使用UVR5模型. <br>不带和声用HP2, 带和声且提取的人声不需要和声用HP5<br>合格的文件夹路径格式举例: E:\\codes\\py39\\vits_vc_gpu\\白鹭霜华测试样例(去文件管理器地址栏拷就行了)"1182 )1183 )1184 with gr.Row():1185 with gr.Column():1186 dir_wav_input = gr.Textbox(1187 label=i18n("输入待处理音频文件夹路径"),1188 value="E:\\codes\\py39\\vits_vc_gpu_train\\todo-songs",1189 )1190 wav_inputs = gr.File(1191 file_count="multiple", label=i18n("也可批量输入音频文件, 二选一, 优先读文件夹")1192 )1193 with gr.Column():1194 model_choose = gr.Dropdown(label=i18n("模型"), choices=uvr5_names)1195 agg = gr.Slider(1196 minimum=0,1197 maximum=20,1198 step=1,1199 label="人声提取激进程度",1200 value=10,