CoolFace
Apppublic

kwau/sovits-isla

sourceHugging Faceupdated 3y agoView on Hugging Face
0likes
webUI.py431 linesDownload Raw Back to root
1import glob2import json3import logging4import os5import re6import subprocess7import sys8import time9import traceback10from itertools import chain11from pathlib import Path12 13# os.system("wget -P cvec/ https://huggingface.co/spaces/innnky/nanami/resolve/main/checkpoint_best_legacy_500.pt")14import gradio as gr15import librosa16import numpy as np17import soundfile18import torch19 20from compress_model import removeOptimizer21from edgetts.tts_voices import SUPPORTED_LANGUAGES22from inference.infer_tool import Svc23from utils import mix_model24 25logging.getLogger('numba').setLevel(logging.WARNING)26logging.getLogger('markdown_it').setLevel(logging.WARNING)27logging.getLogger('urllib3').setLevel(logging.WARNING)28logging.getLogger('matplotlib').setLevel(logging.WARNING)29logging.getLogger('multipart').setLevel(logging.WARNING)30 31model = None32spk = None33debug = False34 35local_model_root = './trained'36 37cuda = {}38if torch.cuda.is_available():39    for i in range(torch.cuda.device_count()):40        device_name = torch.cuda.get_device_properties(i).name41        cuda[f"CUDA:{i} {device_name}"] = f"cuda:{i}"42 43def upload_mix_append_file(files,sfiles):44    try:45        if(sfiles is None):46            file_paths = [file.name for file in files]47        else:48            file_paths = [file.name for file in chain(files,sfiles)]49        p = {file:100 for file in file_paths}50        return file_paths,mix_model_output1.update(value=json.dumps(p,indent=2))51    except Exception as e:52        if debug:53            traceback.print_exc()54        raise gr.Error(e)55 56def mix_submit_click(js,mode):57    try:58        assert js.lstrip()!=""59        modes = {"凸组合":0, "线性组合":1}60        mode = modes[mode]61        data = json.loads(js)62        data = list(data.items())63        model_path,mix_rate = zip(*data)64        path = mix_model(model_path,mix_rate,mode)65        return f"成功,文件被保存在了{path}"66    except Exception as e:67        if debug:68            traceback.print_exc()69        raise gr.Error(e)70 71def updata_mix_info(files):72    try:73        if files is None :74            return mix_model_output1.update(value="")75        p = {file.name:100 for file in files}76        return mix_model_output1.update(value=json.dumps(p,indent=2))77    except Exception as e:78        if debug:79            traceback.print_exc()80        raise gr.Error(e)81 82def modelAnalysis(model_path,config_path,cluster_model_path,device,enhance,diff_model_path,diff_config_path,only_diffusion,use_spk_mix,local_model_enabled,local_model_selection):83    global model84    try:85        device = cuda[device] if "CUDA" in device else device86        cluster_filepath = os.path.split(cluster_model_path.name) if cluster_model_path is not None else "no_cluster"87        # get model and config path88        if (local_model_enabled):89            # local path90            model_path = glob.glob(os.path.join(local_model_selection, '*.pth'))[0]91            config_path = glob.glob(os.path.join(local_model_selection, '*.json'))[0]92        else:93            # upload from webpage94            model_path = model_path.name95            config_path = config_path.name96        fr = ".pkl" in cluster_filepath[1]97        model = Svc(model_path,98                config_path,99                device=device if device != "Auto" else None,100                cluster_model_path = cluster_model_path.name if cluster_model_path is not None else "",101                nsf_hifigan_enhance=enhance,102                diffusion_model_path = diff_model_path.name if diff_model_path is not None else "",103                diffusion_config_path = diff_config_path.name if diff_config_path is not None else "",104                shallow_diffusion = True if diff_model_path is not None else False,105                only_diffusion = only_diffusion,106                spk_mix_enable = use_spk_mix,107                feature_retrieval = fr108                )109        spks = list(model.spk2id.keys())110        device_name = torch.cuda.get_device_properties(model.dev).name if "cuda" in str(model.dev) else str(model.dev)111        msg = f"成功加载模型到设备{device_name}上\n"112        if cluster_model_path is None:113            msg += "未加载聚类模型或特征检索模型\n"114        elif fr:115            msg += f"特征检索模型{cluster_filepath[1]}加载成功\n"116        else:117            msg += f"聚类模型{cluster_filepath[1]}加载成功\n"118        if diff_model_path is None:119            msg += "未加载扩散模型\n"120        else:121            msg += f"扩散模型{diff_model_path.name}加载成功\n"122        msg += "当前模型的可用音色:\n"123        for i in spks:124            msg += i + " "125        return sid.update(choices = spks,value=spks[0]), msg126    except Exception as e:127        if debug:128            traceback.print_exc()129        raise gr.Error(e)130 131    132def modelUnload():133    global model134    if model is None:135        return sid.update(choices = [],value=""),"没有模型需要卸载!"136    else:137        model.unload_model()138        model = None139        torch.cuda.empty_cache()140        return sid.update(choices = [],value=""),"模型卸载完毕!"141    142def vc_infer(output_format, sid, audio_path, truncated_basename, 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):143    global model144    _audio = model.slice_inference(145        audio_path,146        sid,147        vc_transform,148        slice_db,149        cluster_ratio,150        auto_f0,151        noise_scale,152        pad_seconds,153        cl_num,154        lg_num,155        lgr_num,156        f0_predictor,157        enhancer_adaptive_key,158        cr_threshold,159        k_step,160        use_spk_mix,161        second_encoding,162        loudness_envelope_adjustment163    )  164    model.clear_empty()165    #构建保存文件的路径,并保存到results文件夹内166    str(int(time.time()))167    if not os.path.exists("results"):168        os.makedirs("results")169    key = "auto" if auto_f0 else f"{int(vc_transform)}key"170    cluster = "_" if cluster_ratio == 0 else f"_{cluster_ratio}_"171    isdiffusion = "sovits"172    if model.shallow_diffusion:173        isdiffusion = "sovdiff"174 175    if model.only_diffusion:176        isdiffusion = "diff"177    178    output_file_name = 'result_'+truncated_basename+f'_{sid}_{key}{cluster}{isdiffusion}.{output_format}'179    output_file = os.path.join("results", output_file_name)180    soundfile.write(output_file, _audio, model.target_sample, format=output_format)181    return output_file182 183def vc_fn(sid, input_audio, output_format, 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):184    global model185    try:186        if input_audio is None:187            return "You need to upload an audio", None188        if model is None:189            return "You need to upload an model", None190        if getattr(model, 'cluster_model', None) is None and model.feature_retrieval is False:191            if cluster_ratio != 0:192                return "You need to upload an cluster model or feature retrieval model before assigning cluster ratio!", None193        #print(input_audio)    194        audio, sampling_rate = soundfile.read(input_audio)195        #print(audio.shape,sampling_rate)196        if np.issubdtype(audio.dtype, np.integer):197            audio = (audio / np.iinfo(audio.dtype).max).astype(np.float32)198        #print(audio.dtype)199        if len(audio.shape) > 1:200            audio = librosa.to_mono(audio.transpose(1, 0))201        # 未知原因Gradio上传的filepath会有一个奇怪的固定后缀,这里去掉202        truncated_basename = Path(input_audio).stem[:-6]203        processed_audio = os.path.join("raw", f"{truncated_basename}.wav")204        soundfile.write(processed_audio, audio, sampling_rate, format="wav")205        output_file = vc_infer(output_format, sid, processed_audio, truncated_basename, 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)206 207        return "Success", output_file208    except Exception as e:209        if debug:210            traceback.print_exc()211        raise gr.Error(e)212 213def text_clear(text):214    return re.sub(r"[\n\,\(\) ]", "", text)215 216def vc_fn2(_text, _lang, _gender, _rate, _volume, sid, output_format, 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):217    global model218    try:219        if model is None:220            return "You need to upload an model", None221        if getattr(model, 'cluster_model', None) is None and model.feature_retrieval is False:222            if cluster_ratio != 0:223                return "You need to upload an cluster model or feature retrieval model before assigning cluster ratio!", None224        _rate = f"+{int(_rate*100)}%" if _rate >= 0 else f"{int(_rate*100)}%"225        _volume = f"+{int(_volume*100)}%" if _volume >= 0 else f"{int(_volume*100)}%"226        if _lang == "Auto":227            _gender = "Male" if _gender == "男" else "Female"228            subprocess.run([sys.executable, "edgetts/tts.py", _text, _lang, _rate, _volume, _gender])229        else:230            subprocess.run([sys.executable, "edgetts/tts.py", _text, _lang, _rate, _volume])231        target_sr = 44100232        y, sr = librosa.load("tts.wav")233        resampled_y = librosa.resample(y, orig_sr=sr, target_sr=target_sr)234        soundfile.write("tts.wav", resampled_y, target_sr, subtype = "PCM_16")235        input_audio = "tts.wav"236        #audio, _ = soundfile.read(input_audio)237        output_file_path = vc_infer(output_format, sid, input_audio, "tts", 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)238        os.remove("tts.wav")239        return "Success", output_file_path240    except Exception as e:241        if debug: traceback.print_exc()  # noqa: E701242        raise gr.Error(e)243 244def model_compression(_model):245    if _model == "":246        return "请先选择要压缩的模型"247    else:248        model_path = os.path.split(_model.name)249        filename, extension = os.path.splitext(model_path[1])250        output_model_name = f"{filename}_compressed{extension}"251        output_path = os.path.join(os.getcwd(), output_model_name)252        removeOptimizer(_model.name, output_path)253        return f"模型已成功被保存在了{output_path}"254 255def scan_local_models():256    res = []257    candidates = glob.glob(os.path.join(local_model_root, '**', '*.json'), recursive=True)258    candidates = set([os.path.dirname(c) for c in candidates])259    for candidate in candidates:260        jsons = glob.glob(os.path.join(candidate, '*.json'))261        pths = glob.glob(os.path.join(candidate, '*.pth'))262        if (len(jsons) == 1 and len(pths) == 1):263            # must contain exactly one json and one pth file264            res.append(candidate)265    return res266 267def local_model_refresh_fn():268    choices = scan_local_models()269    return gr.Dropdown.update(choices=choices)270 271def debug_change():272    global debug273    debug = debug_button.value274 275with gr.Blocks(276    theme=gr.themes.Base(277        primary_hue = gr.themes.colors.green,278        font=["Source Sans Pro", "Arial", "sans-serif"],279        font_mono=['JetBrains mono', "Consolas", 'Courier New']280    ),281) as app:282    with gr.Tabs():283        with gr.TabItem("推理"):284            gr.Markdown(value="""285                So-vits-svc 4.0 推理 webui286                """)287            with gr.Row(variant="panel"):288                with gr.Column():289                    gr.Markdown(value="""290                        <font size=2> 模型设置</font>291                        """)292                    with gr.Tabs():293                        # invisible checkbox that tracks tab status294                        local_model_enabled = gr.Checkbox(value=False, visible=False)295                        with gr.TabItem('上传') as local_model_tab_upload:296                            with gr.Row():297                                model_path = gr.File(label="选择模型文件")298                                config_path = gr.File(label="选择配置文件")299                        with gr.TabItem('本地') as local_model_tab_local:300                            gr.Markdown(f'模型应当放置于{local_model_root}文件夹下')301                            local_model_refresh_btn = gr.Button('刷新本地模型列表')302                            local_model_selection = gr.Dropdown(label='选择模型文件夹', choices=[], interactive=True)303                    with gr.Row():304                        diff_model_path = gr.File(label="选择扩散模型文件")305                        diff_config_path = gr.File(label="选择扩散模型配置文件")306                    cluster_model_path = gr.File(label="选择聚类模型或特征检索文件(没有可以不选)")307                    device = gr.Dropdown(label="推理设备,默认为自动选择CPU和GPU", choices=["Auto",*cuda.keys(),"cpu"], value="Auto")308                    enhance = gr.Checkbox(label="是否使用NSF_HIFIGAN增强,该选项对部分训练集少的模型有一定的音质增强效果,但是对训练好的模型有反面效果,默认关闭", value=False)309                    only_diffusion = gr.Checkbox(label="是否使用全扩散推理,开启后将不使用So-VITS模型,仅使用扩散模型进行完整扩散推理,默认关闭", value=False)310                with gr.Column():311                    gr.Markdown(value="""312                        <font size=3>左侧文件全部选择完毕后(全部文件模块显示download),点击“加载模型”进行解析:</font>313                        """)314                    model_load_button = gr.Button(value="加载模型", variant="primary")315                    model_unload_button = gr.Button(value="卸载模型", variant="primary")316                    sid = gr.Dropdown(label="音色(说话人)")317                    sid_output = gr.Textbox(label="Output Message")318 319 320            with gr.Row(variant="panel"):321                with gr.Column():322                    gr.Markdown(value="""323                        <font size=2> 推理设置</font>324                        """)325                    auto_f0 = gr.Checkbox(label="自动f0预测,配合聚类模型f0预测效果更好,会导致变调功能失效(仅限转换语音,歌声勾选此项会究极跑调)", value=False)326                    f0_predictor = gr.Dropdown(label="选择F0预测器,可选择crepe,pm,dio,harvest,rmvpe,默认为pm(注意:crepe为原F0使用均值滤波器)", choices=["pm","dio","harvest","crepe","rmvpe"], value="pm")327                    vc_transform = gr.Number(label="变调(整数,可以正负,半音数量,升高八度就是12)", value=0)328                    cluster_ratio = gr.Number(label="聚类模型/特征检索混合比例,0-1之间,0即不启用聚类/特征检索。使用聚类/特征检索能提升音色相似度,但会导致咬字下降(如果使用建议0.5左右)", value=0)329                    slice_db = gr.Number(label="切片阈值", value=-40)330                    output_format = gr.Radio(label="音频输出格式", choices=["wav", "flac", "mp3"], value = "wav")331                    noise_scale = gr.Number(label="noise_scale 建议不要动,会影响音质,玄学参数", value=0.4)332                    k_step = gr.Slider(label="浅扩散步数,只有使用了扩散模型才有效,步数越大越接近扩散模型的结果", value=100, minimum = 1, maximum = 1000)333                with gr.Column():334                    pad_seconds = gr.Number(label="推理音频pad秒数,由于未知原因开头结尾会有异响,pad一小段静音段后就不会出现", value=0.5)335                    cl_num = gr.Number(label="音频自动切片,0为不切片,单位为秒(s)", value=0)336                    lg_num = gr.Number(label="两端音频切片的交叉淡入长度,如果自动切片后出现人声不连贯可调整该数值,如果连贯建议采用默认值0,注意,该设置会影响推理速度,单位为秒/s", value=0)337                    lgr_num = gr.Number(label="自动音频切片后,需要舍弃每段切片的头尾。该参数设置交叉长度保留的比例,范围0-1,左开右闭", value=0.75)338                    enhancer_adaptive_key = gr.Number(label="使增强器适应更高的音域(单位为半音数)|默认为0", value=0)339                    cr_threshold = gr.Number(label="F0过滤阈值,只有启动crepe时有效. 数值范围从0-1. 降低该值可减少跑调概率,但会增加哑音", value=0.05)340                    loudness_envelope_adjustment = gr.Number(label="输入源响度包络替换输出响度包络融合比例,越靠近1越使用输出响度包络", value = 0)341                    second_encoding = gr.Checkbox(label = "二次编码,浅扩散前会对原始音频进行二次编码,玄学选项,效果时好时差,默认关闭", value=False)342                    use_spk_mix = gr.Checkbox(label = "动态声线融合", value = False, interactive = False)343            with gr.Tabs():344                with gr.TabItem("音频转音频"):345                    vc_input3 = gr.Audio(label="选择音频", type="filepath")346                    vc_submit = gr.Button("音频转换", variant="primary")347                with gr.TabItem("文字转音频"):348                    text2tts=gr.Textbox(label="在此输入要转译的文字。注意,使用该功能建议打开F0预测,不然会很怪")349                    with gr.Row():350                        tts_gender = gr.Radio(label = "说话人性别", choices = ["男","女"], value = "男")351                        tts_lang = gr.Dropdown(label = "选择语言,Auto为根据输入文字自动识别", choices=SUPPORTED_LANGUAGES, value = "Auto")352                        tts_rate = gr.Slider(label = "TTS语音变速(倍速相对值)", minimum = -1, maximum = 3, value = 0, step = 0.1)353                        tts_volume = gr.Slider(label = "TTS语音音量(相对值)", minimum = -1, maximum = 1.5, value = 0, step = 0.1)354                    vc_submit2 = gr.Button("文字转换", variant="primary")355            with gr.Row():356                with gr.Column():357                    vc_output1 = gr.Textbox(label="Output Message")358                with gr.Column():359                    vc_output2 = gr.Audio(label="Output Audio", interactive=False)360 361        with gr.TabItem("小工具/实验室特性"):362            gr.Markdown(value="""363                        <font size=2> So-vits-svc 4.0 小工具/实验室特性</font>364                        """)365            with gr.Tabs():366                with gr.TabItem("静态声线融合"):367                    gr.Markdown(value="""368                        <font size=2> 介绍:该功能可以将多个声音模型合成为一个声音模型(多个模型参数的凸组合或线性组合),从而制造出现实中不存在的声线 369                                          注意:370                                          1.该功能仅支持单说话人的模型371                                          2.如果强行使用多说话人模型,需要保证多个模型的说话人数量相同,这样可以混合同一个SpaekerID下的声音372                                          3.保证所有待混合模型的config.json中的model字段是相同的373                                          4.输出的混合模型可以使用待合成模型的任意一个config.json,但聚类模型将不能使用374                                          5.批量上传模型的时候最好把模型放到一个文件夹选中后一起上传375                                          6.混合比例调整建议大小在0-100之间,也可以调为其他数字,但在线性组合模式下会出现未知的效果376                                          7.混合完毕后,文件将会保存在项目根目录中,文件名为output.pth377                                          8.凸组合模式会将混合比例执行Softmax使混合比例相加为1,而线性组合模式不会378                        </font>379                        """)380                    mix_model_path = gr.Files(label="选择需要混合模型文件")381                    mix_model_upload_button = gr.UploadButton("选择/追加需要混合模型文件", file_count="multiple")382                    mix_model_output1 = gr.Textbox(383                                            label="混合比例调整,单位/%",384                                            interactive = True385                                         )386                    mix_mode = gr.Radio(choices=["凸组合", "线性组合"], label="融合模式",value="凸组合",interactive = True)387                    mix_submit = gr.Button("声线融合启动", variant="primary")388                    mix_model_output2 = gr.Textbox(389                                            label="Output Message"390                                         )391                    mix_model_path.change(updata_mix_info,[mix_model_path],[mix_model_output1])392                    mix_model_upload_button.upload(upload_mix_append_file, [mix_model_upload_button,mix_model_path], [mix_model_path,mix_model_output1])393                    mix_submit.click(mix_submit_click, [mix_model_output1,mix_mode], [mix_model_output2])394                395                with gr.TabItem("模型压缩工具"):396                    gr.Markdown(value="""397                        该工具可以实现对模型的体积压缩,在**不影响模型推理功能**的情况下,将原本约600M的So-VITS模型压缩至约200M, 大大减少了硬盘的压力。398                        **注意:压缩后的模型将无法继续训练,请在确认封炉后再压缩。**399                    """)400                    model_to_compress = gr.File(label="模型上传")401                    compress_model_btn = gr.Button("压缩模型", variant="primary")402                    compress_model_output = gr.Textbox(label="输出信息", value="")403 404                    compress_model_btn.click(model_compression, [model_to_compress], [compress_model_output])405                    406                    407    with gr.Tabs():408        with gr.Row(variant="panel"):409            with gr.Column():410                gr.Markdown(value="""411                    <font size=2> WebUI设置</font>412                    """)413                debug_button = gr.Checkbox(label="Debug模式,如果向社区反馈BUG需要打开,打开后控制台可以显示具体错误提示", value=debug)414        # refresh local model list415        local_model_refresh_btn.click(local_model_refresh_fn, outputs=local_model_selection)416        # set local enabled/disabled on tab switch417        local_model_tab_upload.select(lambda: False, outputs=local_model_enabled)418        local_model_tab_local.select(lambda: True, outputs=local_model_enabled)419        420        vc_submit.click(vc_fn, [sid, vc_input3, output_format, 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], [vc_output1, vc_output2])421        vc_submit2.click(vc_fn2, [text2tts, tts_lang, tts_gender, tts_rate, tts_volume, sid, output_format, 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], [vc_output1, vc_output2])422 423        debug_button.change(debug_change,[],[])424        model_load_button.click(modelAnalysis,[model_path,config_path,cluster_model_path,device,enhance,diff_model_path,diff_config_path,only_diffusion,use_spk_mix,local_model_enabled,local_model_selection],[sid,sid_output])425        model_unload_button.click(modelUnload,[],[sid,sid_output])426    os.system("start http://127.0.0.1:7860")427    app.launch()428 429 430 431