kwau/sovits-isla
0
1import ast2import base643import datetime4import glob5import json6import logging7import multiprocessing8import os9import re10import requests11import shutil12import subprocess13import sys14import traceback15import zipfile16from itertools import chain17from pathlib import Path18 19import gradio as gr20import librosa21import numpy as np22import soundfile as sf23import torch24import yaml25 26from auto_slicer import AutoSlicer27from compress_model import removeOptimizer28from inference.infer_tool_webui import Svc29from onnx_export import main as onnx_export30from sami import SAMIService31from tts_voices import SUPPORTED_LANGUAGES32from utils import mix_model33 34os.environ["PATH"] += os.pathsep + os.path.join(os.getcwd(), "ffmpeg", "bin")35 36logging.getLogger('numba').setLevel(logging.WARNING)37logging.getLogger('markdown_it').setLevel(logging.WARNING)38logging.getLogger('urllib3').setLevel(logging.WARNING)39logging.getLogger('matplotlib').setLevel(logging.WARNING)40 41# Some directories42workdir = "logs/44k"43second_dir = "models"44diff_second_dir = "models/diffusion"45diff_workdir = "logs/44k/diffusion"46config_dir = "configs/"47dataset_dir = "dataset/44k"48raw_path = "dataset_raw"49raw_wavs_path = "raw"50models_backup_path = 'models_backup'51root_dir = "checkpoints"52default_settings_file = "settings.yaml"53current_mode = ""54# Some global variables55debug = False56precheck_ok = False57model = None58sovits_params = {}59diff_params = {}60# Some dicts for mapping61MODEL_TYPE = {62 "vec768l12": 768,63 "vec256l9": 256,64 "hubertsoft": 256,65 "whisper-ppg": 1024,66 "cnhubertlarge": 1024,67 "dphubert": 768,68 "wavlmbase+": 768,69 "whisper-ppg-large": 128070}71ENCODER_PRETRAIN = {72 "vec256l9": "pretrain/checkpoint_best_legacy_500.pt",73 "vec768l12": "pretrain/checkpoint_best_legacy_500.pt",74 "hubertsoft": "pretrain/hubert-soft-0d54a1f4.pt",75 "whisper-ppg": "pretrain/medium.pt",76 "cnhubertlarge": "pretrain/chinese-hubert-large-fairseq-ckpt.pt",77 "dphubert": "pretrain/DPHuBERT-sp0.75.pth",78 "wavlmbase+": "pretrain/WavLM-Base+.pt",79 "whisper-ppg-large": "pretrain/large-v2.pt"80}81 82 83class Config:84 def __init__(self, path, type):85 self.path = path86 self.type = type87 88 def read(self):89 if self.type == "json":90 with open(self.path, 'r') as f:91 return json.load(f)92 if self.type == "yaml":93 with open(self.path, 'r') as f:94 return yaml.safe_load(f)95 96 def save(self, content):97 if self.type == "json":98 with open(self.path, 'w') as f:99 json.dump(content, f, indent=4)100 if self.type == "yaml":101 with open(self.path, 'w') as f:102 yaml.safe_dump(content, f, default_flow_style=False, sort_keys=False)103 104 105class ReleasePacker:106 def __init__(self, speaker, model):107 self.speaker = speaker108 self.model = model109 self.output_path = os.path.join("release_packs", f"{speaker}_release.zip")110 self.file_list = []111 112 def remove_temp(self, path):113 for filename in os.listdir(path):114 file_path = os.path.join(path, filename)115 if os.path.isfile(file_path) and not filename.endswith(".zip"):116 os.remove(file_path)117 elif os.path.isdir(file_path):118 shutil.rmtree(file_path, ignore_errors=True)119 120 def add_file(self, file_paths):121 self.file_list.extend(file_paths)122 123 def spk_to_dict(self):124 spk_string = self.speaker.replace(',', ',')125 spk_string = spk_string.replace(' ', '')126 _spk = spk_string.split(',')127 return {_spk: index for index, _spk in enumerate(_spk)}128 129 def generate_config(self, diff_model, config_origin):130 _config_origin = Config(os.path.join(config_read_dir, config_origin), "json")131 _template = Config("release_packs/config_template.json", "json")132 _d_template = Config("release_packs/diffusion_template.yaml", "yaml")133 orig_config = _config_origin.read()134 config_template = _template.read()135 diff_config_template = _d_template.read()136 spk_dict = self.spk_to_dict()137 _net = torch.load(os.path.join(ckpt_read_dir, self.model), map_location='cpu')138 emb_dim, model_dim = _net['model'].get('emb_g.weight', torch.empty(0, 0)).size()139 vol_emb = _net['model'].get('emb_vol.weight')140 if vol_emb is not None:141 config_template["train"]["vol_aug"] = config_template["model"]["vol_embedding"] = True142 #Keep the spk_dict length same as emb_dim143 if emb_dim > len(spk_dict):144 for i in range(emb_dim - len(spk_dict)):145 spk_dict[f"spk{i}"] = len(spk_dict)146 if emb_dim < len(spk_dict):147 for i in range(len(spk_dict) - emb_dim):148 spk_dict.popitem()149 self.speaker = ','.join(spk_dict.keys())150 config_template['model']['ssl_dim'] = config_template["model"]["filter_channels"] = config_template["model"]["gin_channels"] = model_dim151 config_template['model']['n_speakers'] = diff_config_template['model']['n_spk'] = emb_dim152 config_template['spk'] = diff_config_template['spk'] = spk_dict153 encoder = [k for k, v in MODEL_TYPE.items() if v == model_dim]154 if orig_config['model']['speech_encoder'] in encoder:155 config_template['model']['speech_encoder'] = orig_config['model']['speech_encoder']156 else:157 raise Exception("Config is not compatible with the model")158 159 if diff_model != "no_diff":160 _diff = torch.load(os.path.join(diff_read_dir, diff_model), map_location='cpu')161 _, diff_dim = _diff["model"].get("unit_embed.weight", torch.empty(0, 0)).size()162 if diff_dim == 256:163 diff_config_template['data']['encoder'] = 'hubertsoft'164 diff_config_template['data']['encoder_out_channels'] = 256165 elif diff_dim == 768:166 diff_config_template['data']['encoder'] = 'vec768l12'167 diff_config_template['data']['encoder_out_channels'] = 768168 elif diff_dim == 1024:169 diff_config_template['data']['encoder'] = 'whisper-ppg'170 diff_config_template['data']['encoder_out_channels'] = 1024171 172 with open("release_packs/install.txt", 'w') as f:173 f.write(str(self.file_list) + '#' + str(self.speaker))174 175 _template.save(config_template)176 _d_template.save(diff_config_template)177 178 def unpack(self, zip_file):179 with zipfile.ZipFile(zip_file, 'r') as zipf:180 zipf.extractall("release_packs")181 182 def formatted_install(self, install_txt):183 with open(install_txt, 'r') as f:184 content = f.read()185 file_list, speaker = content.split('#')186 self.speaker = speaker187 file_list = ast.literal_eval(file_list)188 self.file_list = file_list189 for _, target_path in self.file_list:190 if target_path != "install.txt" and target_path != "":191 shutil.move(os.path.join("release_packs", target_path), target_path)192 self.remove_temp("release_packs")193 return self.speaker194 195 def pack(self):196 with zipfile.ZipFile(self.output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:197 for file_path, target_path in self.file_list:198 if os.path.isfile(file_path):199 zipf.write(file_path, arcname=target_path)200 201 202def debug_change():203 global debug204 debug = debug_button.value205 206def get_default_settings():207 global sovits_params, diff_params, second_dir_enable208 config_file = Config(default_settings_file, "yaml")209 default_settings = config_file.read()210 sovits_params = default_settings['sovits_params']211 diff_params = default_settings['diff_params']212 webui_settings = default_settings['webui_settings']213 second_dir_enable = webui_settings['second_dir']214 return sovits_params, diff_params, second_dir_enable215 216def webui_change(read_second_dir):217 global second_dir_enable218 config_file = Config(default_settings_file, "yaml")219 default_settings = config_file.read()220 second_dir_enable = default_settings['webui_settings']['second_dir'] = read_second_dir221 config_file.save(default_settings)222 223def get_current_mode():224 global current_mode225 current_mode = "当前模式:独立目录模式,将从'./models/'读取模型文件" if second_dir_enable else "当前模式:工作目录模式,将从'./logs/44k'读取模型文件" 226 return current_mode227 228def save_default_settings(log_interval,eval_interval,keep_ckpts,batch_size,learning_rate,amp_dtype,all_in_mem,num_workers,cache_all_data,cache_device,diff_amp_dtype,diff_batch_size,diff_lr,diff_interval_log,diff_interval_val,diff_force_save,diff_k_step_max):229 config_file = Config(default_settings_file, "yaml")230 default_settings = config_file.read()231 default_settings['sovits_params']['log_interval'] = int(log_interval)232 default_settings['sovits_params']['eval_interval'] = int(eval_interval)233 default_settings['sovits_params']['keep_ckpts'] = int(keep_ckpts)234 default_settings['sovits_params']['batch_size'] = int(batch_size)235 default_settings['sovits_params']['learning_rate'] = float(learning_rate)236 default_settings['sovits_params']['amp_dtype'] = str(amp_dtype)237 default_settings['sovits_params']['all_in_mem'] = all_in_mem238 default_settings['diff_params']['num_workers'] = int(num_workers)239 default_settings['diff_params']['cache_all_data'] = cache_all_data240 default_settings['diff_params']['cache_device'] = str(cache_device)241 default_settings['diff_params']['amp_dtype'] = str(diff_amp_dtype)242 default_settings['diff_params']['diff_batch_size'] = int(diff_batch_size)243 default_settings['diff_params']['diff_lr'] = float(diff_lr)244 default_settings['diff_params']['diff_interval_log'] = int(diff_interval_log)245 default_settings['diff_params']['diff_interval_val'] = int(diff_interval_val)246 default_settings['diff_params']['diff_force_save'] = int(diff_force_save)247 default_settings['diff_params']['diff_k_step_max'] = diff_k_step_max248 config_file.save(default_settings)249 return "成功保存默认配置"250 251def get_model_info(choice_ckpt):252 pthfile = os.path.join(ckpt_read_dir, choice_ckpt)253 net = torch.load(pthfile, map_location=torch.device('cpu')) #cpu load to avoid using gpu memory254 spk_emb = net["model"].get("emb_g.weight")255 if spk_emb is None:256 return "所选模型缺少emb_g.weight,你可能选择了一个底模"257 _layer = spk_emb.size(1)258 encoder = [k for k, v in MODEL_TYPE.items() if v == _layer] #通过维度对应编码器259 encoder.sort()260 if encoder == ["hubertsoft", "vec256l9"]:261 encoder = ["vec256l9 / hubertsoft"]262 if encoder == ["cnhubertlarge", "whisper-ppg"]:263 encoder = ["whisper-ppg / cnhubertlarge"]264 if encoder == ["dphubert", "vec768l12", "wavlmbase+"]:265 encoder = ["vec768l12 / dphubert / wavlmbase+"]266 return encoder[0]267 268def load_json_encoder(config_choice, choice_ckpt):269 if config_choice == "no_config":270 return "未启用自动加载,请手动选择配置文件"271 if choice_ckpt == "no_model":272 return "请先选择模型"273 config_file = Config(os.path.join(config_read_dir, config_choice), "json")274 config = config_file.read()275 try:276 #比对配置文件中的模型维度与该encoder的实际维度是否对应,防止古神语277 config_encoder = config["model"].get("speech_encoder", "no_encoder")278 config_dim = config["model"]["ssl_dim"]279 #旧版配置文件自动匹配280 if config_encoder == "no_encoder":281 config_encoder = config["model"]["speech_encoder"] = "vec256l9" if config_dim == 256 else "vec768l12"282 config_file.save(config)283 correct_dim = MODEL_TYPE.get(config_encoder, "unknown")284 if config_dim != correct_dim:285 return "配置文件中的编码器与模型维度不匹配"286 return config_encoder287 except Exception as e:288 return f"出错了: {e}"289 290def auto_load(choice_ckpt):291 global second_dir_enable292 model_output_msg = get_model_info(choice_ckpt)293 json_output_msg = config_choice = ""294 choice_ckpt_name, _ = os.path.splitext(choice_ckpt)295 if second_dir_enable:296 all_config = [json for json in os.listdir(second_dir) if json.endswith(".json")]297 for config in all_config:298 config_fname, _ = os.path.splitext(config)299 if config_fname == choice_ckpt_name:300 config_choice = config301 json_output_msg = load_json_encoder(config, choice_ckpt)302 if json_output_msg != "":303 return model_output_msg, config_choice, json_output_msg304 else:305 return model_output_msg, "no_config", ""306 else:307 return model_output_msg, "no_config", ""308 309def auto_load_diff(diff_model):310 global second_dir_enable311 if second_dir_enable is False:312 return "no_diff_config"313 all_diff_config = [yaml for yaml in os.listdir(second_dir) if yaml.endswith(".yaml")]314 for config in all_diff_config:315 config_fname, _ = os.path.splitext(config)316 diff_fname, _ = os.path.splitext(diff_model)317 if config_fname == diff_fname:318 return config319 return "no_diff_config"320 321def load_model_func(ckpt_name,cluster_name,config_name,enhance,diff_model_name,diff_config_name,only_diffusion,use_spk_mix,using_device,method,speedup,cl_num):322 global model323 config_path = os.path.join(config_read_dir, config_name) if not only_diffusion else "configs/config.json"324 diff_config_path = os.path.join(config_read_dir, diff_config_name) if diff_config_name != "no_diff_config" else "configs/diffusion.yaml"325 ckpt_path = os.path.join(ckpt_read_dir, ckpt_name)326 cluster_path = os.path.join(ckpt_read_dir, cluster_name)327 diff_model_path = os.path.join(diff_read_dir, diff_model_name)328 k_step_max = 1000329 if not only_diffusion:330 config = Config(config_path, "json").read()331 if diff_model_name != "no_diff":332 _diff = Config(diff_config_path, "yaml")333 _content = _diff.read()334 diff_spk = _content.get('spk', {})335 diff_spk_choice = spk_choice = next(iter(diff_spk), "未检测到音色")336 if not only_diffusion:337 if _content['data'].get('encoder_out_channels') != config["model"].get('ssl_dim'):338 return "扩散模型维度与主模型不匹配,请确保两个模型使用的是同一个编码器", gr.Dropdown.update(choices=[], value=""), 0, None339 _content["infer"]["speedup"] = int(speedup)340 _content["infer"]["method"] = str(method)341 k_step_max = _content["model"].get('k_step_max', 0) if _content["model"].get('k_step_max', 0) != 0 else 1000342 _diff.save(_content)343 if not only_diffusion:344 net = torch.load(ckpt_path, map_location=torch.device('cpu'))345 #读取模型各维度并比对,还有小可爱无视提示硬要加载底模的就返回个未初始张量346 emb_dim, model_dim = net["model"].get("emb_g.weight", torch.empty(0, 0)).size() 347 if emb_dim > config["model"]["n_speakers"]:348 return "模型说话人数量与emb维度不匹配", gr.Dropdown.update(choices=[], value=""), 0, None349 if model_dim != config["model"]["ssl_dim"]: 350 return "配置文件与模型不匹配", gr.Dropdown.update(choices=[], value=""), 0, None351 encoder = config["model"]["speech_encoder"]352 spk_dict = config.get('spk', {})353 spk_choice = next(iter(spk_dict), "未检测到音色")354 else:355 spk_dict = diff_spk356 spk_choice = diff_spk_choice357 fr = cluster_name.endswith(".pkl") #如果是pkl后缀就启用特征检索358 shallow_diffusion = diff_model_name != "no_diff" #加载了扩散模型就启用浅扩散359 device = cuda[using_device] if "CUDA" in using_device else using_device360 model = Svc(ckpt_path,361 config_path,362 device=device if device != "Auto" else None,363 cluster_model_path=cluster_path,364 nsf_hifigan_enhance=enhance,365 diffusion_model_path=diff_model_path,366 diffusion_config_path=diff_config_path,367 shallow_diffusion=shallow_diffusion,368 only_diffusion=only_diffusion,369 spk_mix_enable=use_spk_mix,370 feature_retrieval=fr)371 spk_list = list(spk_dict.keys())372 if not only_diffusion:373 clip = 25 if encoder == "whisper-ppg" or encoder == "whisper-ppg-large" else cl_num #Whisper必须强制切片25秒374 device_name = torch.cuda.get_device_properties(model.dev).name if "cuda" in str(model.dev) else str(model.dev)375 sovits_msg = f"模型被成功加载到了{device_name}上\n"376 else: 377 clip = cl_num378 sovits_msg = "启用全扩散推理,未加载So-VITS模型\n"379 index_or_kmeans = "特征索引" if fr else "聚类模型"380 clu_load = "未加载" if cluster_name == "no_clu" else cluster_name381 diff_load = "未加载" if diff_model_name == "no_diff" else f"{diff_model_name} | 采样器: {method} | 加速倍数:{int(speedup)} | 最大浅扩散步数:{k_step_max}"382 output_msg = f"{sovits_msg}{index_or_kmeans}:{clu_load}\n扩散模型:{diff_load}"383 return (384 output_msg, 385 gr.Dropdown.update(choices=spk_list, value=spk_choice), 386 clip, 387 gr.Slider.update(value=100 if k_step_max>100 else k_step_max, minimum=speedup, maximum=k_step_max)388 )389 390def model_empty_cache():391 global model392 if model is None:393 return sid.update(choices = [],value=""),"没有模型需要卸载!"394 else:395 model.unload_model()396 model = None397 torch.cuda.empty_cache()398 return sid.update(choices = [],value=""),"模型卸载完毕!"399 400def get_file_options(directory, extension):401 return [file for file in os.listdir(directory) if file.endswith(extension)]402 403def load_options():404 ckpt_list = [file for file in get_file_options(ckpt_read_dir, ".pth") if not file.startswith("D_") or file == "G_0.pth"]405 config_list = get_file_options(config_read_dir, ".json")406 cluster_list = ["no_clu"] + get_file_options(ckpt_read_dir, ".pt") + get_file_options(ckpt_read_dir, ".pkl") # 聚类和特征检索模型407 diff_list = ["no_diff"] + get_file_options(diff_read_dir, ".pt")408 diff_config_list = ["no_diff_config"] + get_file_options(config_read_dir, ".yaml")409 return ckpt_list, config_list, cluster_list, diff_list, diff_config_list410 411def refresh_options():412 global ckpt_read_dir, config_read_dir, diff_read_dir, current_mode413 ckpt_read_dir = second_dir if second_dir_enable else workdir414 config_read_dir = second_dir if second_dir_enable else config_dir415 diff_read_dir = diff_second_dir if second_dir_enable else diff_workdir416 ckpt_list, config_list, cluster_list, diff_list, diff_config_list = load_options()417 current_mode = get_current_mode()418 return (419 choice_ckpt.update(choices=ckpt_list),420 config_choice.update(choices=config_list),421 cluster_choice.update(choices=cluster_list),422 diff_choice.update(choices=diff_list),423 diff_config_choice.update(choices=diff_config_list),424 mode_caption.update(value=f"""{current_mode},可在页面底端切换模式""")425 )426 427def source_change(use_microphone):428 if use_microphone:429 return vc_input3.update(source="microphone")430 else:431 return vc_input3.update(source="upload")432 433def vc_infer(output_format, sid, input_audio, sr, input_audio_path, vc_transform, auto_f0, cluster_ratio, slice_db, noise_scale, pad_seconds, cl_num, lg_num, lgr_num, f0_predictor, enhancer_adaptive_key, cr_threshold, k_step, use_spk_mix, second_encoding, loudness_envelope_adjustment):434 if np.issubdtype(input_audio.dtype, np.integer):435 input_audio = (input_audio / np.iinfo(input_audio.dtype).max).astype(np.float32)436 if len(input_audio.shape) > 1:437 input_audio = librosa.to_mono(input_audio.transpose(1, 0))438 if sr != 44100:439 input_audio = librosa.resample(input_audio, orig_sr=sr, target_sr=44100)440 sf.write("temp.wav", input_audio, 44100, format="wav")441 _audio = model.slice_inference(442 "temp.wav",443 sid,444 vc_transform,445 slice_db,446 cluster_ratio,447 auto_f0,448 noise_scale,449 pad_seconds,450 cl_num,451 lg_num,452 lgr_num,453 f0_predictor,454 enhancer_adaptive_key,455 cr_threshold,456 k_step,457 use_spk_mix,458 second_encoding,459 loudness_envelope_adjustment460 ) 461 model.clear_empty()462 if not os.path.exists("results"):463 os.makedirs("results")464 key = "auto" if auto_f0 else f"{int(vc_transform)}key"465 cluster = "_" if cluster_ratio == 0 else f"_{cluster_ratio}_"466 isdiffusion = "sovits_"467 if model.shallow_diffusion:468 isdiffusion = "sovdiff_"469 if model.only_diffusion:470 isdiffusion = "diff_"471 #Gradio上传的filepath因为未知原因会有一个无意义的固定后缀,这里去掉472 truncated_basename = Path(input_audio_path).stem[:-6] if Path(input_audio_path).stem[-6:] == "-0-100" else Path(input_audio_path).stem473 output_file_name = f'{truncated_basename}_{sid}_{key}{cluster}{isdiffusion}{f0_predictor}.{output_format}'474 output_file_path = os.path.join("results", output_file_name)475 if os.path.exists(output_file_path):476 count = 1477 while os.path.exists(output_file_path):478 output_file_name = f'{truncated_basename}_{sid}_{key}{cluster}{isdiffusion}{f0_predictor}_{str(count)}.{output_format}'479 output_file_path = os.path.join("results", output_file_name)480 count += 1 481 sf.write(output_file_path, _audio, model.target_sample, format=output_format)482 return output_file_path483 484def vc_fn(output_format, sid, input_audio, vc_transform, auto_f0, cluster_ratio, slice_db, noise_scale, pad_seconds, cl_num, lg_num, lgr_num, f0_predictor, enhancer_adaptive_key, cr_threshold, k_step, use_spk_mix, second_encoding, loudness_envelope_adjustment, progress=gr.Progress(track_tqdm=True)):485 global model486 try:487 if input_audio is None:488 return "你还没有上传音频", None489 if model is None:490 return "你还没有加载模型", None491 if getattr(model, 'cluster_model', None) is None and model.feature_retrieval is False:492 if cluster_ratio != 0:493 return "你还未加载聚类或特征检索模型,无法启用聚类/特征检索混合比例", None494 audio, sr = sf.read(input_audio)495 output_file_path = vc_infer(output_format, sid, audio, sr, input_audio, vc_transform, auto_f0, cluster_ratio, slice_db, noise_scale, pad_seconds, cl_num, lg_num, lgr_num, f0_predictor, enhancer_adaptive_key, cr_threshold, k_step, use_spk_mix, second_encoding, loudness_envelope_adjustment)496 os.remove("temp.wav")497 return "Success", output_file_path498 except Exception as e:499 if debug:500 traceback.print_exc()501 raise gr.Error(e)502 503def vc_batch_fn(output_format, sid, input_audio_files, vc_transform, auto_f0, cluster_ratio, slice_db, noise_scale, pad_seconds, cl_num, lg_num, lgr_num, f0_predictor, enhancer_adaptive_key, cr_threshold, k_step, use_spk_mix, second_encoding, loudness_envelope_adjustment, progress=gr.Progress()):504 global model505 try:506 if input_audio_files is None or len(input_audio_files) == 0:507 return "你还没有上传音频"508 if model is None:509 return "你还没有加载模型"510 if getattr(model, 'cluster_model', None) is None and model.feature_retrieval is False:511 if cluster_ratio != 0:512 return "你还未加载聚类或特征检索模型,无法启用聚类/特征检索混合比例", None513 _output = []514 for file_obj in progress.tqdm(input_audio_files, desc="Inferencing"):515 print(f"Start processing: {file_obj.name}")516 input_audio_path = file_obj.name517 audio, sr = sf.read(input_audio_path)518 output_file_path = vc_infer(output_format, sid, audio, sr, input_audio_path, vc_transform, auto_f0, cluster_ratio, slice_db, noise_scale, pad_seconds, cl_num, lg_num, lgr_num, f0_predictor, enhancer_adaptive_key, cr_threshold, k_step, use_spk_mix, second_encoding, loudness_envelope_adjustment)519 _output.append(output_file_path)520 return "批量推理完成,音频已经被保存到results文件夹"521 except Exception as e:522 if debug:523 traceback.print_exc()524 raise gr.Error(e)525 526def tts_fn(_text, _gender, _lang, _rate, _volume, output_format, sid, vc_transform, auto_f0,cluster_ratio, slice_db, noise_scale,pad_seconds,cl_num,lg_num,lgr_num,f0_predictor,enhancer_adaptive_key,cr_threshold, k_step,use_spk_mix,second_encoding,loudness_envelope_adjustment,progress=gr.Progress(track_tqdm=True)):527 global model528 try:529 if model is None:530 return "你还没有加载模型", None531 if getattr(model, 'cluster_model', None) is None and model.feature_retrieval is False:532 if cluster_ratio != 0:533 return "你还未加载聚类或特征检索模型,无法启用聚类/特征检索混合比例", None534 _rate = f"+{int(_rate*100)}%" if _rate >= 0 else f"{int(_rate*100)}%"535 _volume = f"+{int(_volume*100)}%" if _volume >= 0 else f"{int(_volume*100)}%"536 if _lang == "Auto":537 _gender = "Male" if _gender == "男" else "Female"538 subprocess.run([r".\workenv\python.exe", "tts.py", _text, _lang, _rate, _volume, _gender])539 else:540 subprocess.run([r".\workenv\python.exe", "tts.py", _text, _lang, _rate, _volume])541 target_sr = 44100542 y, sr = librosa.load("tts.wav")543 resampled_y = librosa.resample(y, orig_sr=sr, target_sr=target_sr)544 sf.write("tts.wav", resampled_y, target_sr, subtype = "PCM_16")545 input_audio = "tts.wav"546 audio, sr = sf.read(input_audio)547 output_file_path = vc_infer(output_format, sid, audio, sr, input_audio, vc_transform, auto_f0, cluster_ratio, slice_db, noise_scale, pad_seconds, cl_num, lg_num, lgr_num, f0_predictor, enhancer_adaptive_key, cr_threshold, k_step, use_spk_mix, second_encoding, loudness_envelope_adjustment)548 #os.remove("tts.wav")549 return "Success", output_file_path550 except Exception as e:551 if debug:552 traceback.print_exc()553 raise gr.Error(e)554 555def load_raw_dirs():556 global precheck_ok557 precheck_ok = False558 allowed_pattern = re.compile(r'^[a-zA-Z0-9_@#$%^&()_+\-=\s\.]*$')559 illegal_files = illegal_dataset = []560 for root, dirs, files in os.walk(raw_path):561 for dir in dirs:562 if not allowed_pattern.match(dir):563 illegal_dataset.append(dir)564 if illegal_dataset:565 return f"数据集文件夹名只能包含数字、字母、下划线,以下文件夹不符合要求,请改名后再试:\n{illegal_dataset}"566 if root != raw_path: # 只处理子文件夹内的文件567 for file in files:568 if not allowed_pattern.match(file) and file not in illegal_files:569 illegal_files.append(file)570 if not file.lower().endswith('.wav') and file not in illegal_files:571 illegal_files.append(file)572 if illegal_files:573 return f"数据集文件名只能包含数字、字母、下划线,且必须是.wav格式,以下文件不符合要求,请改名后再试:\n{illegal_files}"574 spk_dirs = [entry.name for entry in os.scandir(raw_path) if entry.is_dir()]575 if spk_dirs:576 precheck_ok = True577 return spk_dirs578 else:579 return "未找到数据集,请检查dataset_raw文件夹"580 581def dataset_preprocess(encoder, f0_predictor, use_diff, vol_aug, skip_loudnorm, num_processes, tiny_enable):582 if precheck_ok:583 diff_arg = "--use_diff" if use_diff else ""584 vol_aug_arg = "--vol_aug" if vol_aug else ""585 skip_loudnorm_arg = "--skip_loudnorm" if skip_loudnorm else ""586 tiny_arg = "--tiny" if tiny_enable else ""587 preprocess_commands = [588 r".\workenv\python.exe resample.py %s" % (skip_loudnorm_arg),589 r".\workenv\python.exe preprocess_flist_config.py --speech_encoder %s %s %s" % (encoder, vol_aug_arg, tiny_arg),590 r".\workenv\python.exe preprocess_hubert_f0.py --num_processes %s --f0_predictor %s %s" % (num_processes ,f0_predictor, diff_arg)591 ]592 accumulated_output = ""593 #清空dataset594 dataset = os.listdir(dataset_dir)595 if len(dataset) != 0:596 for dir in dataset:597 dataset_spk_dir = os.path.join(dataset_dir, str(dir))598 if os.path.isdir(dataset_spk_dir):599 shutil.rmtree(dataset_spk_dir)600 accumulated_output += f"Deleting previous dataset: {dir}\n"601 for command in preprocess_commands:602 try:603 result = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, text=True)604 accumulated_output += f"Command: {command}, Using Encoder: {encoder}, Using f0 Predictor: {f0_predictor}\n"605 yield accumulated_output, None606 progress_line = None607 for line in result.stdout:608 if r"it/s" in line or r"s/it" in line: #防止进度条刷屏609 progress_line = line610 else:611 accumulated_output += line612 if progress_line is None:613 yield accumulated_output, None614 else:615 yield accumulated_output + progress_line, None616 result.communicate()617 except subprocess.CalledProcessError as e:618 result = e.output619 accumulated_output += f"Error: {result}\n"620 yield accumulated_output, None621 if progress_line is not None:622 accumulated_output += progress_line623 accumulated_output += '-' * 50 + '\n'624 yield accumulated_output, None625 config_path = "configs/config.json"626 with open(config_path, 'r') as f:627 config = json.load(f)628 spk_name = config.get('spk', None)629 yield accumulated_output, gr.Textbox.update(value=spk_name)630 else:631 yield "数据集识别未通过,请先识别数据集并确保没有报错信息", None632 633def regenerate_config(encoder, vol_aug, tiny_enable):634 if precheck_ok is False:635 return "数据集识别未通过,请检查识别结果的报错信息"636 vol_aug_arg = "--vol_aug" if vol_aug else ""637 tiny_arg = "--tiny" if tiny_enable else ""638 cmd = r".\workenv\python.exe preprocess_flist_config.py --speech_encoder %s %s %s" % (encoder, vol_aug_arg, tiny_arg)639 output = ""640 try:641 result = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, text=True)642 for line in result.stdout:643 output += line644 output += "Regenerate config file successfully."645 except subprocess.CalledProcessError as e:646 result = e.output647 output += f"Error: {result}\n"648 return output649 650def clear_output():651 return gr.Textbox.update(value="Cleared!>_<")652 653def get_available_encoder():654 current_pretrain = os.listdir("pretrain")655 current_pretrain = [("pretrain/" + model) for model in current_pretrain]656 encoder_list = []657 for encoder, path in ENCODER_PRETRAIN.items():658 if path in current_pretrain:659 encoder_list.append(encoder)660 return encoder_list661 662def config_fn(log_interval, eval_interval, keep_ckpts, batch_size, lr, amp_dtype, all_in_mem, diff_num_workers, diff_cache_all_data, diff_batch_size, diff_lr, diff_interval_log, diff_interval_val, diff_cache_device, diff_amp_dtype, diff_force_save, diff_k_step_max):663 if amp_dtype == "fp16" or amp_dtype == "bf16":664 fp16_run = True665 else:666 fp16_run = False667 amp_dtype = "fp16"668 config_origin = Config("configs/config.json", "json")669 diff_config = Config("configs/diffusion.yaml", "yaml")670 config_data = config_origin.read()671 config_data['train']['log_interval'] = int(log_interval)672 config_data['train']['eval_interval'] = int(eval_interval)673 config_data['train']['keep_ckpts'] = int(keep_ckpts)674 config_data['train']['batch_size'] = int(batch_size)675 config_data['train']['learning_rate'] = float(lr)676 config_data['train']['fp16_run'] = fp16_run677 config_data['train']['half_type'] = str(amp_dtype)678 config_data['train']['all_in_mem'] = all_in_mem679 config_origin.save(config_data)680 diff_config_data = diff_config.read()681 diff_config_data['train']['num_workers'] = int(diff_num_workers)682 diff_config_data['train']['cache_all_data'] = diff_cache_all_data683 diff_config_data['train']['batch_size'] = int(diff_batch_size)684 diff_config_data['train']['lr'] = float(diff_lr)685 diff_config_data['train']['interval_log'] = int(diff_interval_log)686 diff_config_data['train']['interval_val'] = int(diff_interval_val)687 diff_config_data['train']['cache_device'] = str(diff_cache_device)688 diff_config_data['train']['amp_dtype'] = str(diff_amp_dtype)689 diff_config_data['train']['interval_force_save'] = int(diff_force_save)690 diff_config_data['model']['k_step_max'] = 100 if diff_k_step_max else 0691 diff_config.save(diff_config_data)692 return "配置文件写入完成"693 694def check_dataset(dataset_path):695 if not os.listdir(dataset_path):696 return "数据集不存在,请检查dataset文件夹"697 no_npy_pt_files = True698 for root, dirs, files in os.walk(dataset_path):699 for file in files:700 if file.endswith('.npy') or file.endswith('.pt'):701 no_npy_pt_files = False702 break703 if no_npy_pt_files:704 return "数据集中未检测到f0和hubert文件,可能是预处理未完成"705 return None706 707def training(gpu_selection, encoder, tiny_enable):708 if tiny_enable:709 encoder = "vec768l12_tiny"710 config_file = Config("configs/config.json", "json")711 config_data = config_file.read()712 vol_emb = config_data["model"]["vol_embedding"]713 dataset_warn = check_dataset(dataset_dir)714 if dataset_warn is not None:715 return dataset_warn716 PRETRAIN = { 717 "vec256l9": ("D_0.pth", "G_0.pth", "pre_trained_model"),718 "vec768l12": ("D_0.pth", "G_0.pth", "pre_trained_model/768l12/vol_emb" if vol_emb else "pre_trained_model/768l12"),719 "vec768l12_tiny": ("D_0.pth", "G_0.pth", "pre_trained_model/tiny/vec768l12_vol_emb"),720 "hubertsoft": ("D_0.pth", "G_0.pth", "pre_trained_model/hubertsoft"),721 "whisper-ppg": ("D_0.pth", "G_0.pth", "pre_trained_model/whisper-ppg"),722 "cnhubertlarge": ("D_0.pth", "G_0.pth", "pre_trained_model/cnhubertlarge"),723 "dphubert": ("D_0.pth", "G_0.pth", "pre_trained_model/dphubert"),724 "wavlmbase+": ("D_0.pth", "G_0.pth", "pre_trained_model/wavlmbase+"),725 "whisper-ppg-large": ("D_0.pth", "G_0.pth", "pre_trained_model/whisper-ppg-large")726 }727 if encoder not in PRETRAIN:728 return "未知编码器"729 d_0_file, g_0_file, encoder_model_path = PRETRAIN[encoder]730 d_0_path = os.path.join(encoder_model_path, d_0_file)731 g_0_path = os.path.join(encoder_model_path, g_0_file)732 timestamp = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M')733 new_backup_folder = os.path.join(models_backup_path, str(timestamp))734 output_msg = ""735 if os.listdir(workdir) != ['diffusion']:736 os.makedirs(new_backup_folder, exist_ok=True)737 for file in os.listdir(workdir):738 if file != "diffusion":739 shutil.move(os.path.join(workdir, file), os.path.join(new_backup_folder, file))740 if os.path.isfile(g_0_path) and os.path.isfile(d_0_path):741 shutil.copy(d_0_path, os.path.join(workdir, "D_0.pth"))742 shutil.copy(g_0_path, os.path.join(workdir, "G_0.pth"))743 output_msg += f"成功装载预训练模型,编码器:{encoder}\n"744 else:745 output_msg += f"{encoder}的预训练模型不存在,未装载预训练模型\n"746 747 cmd = r"set CUDA_VISIBLE_DEVICES=%s && .\workenv\python.exe train.py -c configs/config.json -m 44k" % (gpu_selection)748 subprocess.Popen(["cmd", "/c", "start", "cmd", "/k", cmd])749 output_msg += "已经在新的终端窗口开始训练,请监看终端窗口的训练日志。在终端中按Ctrl+C可暂停训练。"750 return output_msg751 752def continue_training(gpu_selection, encoder):753 dataset_warn = check_dataset(dataset_dir)754 if dataset_warn is not None:755 return dataset_warn756 if encoder == "":757 return "请先选择预处理对应的编码器"758 all_files = os.listdir(workdir)759 model_files = [f for f in all_files if f.startswith('G_') and f.endswith('.pth')]760 if len(model_files) == 0:761 return "你还没有已开始的训练"762 cmd = r"set CUDA_VISIBLE_DEVICES=%s && .\workenv\python.exe train.py -c configs/config.json -m 44k" % (gpu_selection)763 subprocess.Popen(["cmd", "/c", "start", "cmd", "/k", cmd])764 return "已经在新的终端窗口开始训练,请监看终端窗口的训练日志。在终端中按Ctrl+C可暂停训练。"765 766def kmeans_training(kmeans_gpu):767 if not os.listdir(dataset_dir):768 return "数据集不存在,请检查dataset文件夹"769 cmd = r".\workenv\python.exe cluster/train_cluster.py --gpu" if kmeans_gpu else r".\workenv\python.exe cluster/train_cluster.py"770 subprocess.Popen(["cmd", "/c", "start", "cmd", "/k", cmd])771 return "已经在新的终端窗口开始训练,训练聚类模型不会输出日志,CPU训练一般需要5-10分钟左右"772 773def index_training():774 if not os.listdir(dataset_dir):775 return "数据集不存在,请检查dataset文件夹"776 cmd = r".\workenv\python.exe train_index.py -c configs/config.json"777 subprocess.Popen(["cmd", "/c", "start", "cmd", "/k", cmd])778 return "已经在新的终端窗口开始训练"779 780def diff_training(encoder, k_step_max):781 if not os.listdir(dataset_dir):782 return "数据集不存在,请检查dataset文件夹"783 timestamp = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M')784 new_backup_folder = os.path.join(models_backup_path, "diffusion", str(timestamp))785 if len(os.listdir(diff_workdir)) != 0:786 os.makedirs(new_backup_folder, exist_ok=True)787 for file in os.listdir(diff_workdir):788 shutil.move(os.path.join(diff_workdir, file), os.path.join(new_backup_folder, file))789 DIFF_PRETRAIN = {790 "768-kstepmax100": "pre_trained_model/diffusion/768l12/max100/model_0.pt",791 "vec768l12": "pre_trained_model/diffusion/768l12/model_0.pt",792 "hubertsoft": "pre_trained_model/diffusion/hubertsoft/model_0.pt",793 "whisper-ppg": "pre_trained_model/diffusion/whisper-ppg/model_0.pt"794 }795 if encoder not in DIFF_PRETRAIN:796 return "你所选的编码器暂时不支持训练扩散模型"797 if k_step_max:798 encoder = "768-kstepmax100"799 diff_pretrained_model = DIFF_PRETRAIN[encoder]800 shutil.copy(diff_pretrained_model, os.path.join(diff_workdir, "model_0.pt"))801 subprocess.Popen(["cmd", "/c", "start", "cmd", "/k", r".\workenv\python.exe train_diff.py -c configs/diffusion.yaml"])802 output_message = "已经在新的终端窗口开始训练,请监看终端窗口的训练日志。在终端中按Ctrl+C可暂停训练。"803 if encoder == "768-kstepmax100":804 output_message += "\n正在进行100步深度的浅扩散训练,已加载底模"805 else:806 output_message += f"\n正在进行完整深度的扩散训练,编码器{encoder}"807 return output_message808 809def diff_continue_training(encoder):810 if not os.listdir(dataset_dir):811 return "数据集不存在,请检查dataset文件夹"812 if encoder == "":813 return "请先选择预处理对应的编码器"814 all_files = os.listdir(diff_workdir)815 model_files = [f for f in all_files if f.endswith('.pt')]816 if len(model_files) == 0:817 return "你还没有已开始的训练"818 subprocess.Popen(["cmd", "/c", "start", "cmd", "/k", r".\workenv\python.exe train_diff.py -c configs/diffusion.yaml"])819 return "已经在新的终端窗口开始训练,请监看终端窗口的训练日志。在终端中按Ctrl+C可暂停训练。"820 821def upload_mix_append_file(files,sfiles):822 try:823 if(sfiles is None):824 file_paths = [file.name for file in files]825 else:826 file_paths = [file.name for file in chain(files,sfiles)]827 p = {file:100 for file in file_paths}828 return file_paths,mix_model_output1.update(value=json.dumps(p,indent=2))829 except Exception as e:830 if debug:831 traceback.print_exc()832 raise gr.Error(e)833 834def mix_submit_click(js,mode):835 try:836 assert js.lstrip()!=""837 modes = {"凸组合":0, "线性组合":1}838 mode = modes[mode]839 data = json.loads(js)840 data = list(data.items())841 model_path,mix_rate = zip(*data)842 path = mix_model(model_path,mix_rate,mode)843 return f"成功,文件被保存在了{path}"844 except Exception as e:845 if debug:846 traceback.print_exc()847 raise gr.Error(e)848 849def updata_mix_info(files):850 try:851 if files is None:852 return mix_model_output1.update(value="")853 p = {file.name:100 for file in files}854 return mix_model_output1.update(value=json.dumps(p,indent=2))855 except Exception as e:856 if debug:857 traceback.print_exc()858 raise gr.Error(e)859 860def pth_identify():861 if not os.path.exists(root_dir):862 return f"未找到{root_dir}文件夹,请先创建一个{root_dir}文件夹并按第一步流程操作"863 model_dirs = [d for d in os.listdir(root_dir) if os.path.isdir(os.path.join(root_dir, d))]864 if not model_dirs:865 return f"未在{root_dir}文件夹中找到模型文件夹,请确保每个模型和配置文件都被放置在单独的文件夹中"866 valid_model_dirs = []867 for path in model_dirs:868 pth_files = glob.glob(f"{root_dir}/{path}/*.pth")869 json_files = glob.glob(f"{root_dir}/{path}/*.json")870 if len(pth_files) != 1 or len(json_files) != 1:871 return f"错误: 在{root_dir}/{path}中找到了{len(pth_files)}个.pth文件和{len(json_files)}个.json文件。应当确保每个文件夹内有且只有一个.pth文件和.json文件"872 valid_model_dirs.append(path)873 874 return f"成功识别了{len(valid_model_dirs)}个模型:{valid_model_dirs}"875 876def onnx_export_func():877 model_dirs = [d for d in os.listdir(root_dir) if os.path.isdir(os.path.join(root_dir, d))]878 output_msg = ""879 try:880 for path in model_dirs:881 pth_files = glob.glob(f"{root_dir}/{path}/*.pth")882 json_files = glob.glob(f"{root_dir}/{path}/*.json")883 model_file = Path(pth_files[0]).name884 json_file = Path(json_files[0]).name885 try:886 onnx_export(path, json_file, model_file)887 output_msg += f"成功转换{path}\n"888 except Exception as e:889 output_msg += f"转换{path}时出现错误: {e}\n"890 return output_msg891 except Exception as e:892 if debug:893 traceback.print_exc()894 raise gr.Error(e)895 896def load_raw_audio(audio_path):897 if not os.path.isdir(audio_path):898 return "请输入正确的目录", None899 files = os.listdir(audio_path)900 wav_files = [file for file in files if file.lower().endswith('.wav')]901 if not wav_files:902 return "未在目录中找到.wav音频文件", None903 return "成功加载", wav_files904 905def slicer_fn(input_dir, output_dir, process_method, max_sec, min_sec):906 if output_dir == "":907 return "请先选择输出的文件夹"908 if output_dir == input_dir:909 return "输出目录不能和输入目录相同"910 slicer = AutoSlicer()911 if os.path.exists(output_dir) is not True:912 os.makedirs(output_dir)913 for filename in os.listdir(input_dir):914 if filename.lower().endswith(".wav"):915 slicer.auto_slice(filename, input_dir, output_dir, max_sec)916 if process_method == "丢弃":917 for filename in os.listdir(output_dir):918 if filename.endswith(".wav"):919 filepath = os.path.join(output_dir, filename)920 audio, sr = librosa.load(filepath, sr=None, mono=False)921 if librosa.get_duration(y=audio, sr=sr) < min_sec:922 os.remove(filepath)923 elif process_method == "将过短音频整合为长音频":924 slicer.merge_short(output_dir, max_sec, min_sec)925 file_count, max_duration, min_duration, orig_duration, final_duration = slicer.slice_count(input_dir, output_dir)926 hrs = int(final_duration / 3600)927 mins = int((final_duration % 3600) / 60)928 sec = format(float(final_duration % 60), '.2f')929 rate = format(100 * (final_duration / orig_duration), '.2f') if orig_duration != 0 else 0930 rate_msg = f"为原始音频时长的{rate}%" if rate != 0 else "因未知问题,无法计算切片时长的占比"931 return f"成功将音频切分为{file_count}条片段,其中最长{max_duration}秒,最短{min_duration}秒,切片后的音频总时长{hrs:02d}小时{mins:02d}分{sec}秒,{rate_msg}"932 933def model_compression(_model, is_fp16):934 if _model == "":935 return "请先选择要压缩的模型"936 else:937 model_path = os.path.join(ckpt_read_dir, _model)938 filename, extension = os.path.splitext(_model)939 output_model_name = f"{filename}_compressed{extension}"940 output_path = os.path.join(ckpt_read_dir, output_model_name)941 removeOptimizer("configs/config.json", model_path, is_fp16, output_path)942 return f"模型已成功被保存在了{output_path}"943 944def pack_autoload(model_to_pack):945 _, config_name, _ = auto_load(model_to_pack)946 if config_name == "no_config":947 return "未找到对应的配置文件,请手动选择", None948 else:949 _config = Config(os.path.join(config_read_dir, config_name), "json")950 _content = _config.read()951 spk_dict = _content["spk"]952 spk_list = ",".join(spk_dict.keys())953 return config_name, spk_list954 955def release_packing(model_to_pack, model_config, speaker, diff_to_pack, cluster_to_pack):956 model_path = diff_path = cluster_path = ""957 basename = os.path.splitext(model_to_pack)[0]958 diff_basename = os.path.splitext(diff_to_pack)[0]959 if model_to_pack == "" or model_config == "" or speaker == "":960 return "存在必选项为空,请检查后重试"961 released_pack = ReleasePacker(speaker, model_to_pack)962 released_pack.remove_temp("release_packs")963 model_path = os.path.join(ckpt_read_dir, model_to_pack)964 config_path = os.path.join(config_read_dir, model_config)965 if os.stat(model_path).st_size > 300000000:966 removeOptimizer(config_path, model_path, False, os.path.join("release_packs", model_to_pack))967 model_path = os.path.join("release_packs", model_to_pack)968 if diff_to_pack != "no_diff":969 diff_path = os.path.join(diff_read_dir, diff_to_pack)970 if cluster_to_pack != "no_cluster":971 cluster_path = os.path.join(ckpt_read_dir, cluster_to_pack)972 shutil.copyfile("configs_template/config_template.json", "release_packs/config_template.json")973 shutil.copyfile("configs_template/diffusion_template.yaml", "release_packs/diffusion_template.yaml")974 files_to_pack = [975 (model_path, f"models/{model_to_pack}"),976 (diff_path, f"models/diffusion/{diff_to_pack}") if diff_to_pack != "no_diff" else ("", ""),977 (cluster_path, f"models/{cluster_to_pack}") if cluster_to_pack != "no_cluster" else ("", ""),978 (f"release_packs/{basename}.json", f"models/{basename}.json"),979 (f"release_packs/{diff_basename}.yaml", f"models/{diff_basename}.yaml") if diff_to_pack != "no_diff" else ("", ""),980 ("release_packs/install.txt", "install.txt")981 ]982 released_pack.add_file(files_to_pack)983 released_pack.generate_config(diff_to_pack, model_config)984 os.rename("release_packs/config_template.json", f"release_packs/{basename}.json")985 os.rename("release_packs/diffusion_template.yaml", f"release_packs/{diff_basename}.yaml")986 released_pack.pack()987 to_remove = [file for file in os.listdir("release_packs") if not file.endswith(".zip")]988 for file in to_remove:989 os.remove(os.path.join("release_packs", file))990 return "打包成功, 请在release_packs目录下查看"991 992def release_install(model_zip_path):993 model_zip = ReleasePacker("", "")994 model_zip.unpack(model_zip_path)995 for file in os.listdir("release_packs"):996 if file.endswith(".txt"):997 install_txt = os.path.join("release_packs", file)998 break999 else:1000 model_zip.remove_temp("release_packs")1001 return "非格式化安装包,无法安装"1002 _spk = model_zip.formatted_install(install_txt)1003 model_zip.remove_temp("release_packs")1004 return f"安装成功,可用说话人{_spk},请启用独立目录模式加载模型"1005 1006def sami_inference(ac_key, s_key, app_key, audio_path, model, use_proxy, port):1007 if ac_key == "" or s_key == "" or app_key == "":1008 return None, "密钥和APP_KEY不能为空"1009 1010 if use_proxy:1011 os.environ['HTTP_PROXY'] = f"http://127.0.0.1:{int(port)}/"1012 1013 sami_service = SAMIService()1014 1015 sami_service.set_ak(ac_key)1016 sami_service.set_sk(s_key)1017 1018 auth_req = {"appkey": app_key, "token_version": 'volc-auth-v1', "expiration": 3600}1019 auth_resp = sami_service.common_json_handler("GetToken", auth_req)1020 1021 try:1022 auth_token = auth_resp["token"]1023 except KeyError as e:1024 if debug:1025 traceback.print_exc()1026 raise gr.Error(e)1027 1028 payload = json.dumps({"model": model})1029 with open(audio_path, "rb") as f:1030 data = f.read()1031 data = base64.b64encode(data).decode('utf-8')1032 1033 req = {1034 "appkey": app_key,1035 "token": auth_token,1036 "namespace": "MusicSourceSeparate",1037 "payload": payload,1038 "data": data1039 }1040 1041 resp = requests.post("https://sami.bytedance.com/api/v1/invoke", json=req)1042 1043 try:1044 sami_resp = resp.json()1045 if resp.status_code != 200:1046 print(sami_resp)1047 sys.exit(1)1048 except Exception as e:1049 if debug:1050 traceback.print_exc()1051 raise gr.Error(e)1052 1053 print("response task_id=%s status_code=%d status_text=%s" % (1054 sami_resp["task_id"], sami_resp["status_code"], sami_resp["status_text"]), end=" ")1055 if "payload" in sami_resp and len(sami_resp["payload"]) > 0:1056 print("payload=%s" % sami_resp["payload"], end=" ")1057 if "data" in sami_resp and len(sami_resp["data"]) > 0:1058 # Save audio data into file1059 data = base64.b64decode(sami_resp["data"])1060 print("data=[%d]bytes" % len(data))1061 with open("output.wav", "wb") as f:1062 f.write(data)1063 1064 if use_proxy:1065 os.environ.pop('HTTP_PROXY')1066 1067 if os.path.isfile("output.wav"):1068 return "output.wav", "Success"1069 else:1070 return None, "出错了"1071 1072 1073#read default params1074sovits_params, diff_params, second_dir_enable = get_default_settings()1075ckpt_read_dir = second_dir if second_dir_enable else workdir1076config_read_dir = second_dir if second_dir_enable else config_dir1077diff_read_dir = diff_second_dir if second_dir_enable else diff_workdir1078current_mode = get_current_mode()1079 1080# create dirs if they don't exist1081dirs_to_check = [1082 workdir,1083 second_dir,1084 diff_workdir,1085 diff_second_dir,1086 dataset_dir,1087]1088for dir in dirs_to_check:1089 if not os.path.exists(dir):1090 os.makedirs(dir)1091 1092# read ckpt list1093ckpt_list, config_list, cluster_list, diff_list, diff_config_list = load_options()1094 1095# read available encoder list1096encoder_list = get_available_encoder()1097 1098#read GPU info1099ngpu=torch.cuda.device_count()1100gpu_infos=[]1101if(torch.cuda.is_available() is False or ngpu==0):1102 if_gpu_ok=False1103else:1104 if_gpu_ok = False1105 for i in range(ngpu):1106 gpu_name=torch.cuda.get_device_name(i)1107 if("MX"in gpu_name):1108 continue1109 if("RTX" in gpu_name.upper() or "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 "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 or"P4"in gpu_name or "T4"in gpu_name or "TITAN"in gpu_name.upper()):#A10#A100#V100#A40#P40#M40#K801110 if_gpu_ok=True#至少有一张能用的N卡1111 gpu_infos.append("%s\t%s"%(i,gpu_name))1112gpu_info="\n".join(gpu_infos)if if_gpu_ok is True and len(gpu_infos)>0 else "很遗憾您这没有能用的显卡来支持您训练"1113gpus="-".join([i[0]for i in gpu_infos])1114 1115#read cuda info for inference1116cuda = {}1117min_vram = 01118if torch.cuda.is_available():1119 for i in range(torch.cuda.device_count()):1120 current_vram = torch.cuda.get_device_properties(i).total_memory1121 min_vram = current_vram if current_vram > min_vram else min_vram1122 device_name = torch.cuda.get_device_properties(i).name1123 cuda[f"CUDA:{i} {device_name}"] = f"cuda:{i}"1124total_vram = round(min_vram * 9.31322575e-10) if min_vram != 0 else 01125auto_batch = total_vram - 2 if total_vram <= 12 and total_vram > 0 else total_vram1126print(f"Current vram: {total_vram} GiB, recommended batch size: {auto_batch}")1127 1128#Check BF16 support1129amp_options = ["fp32", "fp16"]1130if if_gpu_ok:1131 if torch.cuda.is_bf16_supported():1132 amp_options = ["fp32", "fp16", "bf16"] 1133 1134#Get F0 Options1135f0_options = ["crepe","pm","dio","harvest","rmvpe","fcpe"]1136 1137app = gr.Blocks()1138with app:1139 gr.Markdown(value="""1140 ### So-VITS-SVC 4.1-Stable WebUI 推理&训练 v2.3.141141 1142 制作协力:bilibili@麦哲云1143 1144 仅供个人娱乐和非商业用途,禁止用于血腥、暴力、性相关、政治相关内容1145 1146 [使用文档和常见报错解答](https://www.yuque.com/umoubuton/ueupp5)1147 1148 整合包作者:bilibili@羽毛布団 | 技术交流群:742817595 | 交流二群:168254971 | 交流三群:416656175 | 交流四群:9035166071149 1150 """)1151 with gr.Tabs():1152 with gr.TabItem("推理") as inference_tab:1153 mode_caption = gr.Markdown(value=f"""1154 {current_mode},可在页面底端切换模式1155 """)1156 with gr.Row():1157 choice_ckpt = gr.Dropdown(label="模型选择", choices=ckpt_list, value="no_model")1158 model_branch = gr.Textbox(label="模型编码器", placeholder="请先选择模型", interactive=False)1159 with gr.Row():1160 config_choice = gr.Dropdown(label="配置文件", choices=config_list, value="no_config")1161 config_info = gr.Textbox(label="配置文件编码器", placeholder="请选择配置文件")1162 gr.Markdown(value="""**请检查模型和配置文件的编码器是否匹配**""")1163 with gr.Row():1164 diff_choice = gr.Dropdown(label="(可选)选择扩散模型", choices=diff_list, value="no_diff", interactive=True)1165 diff_config_choice = gr.Dropdown(label="扩散模型配置文件", choices=diff_config_list, value="no_diff_config", interactive=True)1166 cluster_choice = gr.Dropdown(label="(可选)选择聚类模型/特征检索模型", choices=cluster_list, value="no_clu")1167 refresh = gr.Button("刷新选项")1168 with gr.Row():1169 enhance = gr.Checkbox(label="是否使用NSF_HIFIGAN增强,该选项对部分训练集少的模型有一定的音质增强效果,但是对训练好的模型有反面效果,默认关闭", value=False)1170 only_diffusion = gr.Checkbox(label="是否使用全扩散推理,开启后将不使用So-VITS模型,仅使用扩散模型进行完整扩散推理,不建议使用", value=False)1171 with gr.Row():1172 diffusion_method = gr.Dropdown(label="扩散模型采样器", choices=["dpm-solver++","dpm-solver","pndm","ddim","unipc"], value="dpm-solver++")1173 diffusion_speedup = gr.Number(label="扩散加速倍数,默认为10倍", value=10)1174 using_device = gr.Dropdown(label="推理设备,默认为自动选择", choices=["Auto",*cuda.keys(),"cpu"], value="Auto")1175 with gr.Row():1176 loadckpt = gr.Button("加载模型", variant="primary")1177 unload = gr.Button("卸载模型", variant="primary")1178 with gr.Row():1179 model_message = gr.Textbox(label="Output Message")1180 sid = gr.Dropdown(label="So-VITS说话人", value="speaker0")1181 1182 inference_tab.select(refresh_options,[],[choice_ckpt,config_choice,cluster_choice,diff_choice,diff_config_choice])1183 choice_ckpt.change(auto_load, [choice_ckpt], [model_branch, config_choice, config_info]) 1184 config_choice.change(load_json_encoder, [config_choice, choice_ckpt], [config_info])1185 diff_choice.change(auto_load_diff, [diff_choice], [diff_config_choice])1186 refresh.click(refresh_options,[],[choice_ckpt,config_choice,cluster_choice,diff_choice,diff_config_choice,mode_caption])1187 1188 gr.Markdown(value="""1189 请稍等片刻,模型加载大约需要10秒。后续操作不需要重新加载模型1190 """)1191 with gr.Tabs():1192 with gr.TabItem("单个音频上传"):1193 vc_input3 = gr.Audio(label="单个音频上传", type="filepath", source="upload")1194 use_microphone = gr.Checkbox(label="使用麦克风输入")1195 with gr.TabItem("批量音频上传"):1196 vc_batch_files = gr.Files(label="批量音频上传", file_types=["audio"], file_count="multiple")1197 with gr.TabItem("文字转语音"):1198 gr.Markdown("""1199 文字转语音(TTS)说明:使用edge_tts服务生成音频,并转换为So-VITS模型音色。1200 """)